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