001package ball.maven.plugins.javadoc; 002/*- 003 * ########################################################################## 004 * Javadoc Maven Plugin 005 * %% 006 * Copyright (C) 2021 - 2023 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.URL; 022import java.util.Collection; 023import java.util.Comparator; 024import java.util.LinkedHashSet; 025import java.util.Map; 026import java.util.Objects; 027import java.util.Set; 028import java.util.TreeMap; 029import java.util.TreeSet; 030import java.util.stream.Stream; 031import javax.inject.Inject; 032import lombok.Getter; 033import lombok.NoArgsConstructor; 034import lombok.ToString; 035import lombok.extern.slf4j.Slf4j; 036import org.apache.maven.artifact.Artifact; 037import org.apache.maven.artifact.ArtifactUtils; 038import org.apache.maven.artifact.DefaultArtifact; 039import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; 040import org.apache.maven.execution.MavenSession; 041import org.apache.maven.model.Dependency; 042import org.apache.maven.plugin.AbstractMojo; 043import org.apache.maven.plugin.MojoExecutionException; 044import org.apache.maven.plugin.MojoFailureException; 045import org.apache.maven.plugins.annotations.Parameter; 046import org.apache.maven.project.DefaultProjectBuildingRequest; 047import org.apache.maven.project.MavenProject; 048import org.apache.maven.project.ProjectBuildingRequest; 049import org.apache.maven.repository.RepositorySystem; 050import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver; 051 052import static java.util.stream.Collectors.toCollection; 053import static lombok.AccessLevel.PROTECTED; 054import static org.apache.commons.lang3.StringUtils.EMPTY; 055import static org.apache.commons.lang3.StringUtils.isBlank; 056import static org.apache.commons.lang3.StringUtils.isNotBlank; 057 058/** 059 * Abstract base class for javadoc {@link org.apache.maven.plugin.Mojo}s. 060 * 061 * {@injected.fields} 062 * 063 * {@bean.info} 064 * 065 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 066 */ 067@NoArgsConstructor(access = PROTECTED) @Getter @ToString @Slf4j 068public abstract class AbstractJavadocMojo extends AbstractMojo { 069 @Parameter(required = false) 070 private Link[] links = new Link[] { }; 071 072 @Parameter(required = false) 073 private Offlinelink[] offlinelinks = new Offlinelink[] { }; 074 075 @Parameter(defaultValue = "false", property = "maven.javadoc.skip") 076 private boolean skip = false; 077 078 @Inject private MavenSession session = null; 079 @Inject private ArtifactHandlerManager manager = null; 080 @Inject private RepositorySystem system = null; 081 @Inject private ArtifactResolver resolver = null; 082 083 /** 084 * Method to produce a {@link Stream} of 085 * {@link MavenProject#getDependencies()} and 086 * {@link MavenProject#getDependencyManagement()} 087 * {@link Dependency Dependencies}. 088 * 089 * @param project The {@link MavenProject}. 090 * 091 * @return The {@link Dependency} {@link Stream}. 092 */ 093 protected Stream<Dependency> getDependencyManagementStream(MavenProject project) { 094 Stream<Dependency> stream = 095 Stream.of(project.getDependencies(), project.getDependencyManagement().getDependencies()) 096 .filter(Objects::nonNull) 097 .flatMap(Collection::stream); 098 099 return stream; 100 } 101 102 /** 103 * Method to get the {@link Set} of {@link Link} {@link URL}s. 104 * 105 * @param project The {@link MavenProject}. 106 * @param includeDependencyManagement 107 * Whether or not to include dependency 108 * management in the analysis. 109 * 110 * @return The {@link Set} of {@link Link} {@link URL}s. 111 */ 112 protected Set<URL> getLinkSet(MavenProject project, boolean includeDependencyManagement) { 113 Set<URL> set = new LinkedHashSet<>(); 114 115 for (Link link : links) { 116 if (link.getArtifact() != null) { 117 Stream<Artifact> stream = project.getArtifacts().stream(); 118 119 if (includeDependencyManagement) { 120 stream = 121 Stream.concat(stream, 122 getDependencyManagementStream(project) 123 .filter(t -> isNotBlank(t.getVersion())) 124 .map(JavadocArtifact::new)); 125 } 126 127 stream 128 .filter(link::include) 129 .map(link::getUrl) 130 .forEach(set::add); 131 } else { 132 set.add(link.getUrl()); 133 } 134 } 135 136 return set; 137 } 138 139 /** 140 * Method to get the {@link Map} of {@link Offlinelink} 141 * {@link Artifact}s to {@link URL}s. 142 * 143 * @param project The {@link MavenProject}. 144 * @param includeDependencyManagement 145 * Whether or not to include dependency 146 * management in the analysis. 147 * 148 * @return The {@link Map} of {@link Offlinelink} {@link Artifact}s to 149 * {@link URL}s. 150 */ 151 protected Map<Artifact,URL> getResolvedOfflinelinkMap(MavenProject project, boolean includeDependencyManagement) { 152 TreeMap<Artifact,URL> map = new TreeMap<>(Comparator.comparing(ArtifactUtils::versionlessKey)); 153 154 for (Offlinelink offlinelink : offlinelinks) { 155 project.getArtifacts().stream() 156 .filter(t -> Objects.equals(t.getType(), "jar")) 157 .filter(t -> Objects.equals(t.getClassifier(), "javadoc")) 158 .filter(offlinelink::include) 159 .forEach(t -> map.putIfAbsent(t, offlinelink.getUrl())); 160 } 161 162 Set<Artifact> artifacts = 163 project.getArtifacts().stream() 164 .filter(t -> Objects.equals(t.getType(), "jar")) 165 .filter(t -> isBlank(t.getClassifier())) 166 .map(JavadocArtifact::new) 167 .collect(toCollection(() -> new TreeSet<>(map.comparator()))); 168 169 if (includeDependencyManagement) { 170 getDependencyManagementStream(project) 171 .filter(t -> Objects.equals(t.getType(), "jar")) 172 .filter(t -> isBlank(t.getClassifier())) 173 .filter(t -> isNotBlank(t.getVersion())) 174 .map(JavadocArtifact::new) 175 .forEach(artifacts::add); 176 } 177 178 artifacts.removeAll(map.keySet()); 179 180 ProjectBuildingRequest request = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); 181 182 for (Offlinelink offlinelink : offlinelinks) { 183 Set<Artifact> set = 184 artifacts.stream() 185 .filter(offlinelink::include) 186 .collect(toCollection(LinkedHashSet::new)); 187 188 for (Artifact artifact : set) { 189 URL url = map.get(artifact); 190 191 if (url == null) { 192 log.info("Resolving {}...", artifact); 193 194 try { 195 Artifact key = resolver.resolveArtifact(request, artifact).getArtifact(); 196 197 map.putIfAbsent(key, offlinelink.getUrl(artifact)); 198 } catch (Exception exception) { 199 log.warn("{}: {}", artifact, exception.getMessage()); 200 log.debug("{}", exception); 201 } 202 } else { 203 if (! Objects.equals(url, offlinelink.getUrl(artifact))) { 204 log.warn("{} matches {} but was previously resolved with {}", artifact, offlinelink, url); 205 } 206 } 207 } 208 } 209 210 return map; 211 } 212 213 @Override 214 public void execute() throws MojoExecutionException, MojoFailureException { 215 } 216 217 private class JavadocArtifact extends DefaultArtifact { 218 public JavadocArtifact(Artifact artifact) { 219 this(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()); 220 } 221 222 public JavadocArtifact(Dependency dependency) { 223 this(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion()); 224 } 225 226 public JavadocArtifact(String gav) { this(gav.split("[:]")); } 227 228 public JavadocArtifact(String... gav) { 229 super(gav.length > 0 ? gav[0] : EMPTY, 230 gav.length > 1 ? gav[1] : EMPTY, 231 gav.length > 2 ? gav[gav.length - 1] : EMPTY, 232 EMPTY, "jar", "javadoc", manager.getArtifactHandler("jar")); 233 } 234 } 235}