001package ball.maven.plugins.javadoc; 002/*- 003 * ########################################################################## 004 * Javadoc Maven Plugin 005 * %% 006 * Copyright (C) 2021, 2022 Allen D. Ball 007 * %% 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * 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, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 * ########################################################################## 020 */ 021import java.net.MalformedURLException; 022import java.net.URL; 023import java.util.NoSuchElementException; 024import java.util.Set; 025import java.util.TreeMap; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028import lombok.Data; 029import lombok.extern.slf4j.Slf4j; 030import org.apache.commons.text.StringSubstitutor; 031import org.apache.commons.text.lookup.StringLookup; 032import org.apache.maven.artifact.Artifact; 033import org.apache.maven.artifact.resolver.filter.ArtifactFilter; 034import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter; 035 036import static java.util.Arrays.asList; 037import static java.util.stream.Collectors.toSet; 038 039/** 040 * {@code <link/>} parameter. 041 * 042 * {@bean.info} 043 * 044 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 045 */ 046@Data @Slf4j 047public class Link { 048 private static final Pattern NUMBER = Pattern.compile("[0-9]+"); 049 private static final Pattern DOT = Pattern.compile("[.]"); 050 051 private String artifact = null; 052 private URL url = null; 053 054 /** 055 * See {@link StrictPatternIncludesArtifactFilter#include(Artifact)}. 056 */ 057 public boolean include(Artifact artifact) { 058 ArtifactFilter filter = getArtifactFilter(); 059 060 return filter != null && getArtifactFilter().include(artifact); 061 } 062 063 private ArtifactFilter getArtifactFilter() { 064 ArtifactFilter filter = null; 065 066 if (artifact != null) { 067 filter = new StrictPatternIncludesArtifactFilter(asList(artifact.split("[,\\p{Space}]+"))); 068 } 069 070 return filter; 071 } 072 073 /** 074 * Method to return a {@link URL} substituting <code>{g}</code>, 075 * <code>{a}</code>, and <code>{v}</code> with 076 * {@link Artifact#getGroupId()}, {@link Artifact#getArtifactId()}, and 077 * {@link Artifact#getVersion()}, respectively. 078 * 079 * @param artifact The {@link Artifact}. 080 * 081 * @return The {@link URL} after substitution. 082 */ 083 public URL getUrl(Artifact artifact) { 084 URL url = getUrl(); 085 086 if (url != null && artifact != null) { 087 TreeMap<String,String> map = new MapImpl(artifact); 088 StringSubstitutor substitutor = new StringSubstitutor((StringLookup) map, "{", "}", '\\'); 089 090 substitutor.setEnableSubstitutionInVariables(true); 091 092 String string = url.toString(); 093 094 try { 095 url = new URL(substitutor.replace(string)); 096 } catch (MalformedURLException exception) { 097 throw new IllegalArgumentException(exception); 098 } catch (NoSuchElementException exception) { 099 throw new IllegalArgumentException(String.format("No value defined for '%s' in '%s'", 100 exception.getMessage(), string)); 101 } 102 } 103 104 return url; 105 } 106 107 private class MapImpl extends TreeMap<String,String> implements StringLookup { 108 private static final long serialVersionUID = -1285467833760198210L; 109 110 public MapImpl(Artifact artifact) { 111 super(String.CASE_INSENSITIVE_ORDER); 112 113 put("groupId", artifact.getGroupId()); 114 put("artifactId", artifact.getArtifactId()); 115 put("version", artifact.getBaseVersion()); 116 117 put("g", get("groupId")); 118 put("a", get("artifactId")); 119 put("v", get("version")); 120 121 String version = get("version"); 122 123 for (String key : asList("major", "minor", "micro", "patch")) { 124 Matcher matcher = NUMBER.matcher(version); 125 126 if (matcher.lookingAt()) { 127 put(key, matcher.group()); 128 version = version.substring(matcher.group().length()); 129 } else { 130 break; 131 } 132 133 matcher = DOT.matcher(version); 134 135 if (matcher.lookingAt()) { 136 version = version.substring(matcher.group().length()); 137 continue; 138 } else { 139 break; 140 } 141 } 142 } 143 144 @Override 145 public String lookup(String key) { 146 String value = get(key); 147 148 if (value == null) { 149 String prefix = key.toLowerCase(); 150 Set<String> set = 151 tailMap(key).entrySet().stream() 152 .filter(t -> t.getKey().startsWith(prefix)) 153 .map(t -> t.getValue()) 154 .collect(toSet()); 155 156 if (set.size() == 1) { 157 value = set.iterator().next(); 158 } else { 159 throw new NoSuchElementException(key); 160 } 161 } 162 163 return value; 164 } 165 } 166}