View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.tools.plugin.javadoc;
20  
21  import java.util.Objects;
22  import java.util.Optional;
23  import java.util.regex.Matcher;
24  import java.util.regex.Pattern;
25  
26  import org.codehaus.plexus.util.StringUtils;
27  
28  /**
29   * Describes a code reference used in javadoc tags {@code see}, {@code link} and {@code linkplain}.
30   * The format of the reference given as string is {@code module/package.class#member label}.
31   * Members must be separated with a {@code #} to be detected.
32   * Targets either module, package, class or field/method/constructor in class.
33   * This class does not know whether the second part part refers to a package, class or both,
34   * as they use the same alphabet and separators.
35   * @see <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/javadoc/doc-comment-spec.html#link">link tag specification</a>
36   */
37  public class JavadocReference {
38      private final Optional<String> moduleName;
39  
40      private final Optional<String> packageNameClassName;
41  
42      private final Optional<String> member; // optional, but may appear with both className and packageName being null
43  
44      private final Optional<String> label;
45  
46      /*
47       * Test at https://regex101.com/r/eDzWNx
48       * Captures several groups: module name (1), package name and/or class name (2), member (3), label (4)
49       */
50      private static final Pattern REFERENCE_VALUE_PATTERN =
51              Pattern.compile("^\\s*(?:(.+)/)??([^#\\s/]+)?(?:#([^\\s\\(]+(?:\\([^\\)]*\\))?))?(?: +(.*))?$");
52  
53      private static final int GROUP_INDEX_MODULE = 1;
54  
55      private static final int GROUP_INDEX_PACKAGECLASS = 2;
56  
57      private static final int GROUP_INDEX_MEMBER = 3;
58  
59      private static final int GROUP_INDEX_LABEL = 4;
60  
61      /**
62       *
63       * @param reference the reference value to parse
64       * @return the created {@link JavadocReference}
65       * @throws IllegalArgumentException in case the reference has an invalid format
66       */
67      public static JavadocReference parse(String reference) {
68          Matcher matcher = REFERENCE_VALUE_PATTERN.matcher(reference);
69          if (!matcher.matches()) {
70              throw new IllegalArgumentException("Invalid format of javadoc reference: " + reference);
71          }
72          final Optional<String> moduleName = getOptionalGroup(matcher, GROUP_INDEX_MODULE);
73          final Optional<String> packageNameClassName = getOptionalGroup(matcher, GROUP_INDEX_PACKAGECLASS);
74          final Optional<String> member = getOptionalGroup(matcher, GROUP_INDEX_MEMBER);
75          final Optional<String> label = getOptionalGroup(matcher, GROUP_INDEX_LABEL);
76          return new JavadocReference(moduleName, packageNameClassName, member, label);
77      }
78  
79      private static Optional<String> getOptionalGroup(Matcher matcher, int index) {
80          String group = matcher.group(index);
81          if (StringUtils.isNotEmpty(group)) {
82              return Optional.of(group);
83          } else {
84              return Optional.empty();
85          }
86      }
87  
88      JavadocReference(
89              Optional<String> moduleName,
90              Optional<String> packageNameClassName,
91              Optional<String> member,
92              Optional<String> label) {
93          this.moduleName = moduleName;
94          this.packageNameClassName = packageNameClassName;
95          this.member = member;
96          this.label = label;
97      }
98  
99      public Optional<String> getModuleName() {
100         return moduleName;
101     }
102 
103     /**
104      *
105      * @return a package name, a class name or a package name followed by a class name
106      */
107     public Optional<String> getPackageNameClassName() {
108         return packageNameClassName;
109     }
110 
111     public Optional<String> getMember() {
112         return member;
113     }
114 
115     public Optional<String> getLabel() {
116         return label;
117     }
118 
119     @Override
120     public String toString() {
121         return "JavadocReference [moduleName=" + moduleName + ", packageNameClassName=" + packageNameClassName
122                 + ", member=" + member + ", label=" + label + "]";
123     }
124 
125     @Override
126     public int hashCode() {
127         return Objects.hash(label, member, packageNameClassName, moduleName);
128     }
129 
130     @Override
131     public boolean equals(Object obj) {
132         if (this == obj) {
133             return true;
134         }
135         if (obj == null) {
136             return false;
137         }
138         if (getClass() != obj.getClass()) {
139             return false;
140         }
141         JavadocReference other = (JavadocReference) obj;
142         return Objects.equals(label, other.label)
143                 && Objects.equals(member, other.member)
144                 && Objects.equals(packageNameClassName, other.packageNameClassName)
145                 && Objects.equals(moduleName, other.moduleName);
146     }
147 }