001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.tools.plugin.javadoc;
020
021import java.util.Objects;
022import java.util.Optional;
023
024/**
025 * Wraps a fully qualified (and resolved) code reference used in javadoc tags {@code see}, {@code link} and
026 * {@code linkplain}. Similar to {@link JavadocReference} but can distinguish between package names and class names. The
027 * package name is always set for a resolved reference (except for references to modules). The member is always the
028 * normalized form containing only fully qualified type names (without argument names), separated by {@code ,} without
029 * any whitespace characters. Also the member type is always resolved to one of {@link MemberType} (in case the
030 * reference contains a member part).
031 */
032public class FullyQualifiedJavadocReference extends JavadocReference {
033
034    /** if false, points to a class/package which is part of the current classloader (and not any of its parents) */
035    private final boolean isExternal;
036
037    private final Optional<String> packageName;
038
039    private final Optional<MemberType> memberType;
040
041    /** The type of the member part of the reference. */
042    public enum MemberType {
043        FIELD,
044        METHOD,
045        CONSTRUCTOR
046    }
047
048    public FullyQualifiedJavadocReference(String packageName, boolean isExternal) {
049        this(packageName, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), isExternal);
050    }
051
052    public FullyQualifiedJavadocReference(String packageName, Optional<String> label, boolean isExternal) {
053        this(packageName, Optional.empty(), Optional.empty(), Optional.empty(), label, isExternal);
054    }
055
056    public FullyQualifiedJavadocReference(String packageName, String className, boolean isExternal) {
057        this(packageName, Optional.of(className), Optional.empty(), Optional.empty(), Optional.empty(), isExternal);
058    }
059
060    public FullyQualifiedJavadocReference(
061            String packageName, String className, String member, MemberType memberType, boolean isExternal) {
062        this(
063                packageName,
064                Optional.of(className),
065                Optional.of(member),
066                Optional.of(memberType),
067                Optional.empty(),
068                isExternal);
069    }
070
071    public FullyQualifiedJavadocReference(
072            String packageName,
073            Optional<String> className,
074            Optional<String> member,
075            Optional<MemberType> memberType,
076            Optional<String> label,
077            boolean isExternal) {
078        this(Optional.empty(), Optional.of(packageName), className, member, memberType, label, isExternal);
079    }
080
081    public FullyQualifiedJavadocReference(
082            Optional<String> moduleName,
083            Optional<String> packageName,
084            Optional<String> className,
085            Optional<String> member,
086            Optional<MemberType> memberType,
087            Optional<String> label,
088            boolean isExternal) {
089        super(moduleName, className, member, label);
090        this.packageName = packageName;
091        this.isExternal = isExternal;
092        if (!moduleName.isPresent() && !packageName.isPresent()) {
093            throw new IllegalArgumentException("At least one of module name or package name needs to be set");
094        }
095        if (member.isPresent()) {
096            if (!memberType.isPresent()) {
097                throw new IllegalArgumentException("When member is set, also the member type needs to be set");
098            }
099            if (member.get().matches(".*\\s.*")) {
100                throw new IllegalArgumentException("member must not contain any whitespace characters!");
101            }
102        }
103        this.memberType = memberType;
104    }
105
106    /**
107     *
108     * @return {@code true} in case this class/package is part of another classloader
109     */
110    public boolean isExternal() {
111        return isExternal;
112    }
113
114    /** @return the package name of the referenced class */
115    public Optional<String> getPackageName() {
116        return packageName;
117    }
118
119    /**
120     * @return the simple class name of the referenced class, may be prefixed by the declaring class names, separated by
121     *         '.' (for inner classes)
122     */
123    public Optional<String> getClassName() {
124        return getPackageNameClassName();
125    }
126
127    /** @return the type of the member. Only empty if no member is set. */
128    public Optional<MemberType> getMemberType() {
129        return memberType;
130    }
131
132    public Optional<String> getFullyQualifiedClassName() {
133        if (getClassName().isPresent() && getPackageName().isPresent()) {
134            return Optional.of(getPackageName().get() + "." + getClassName().get());
135        } else {
136            return Optional.empty();
137        }
138    }
139
140    @Override
141    public String toString() {
142        return "FullyQualifiedJavadocReference [moduleName=" + getModuleName() + ", packageName=" + packageName
143                + ", className=" + getClassName() + ", memberType=" + memberType + ", member=" + getMember()
144                + ", label="
145                + getLabel() + ", isExternal=" + isExternal + "]";
146    }
147
148    @Override
149    public int hashCode() {
150        final int prime = 31;
151        int result = super.hashCode();
152        result = prime * result + Objects.hash(memberType, packageName, isExternal);
153        return result;
154    }
155
156    @Override
157    public boolean equals(Object obj) {
158        if (this == obj) {
159            return true;
160        }
161        if (!super.equals(obj)) {
162            return false;
163        }
164        if (getClass() != obj.getClass()) {
165            return false;
166        }
167        FullyQualifiedJavadocReference other = (FullyQualifiedJavadocReference) obj;
168        return Objects.equals(memberType, other.memberType)
169                && Objects.equals(packageName, other.packageName)
170                && isExternal == other.isExternal;
171    }
172}