001package org.apache.maven.tools.plugin.javadoc; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.util.Objects; 023import java.util.Optional; 024import java.util.regex.Matcher; 025import java.util.regex.Pattern; 026 027import org.codehaus.plexus.util.StringUtils; 028 029/** 030 * Describes a code reference used in javadoc tags {@code see}, {@code link} and {@code linkplain}. 031 * The format of the reference given as string is {@code module/package.class#member label}. 032 * Members must be separated with a {@code #} to be detected. 033 * Targets either module, package, class or field/method/constructor in class. 034 * This class does not know whether the second part part refers to a package, class or both, 035 * as they use the same alphabet and separators. 036 * @see <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/javadoc/doc-comment-spec.html#link">link tag specification</a> 037 */ 038public class JavadocReference 039{ 040 private final Optional<String> moduleName; 041 042 private final Optional<String> packageNameClassName; 043 044 private final Optional<String> member; // optional, but may appear with both className and packageName being null 045 046 private final Optional<String> label; 047 048 /* 049 * Test at https://regex101.com/r/eDzWNx/1 050 * Captures several groups: module name (1), package name and/or class name (2), member (3), label (4) 051 */ 052 private static final Pattern REFERENCE_VALUE_PATTERN = 053 Pattern.compile( "^\\s*(?:(.+)/)??([^#\\s/]+)?(?:#([^\\s\\(]+(?:\\([^\\)]*\\))?))?(?: +([^\\s/]+)\\s*)?$" ); 054 055 private static final int GROUP_INDEX_MODULE = 1; 056 057 private static final int GROUP_INDEX_PACKAGECLASS = 2; 058 059 private static final int GROUP_INDEX_MEMBER = 3; 060 061 private static final int GROUP_INDEX_LABEL = 4; 062 063 /** 064 * 065 * @param reference the reference value to parse 066 * @return the created {@link JavadocReference} 067 * @throws IllegalArgumentException in case the reference has an invalid format 068 */ 069 public static JavadocReference parse( String reference ) 070 { 071 Matcher matcher = REFERENCE_VALUE_PATTERN.matcher( reference ); 072 if ( !matcher.matches() ) 073 { 074 throw new IllegalArgumentException( "Invalid format of javadoc reference: " + reference ); 075 } 076 final Optional<String> moduleName = getOptionalGroup( matcher, GROUP_INDEX_MODULE ); 077 final Optional<String> packageNameClassName = getOptionalGroup( matcher, GROUP_INDEX_PACKAGECLASS ); 078 final Optional<String> member = getOptionalGroup( matcher, GROUP_INDEX_MEMBER ); 079 final Optional<String> label = getOptionalGroup( matcher, GROUP_INDEX_LABEL ); 080 return new JavadocReference( moduleName, packageNameClassName, member, label ); 081 } 082 083 private static Optional<String> getOptionalGroup( Matcher matcher, int index ) 084 { 085 String group = matcher.group( index ); 086 if ( StringUtils.isNotEmpty( group ) ) 087 { 088 return Optional.of( group ); 089 } 090 else 091 { 092 return Optional.empty(); 093 } 094 } 095 096 JavadocReference( Optional<String> moduleName, Optional<String> packageNameClassName, 097 Optional<String> member, Optional<String> label ) 098 { 099 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}