001/*
002 * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package org.jdrupes.mdoclet.internal.doclets.toolkit.util;
027
028/**
029 * Abstraction for simple relative URIs, consisting of a path and an
030 * optional fragment. DocLink objects can be created by the constructors
031 * below or from a DocPath using the convenience
032 * {@link DocPath#fragment fragment} method.
033 */
034public class DocLink {
035    final DocPath path;
036    final String fragment;
037
038    /**
039     * Creates a DocLink representing the URI {@code #fragment}.
040     * @param fragment the fragment
041     * @return the DocLink
042     */
043    public static DocLink fragment(String fragment) {
044        return new DocLink((DocPath) null, fragment);
045    }
046
047    /**
048     * Creates a DocLink representing the URI {@code path}.
049     * @param path the path
050     */
051    public DocLink(DocPath path) {
052        this(path, null);
053    }
054
055    /**
056     * Creates a DocLink representing the URI {@code path#fragment}.
057     * Any of the component parts may be null.
058     * @param path the path
059     * @param fragment the fragment
060     */
061    public DocLink(DocPath path, String fragment) {
062        this.path = path;
063        this.fragment = fragment;
064    }
065
066    /**
067     * Creates a DocLink representing the URI {@code path#fragment}.
068     * Any of the component parts may be null.
069     * @param path the path
070     * @param fragment the fragment
071     */
072    public DocLink(String path, String fragment) {
073        this(DocPath.create(path), fragment);
074    }
075
076    /**
077     * Creates a DocLink formed by relativizing the path against a given base.
078     * @param base the base
079     * @return the DocLink
080     */
081    public DocLink relativizeAgainst(DocPath base) {
082        if (base.isEmpty() || path == null) {
083            return this;
084        }
085
086        // The following guards against the (ugly) use-case of using DocPath to
087        // contain a URL
088        if (isAbsoluteURL(path)) {
089            return this;
090        }
091
092        DocPath newPath = base.relativize(path);
093        // avoid generating an empty link by using the basename of the path if
094        // necessary
095        if (newPath.isEmpty() && isEmpty(fragment)) {
096            newPath = path.basename();
097        }
098        return new DocLink(newPath, fragment);
099    }
100
101    public DocLink withFragment(String fragment) {
102        return new DocLink(path, fragment);
103    }
104
105    // return true if the path begins <letters>://
106    private boolean isAbsoluteURL(DocPath path) {
107        String s = path.getPath();
108        for (int i = 0; i < s.length(); i++) {
109            char c = s.charAt(i);
110            if (Character.isLetter(c)) {
111                continue;
112            }
113            return (c == ':' && i + 2 < s.length() && s.charAt(i + 1) == '/'
114                && s.charAt(i + 2) == '/');
115        }
116        return false;
117    }
118
119    /**
120     * Returns the link in the form "path#fragment", omitting any empty
121     * components.
122     * @return the string
123     */
124    @Override
125    public String toString() {
126        // common fast path
127        if (path != null && isEmpty(fragment))
128            return path.getPath();
129
130        StringBuilder sb = new StringBuilder();
131        if (path != null)
132            sb.append(path.getPath());
133        if (!isEmpty(fragment))
134            sb.append("#").append(fragment);
135        return sb.toString();
136    }
137
138    private static boolean isEmpty(String s) {
139        return (s == null) || s.isEmpty();
140    }
141}