Java tutorial
package org.apache.maven.report.projectinfo.dependencies.renderer; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.FieldPosition; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.apache.commons.lang.SystemUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; import org.apache.maven.artifact.resolver.ArtifactNotFoundException; import org.apache.maven.artifact.resolver.ArtifactResolutionException; import org.apache.maven.doxia.sink.Sink; import org.apache.maven.doxia.sink.SinkEventAttributeSet; import org.apache.maven.doxia.sink.SinkEventAttributes; import org.apache.maven.doxia.util.HtmlTools; import org.apache.maven.model.License; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectBuilder; import org.apache.maven.project.ProjectBuildingException; import org.apache.maven.report.projectinfo.AbstractProjectInfoRenderer; import org.apache.maven.report.projectinfo.ProjectInfoReportUtils; import org.apache.maven.report.projectinfo.dependencies.Dependencies; import org.apache.maven.report.projectinfo.dependencies.DependenciesReportConfiguration; import org.apache.maven.report.projectinfo.dependencies.RepositoryUtils; import org.apache.maven.settings.Settings; import org.apache.maven.shared.dependency.graph.DependencyNode; import org.apache.maven.shared.jar.JarData; import org.codehaus.plexus.i18n.I18N; import org.codehaus.plexus.util.StringUtils; /** * Renderer the dependencies report. * * @version $Id$ * @since 2.1 */ public class DependenciesRenderer extends AbstractProjectInfoRenderer { /** URL for the 'icon_info_sml.gif' image */ private static final String IMG_INFO_URL = "./images/icon_info_sml.gif"; /** URL for the 'close.gif' image */ private static final String IMG_CLOSE_URL = "./images/close.gif"; /** Used to format decimal values in the "Dependency File Details" table */ protected static final DecimalFormat DEFAULT_DECIMAL_FORMAT = new DecimalFormat("###0"); private static final Set<String> JAR_SUBTYPE; /** * An HTML script tag with the Javascript used by the dependencies report. */ private static final String JAVASCRIPT; private final DependencyNode dependencyNode; private final Dependencies dependencies; private final DependenciesReportConfiguration configuration; private final Log log; private final Settings settings; private final RepositoryUtils repoUtils; /** Used to format file length values */ private final DecimalFormat fileLengthDecimalFormat; /** * @since 2.1.1 */ private int section; /** Counter for unique IDs that is consistent across generations. */ private int idCounter = 0; /** * Will be filled with license name / set of projects. */ private Map<String, Object> licenseMap = new HashMap<String, Object>() { private static final long serialVersionUID = 1L; /** {@inheritDoc} */ public Object put(String key, Object value) { // handle multiple values as a set to avoid duplicates @SuppressWarnings("unchecked") SortedSet<Object> valueList = (SortedSet<Object>) get(key); if (valueList == null) { valueList = new TreeSet<Object>(); } valueList.add(value); return super.put(key, valueList); } }; private final ArtifactFactory artifactFactory; private final MavenProjectBuilder mavenProjectBuilder; private final List<ArtifactRepository> remoteRepositories; private final ArtifactRepository localRepository; static { Set<String> jarSubtype = new HashSet<String>(); jarSubtype.add("jar"); jarSubtype.add("war"); jarSubtype.add("ear"); jarSubtype.add("sar"); jarSubtype.add("rar"); jarSubtype.add("par"); jarSubtype.add("ejb"); JAR_SUBTYPE = Collections.unmodifiableSet(jarSubtype); JAVASCRIPT = "<script language=\"javascript\" type=\"text/javascript\">" + SystemUtils.LINE_SEPARATOR + " function toggleDependencyDetail( divId, imgId )" + SystemUtils.LINE_SEPARATOR + " {" + SystemUtils.LINE_SEPARATOR + " var div = document.getElementById( divId );" + SystemUtils.LINE_SEPARATOR + " var img = document.getElementById( imgId );" + SystemUtils.LINE_SEPARATOR + " if( div.style.display == '' )" + SystemUtils.LINE_SEPARATOR + " {" + SystemUtils.LINE_SEPARATOR + " div.style.display = 'none';" + SystemUtils.LINE_SEPARATOR + " img.src='" + IMG_INFO_URL + "';" + SystemUtils.LINE_SEPARATOR + " }" + SystemUtils.LINE_SEPARATOR + " else" + SystemUtils.LINE_SEPARATOR + " {" + SystemUtils.LINE_SEPARATOR + " div.style.display = '';" + SystemUtils.LINE_SEPARATOR + " img.src='" + IMG_CLOSE_URL + "';" + SystemUtils.LINE_SEPARATOR + " }" + SystemUtils.LINE_SEPARATOR + " }" + SystemUtils.LINE_SEPARATOR + "</script>" + SystemUtils.LINE_SEPARATOR; } /** * Default constructor. * * @param sink * @param locale * @param i18n * @param log * @param settings * @param dependencies * @param dependencyTreeNode * @param config * @param repoUtils * @param artifactFactory * @param mavenProjectBuilder * @param remoteRepositories * @param localRepository */ public DependenciesRenderer(Sink sink, Locale locale, I18N i18n, Log log, Settings settings, Dependencies dependencies, DependencyNode dependencyTreeNode, DependenciesReportConfiguration config, RepositoryUtils repoUtils, ArtifactFactory artifactFactory, MavenProjectBuilder mavenProjectBuilder, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository) { super(sink, i18n, locale); this.log = log; this.settings = settings; this.dependencies = dependencies; this.dependencyNode = dependencyTreeNode; this.repoUtils = repoUtils; this.configuration = config; this.artifactFactory = artifactFactory; this.mavenProjectBuilder = mavenProjectBuilder; this.remoteRepositories = remoteRepositories; this.localRepository = localRepository; // Using the right set of symbols depending of the locale DEFAULT_DECIMAL_FORMAT.setDecimalFormatSymbols(new DecimalFormatSymbols(locale)); this.fileLengthDecimalFormat = new FileDecimalFormat(i18n, locale); this.fileLengthDecimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(locale)); } @Override protected String getI18Nsection() { return "dependencies"; } // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- @Override public void renderBody() { // Dependencies report if (!dependencies.hasDependencies()) { startSection(getTitle()); // TODO: should the report just be excluded? paragraph(getI18nString("nolist")); endSection(); return; } // === Section: Project Dependencies. renderSectionProjectDependencies(); // === Section: Project Transitive Dependencies. renderSectionProjectTransitiveDependencies(); // === Section: Project Dependency Graph. renderSectionProjectDependencyGraph(); // === Section: Licenses renderSectionDependencyLicenseListing(); if (configuration.getDependencyDetailsEnabled()) { // === Section: Dependency File Details. renderSectionDependencyFileDetails(); } if (configuration.getDependencyLocationsEnabled()) { // === Section: Dependency Repository Locations. renderSectionDependencyRepositoryLocations(); } } // ---------------------------------------------------------------------- // Protected methods // ---------------------------------------------------------------------- /** {@inheritDoc} */ // workaround for MPIR-140 // TODO Remove me when maven-reporting-impl:2.1-SNAPSHOT is out protected void startSection(String name) { startSection(name, name); } /** * Start section with a name and a specific anchor. * * @param anchor not null * @param name not null */ protected void startSection(String anchor, String name) { section = section + 1; super.sink.anchor(HtmlTools.encodeId(anchor)); super.sink.anchor_(); switch (section) { case 1: sink.section1(); sink.sectionTitle1(); break; case 2: sink.section2(); sink.sectionTitle2(); break; case 3: sink.section3(); sink.sectionTitle3(); break; case 4: sink.section4(); sink.sectionTitle4(); break; case 5: sink.section5(); sink.sectionTitle5(); break; default: // TODO: warning - just don't start a section break; } text(name); switch (section) { case 1: sink.sectionTitle1_(); break; case 2: sink.sectionTitle2_(); break; case 3: sink.sectionTitle3_(); break; case 4: sink.sectionTitle4_(); break; case 5: sink.sectionTitle5_(); break; default: // TODO: warning - just don't start a section break; } } /** {@inheritDoc} */ // workaround for MPIR-140 // TODO Remove me when maven-reporting-impl:2.1-SNAPSHOT is out protected void endSection() { switch (section) { case 1: sink.section1_(); break; case 2: sink.section2_(); break; case 3: sink.section3_(); break; case 4: sink.section4_(); break; case 5: sink.section5_(); break; default: // TODO: warning - just don't start a section break; } section = section - 1; if (section < 0) { throw new IllegalStateException("Too many closing sections"); } } // ---------------------------------------------------------------------- // Private methods // ---------------------------------------------------------------------- /** * @param withClassifier <code>true</code> to include the classifier column, <code>false</code> otherwise. * @param withOptional <code>true</code> to include the optional column, <code>false</code> otherwise. * @return the dependency table header with/without classifier/optional column * @see #renderArtifactRow(Artifact, boolean, boolean) */ private String[] getDependencyTableHeader(boolean withClassifier, boolean withOptional) { String groupId = getI18nString("column.groupId"); String artifactId = getI18nString("column.artifactId"); String version = getI18nString("column.version"); String classifier = getI18nString("column.classifier"); String type = getI18nString("column.type"); String license = getI18nString("column.license"); String optional = getI18nString("column.optional"); if (withClassifier) { if (withOptional) { return new String[] { groupId, artifactId, version, classifier, type, license, optional }; } return new String[] { groupId, artifactId, version, classifier, type, license }; } if (withOptional) { return new String[] { groupId, artifactId, version, type, license, optional }; } return new String[] { groupId, artifactId, version, type, license }; } private void renderSectionProjectDependencies() { startSection(getTitle()); // collect dependencies by scope Map<String, List<Artifact>> dependenciesByScope = dependencies.getDependenciesByScope(false); renderDependenciesForAllScopes(dependenciesByScope, false); endSection(); } /** * @param dependenciesByScope map with supported scopes as key and a list of <code>Artifact</code> as values. * @param isTransitive <code>true</code> if it is transitive dependencies rendering. * @see Artifact#SCOPE_COMPILE * @see Artifact#SCOPE_PROVIDED * @see Artifact#SCOPE_RUNTIME * @see Artifact#SCOPE_SYSTEM * @see Artifact#SCOPE_TEST */ private void renderDependenciesForAllScopes(Map<String, List<Artifact>> dependenciesByScope, boolean isTransitive) { renderDependenciesForScope(Artifact.SCOPE_COMPILE, dependenciesByScope.get(Artifact.SCOPE_COMPILE), isTransitive); renderDependenciesForScope(Artifact.SCOPE_RUNTIME, dependenciesByScope.get(Artifact.SCOPE_RUNTIME), isTransitive); renderDependenciesForScope(Artifact.SCOPE_TEST, dependenciesByScope.get(Artifact.SCOPE_TEST), isTransitive); renderDependenciesForScope(Artifact.SCOPE_PROVIDED, dependenciesByScope.get(Artifact.SCOPE_PROVIDED), isTransitive); renderDependenciesForScope(Artifact.SCOPE_SYSTEM, dependenciesByScope.get(Artifact.SCOPE_SYSTEM), isTransitive); } private void renderSectionProjectTransitiveDependencies() { Map<String, List<Artifact>> dependenciesByScope = dependencies.getDependenciesByScope(true); startSection(getI18nString("transitive.title")); if (dependenciesByScope.values().isEmpty()) { paragraph(getI18nString("transitive.nolist")); } else { paragraph(getI18nString("transitive.intro")); renderDependenciesForAllScopes(dependenciesByScope, true); } endSection(); } private void renderSectionProjectDependencyGraph() { startSection(getI18nString("graph.title")); // === SubSection: Dependency Tree renderSectionDependencyTree(); endSection(); } private void renderSectionDependencyTree() { sink.rawText(JAVASCRIPT); // for Dependencies Graph Tree startSection(getI18nString("graph.tree.title")); sink.list(); printDependencyListing(dependencyNode); sink.list_(); endSection(); } private void renderSectionDependencyFileDetails() { startSection(getI18nString("file.details.title")); List<Artifact> alldeps = dependencies.getAllDependencies(); Collections.sort(alldeps, getArtifactComparator()); // i18n String filename = getI18nString("file.details.column.file"); String size = getI18nString("file.details.column.size"); String entries = getI18nString("file.details.column.entries"); String classes = getI18nString("file.details.column.classes"); String packages = getI18nString("file.details.column.packages"); String jdkrev = getI18nString("file.details.column.jdkrev"); String debugInformation = getI18nString("file.details.column.debuginformation"); String debugInformationTitle = getI18nString("file.details.columntitle.debuginformation"); String debugInformationCellYes = getI18nString("file.details.cell.debuginformation.yes"); String debugInformationCellNo = getI18nString("file.details.cell.debuginformation.no"); String sealed = getI18nString("file.details.column.sealed"); int[] justification = new int[] { Sink.JUSTIFY_LEFT, Sink.JUSTIFY_RIGHT, Sink.JUSTIFY_RIGHT, Sink.JUSTIFY_RIGHT, Sink.JUSTIFY_RIGHT, Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER }; startTable(justification, false); TotalCell totaldeps = new TotalCell(DEFAULT_DECIMAL_FORMAT); TotalCell totaldepsize = new TotalCell(fileLengthDecimalFormat); TotalCell totalentries = new TotalCell(DEFAULT_DECIMAL_FORMAT); TotalCell totalclasses = new TotalCell(DEFAULT_DECIMAL_FORMAT); TotalCell totalpackages = new TotalCell(DEFAULT_DECIMAL_FORMAT); double highestjdk = 0.0; TotalCell totalDebugInformation = new TotalCell(DEFAULT_DECIMAL_FORMAT); TotalCell totalsealed = new TotalCell(DEFAULT_DECIMAL_FORMAT); boolean hasSealed = hasSealed(alldeps); // Table header String[] tableHeader; String[] tableHeaderTitles; if (hasSealed) { tableHeader = new String[] { filename, size, entries, classes, packages, jdkrev, debugInformation, sealed }; tableHeaderTitles = new String[] { null, null, null, null, null, null, debugInformationTitle, null }; } else { tableHeader = new String[] { filename, size, entries, classes, packages, jdkrev, debugInformation }; tableHeaderTitles = new String[] { null, null, null, null, null, null, debugInformationTitle }; } tableHeader(tableHeader, tableHeaderTitles); // Table rows for (Artifact artifact : alldeps) { if (artifact.getFile() == null) { log.error("Artifact: " + artifact.getId() + " has no file."); continue; } File artifactFile = artifact.getFile(); totaldeps.incrementTotal(artifact.getScope()); totaldepsize.addTotal(artifactFile.length(), artifact.getScope()); if (JAR_SUBTYPE.contains(artifact.getType().toLowerCase())) { try { JarData jarDetails = dependencies.getJarDependencyDetails(artifact); String debugInformationCellValue = debugInformationCellNo; if (jarDetails.isDebugPresent()) { debugInformationCellValue = debugInformationCellYes; totalDebugInformation.incrementTotal(artifact.getScope()); } totalentries.addTotal(jarDetails.getNumEntries(), artifact.getScope()); totalclasses.addTotal(jarDetails.getNumClasses(), artifact.getScope()); totalpackages.addTotal(jarDetails.getNumPackages(), artifact.getScope()); try { if (jarDetails.getJdkRevision() != null) { highestjdk = Math.max(highestjdk, Double.parseDouble(jarDetails.getJdkRevision())); } } catch (NumberFormatException e) { // ignore } String sealedstr = ""; if (jarDetails.isSealed()) { sealedstr = "sealed"; totalsealed.incrementTotal(artifact.getScope()); } String name = artifactFile.getName(); String fileLength = fileLengthDecimalFormat.format(artifactFile.length()); if (artifactFile.isDirectory()) { File parent = artifactFile.getParentFile(); name = parent.getParentFile().getName() + '/' + parent.getName() + '/' + artifactFile.getName(); fileLength = "-"; } tableRow(hasSealed, new String[] { name, fileLength, DEFAULT_DECIMAL_FORMAT.format(jarDetails.getNumEntries()), DEFAULT_DECIMAL_FORMAT.format(jarDetails.getNumClasses()), DEFAULT_DECIMAL_FORMAT.format(jarDetails.getNumPackages()), jarDetails.getJdkRevision(), debugInformationCellValue, sealedstr }); } catch (IOException e) { createExceptionInfoTableRow(artifact, artifactFile, e, hasSealed); } } else { tableRow(hasSealed, new String[] { artifactFile.getName(), fileLengthDecimalFormat.format(artifactFile.length()), "", "", "", "", "", "" }); } } // Total raws tableHeader[0] = getI18nString("file.details.total"); tableHeader(tableHeader); justification[0] = Sink.JUSTIFY_RIGHT; justification[6] = Sink.JUSTIFY_RIGHT; for (int i = -1; i < TotalCell.SCOPES_COUNT; i++) { if (totaldeps.getTotal(i) > 0) { tableRow(hasSealed, new String[] { totaldeps.getTotalString(i), totaldepsize.getTotalString(i), totalentries.getTotalString(i), totalclasses.getTotalString(i), totalpackages.getTotalString(i), (i < 0) ? String.valueOf(highestjdk) : "", totalDebugInformation.getTotalString(i), totalsealed.getTotalString(i) }); } } endTable(); endSection(); } // Almost as same as in the abstract class but includes the title attribute private void tableHeader(String[] content, String[] titles) { sink.tableRow(); if (content != null) { if (titles != null && content.length != titles.length) throw new IllegalArgumentException( "Length of title array must equal the length of the content array"); for (int i = 0; i < content.length; i++) { if (titles != null) tableHeaderCell(content[i], titles[i]); else tableHeaderCell(content[i]); } } sink.tableRow_(); } private void tableHeaderCell(String text, String title) { if (title != null) { SinkEventAttributes attributes = new SinkEventAttributeSet(SinkEventAttributes.TITLE, title); sink.tableHeaderCell(attributes); } else { sink.tableHeaderCell(); } text(text); sink.tableHeaderCell_(); } private void tableRow(boolean fullRow, String[] content) { sink.tableRow(); int count = fullRow ? content.length : (content.length - 1); for (int i = 0; i < count; i++) { tableCell(content[i]); } sink.tableRow_(); } private void createExceptionInfoTableRow(Artifact artifact, File artifactFile, Exception e, boolean hasSealed) { tableRow(hasSealed, new String[] { artifact.getId(), artifactFile.getAbsolutePath(), e.getMessage(), "", "", "", "", "" }); } private void populateRepositoryMap(Map<String, ArtifactRepository> repos, List<ArtifactRepository> rowRepos) { for (ArtifactRepository repo : rowRepos) { repos.put(repo.getId(), repo); } } private void blacklistRepositoryMap(Map<String, ArtifactRepository> repos, List<String> repoUrlBlackListed) { for (ArtifactRepository repo : repos.values()) { // ping repo if (repo.isBlacklisted()) { repoUrlBlackListed.add(repo.getUrl()); } else { if (repoUrlBlackListed.contains(repo.getUrl())) { repo.setBlacklisted(true); } else { try { URL repoUrl = new URL(repo.getUrl()); if (ProjectInfoReportUtils.getContent(repoUrl, settings) == null) { log.warn("The repository url '" + repoUrl + "' has no stream - Repository '" + repo.getId() + "' will be blacklisted."); repo.setBlacklisted(true); repoUrlBlackListed.add(repo.getUrl()); } } catch (IOException e) { log.warn("The repository url '" + repo.getUrl() + "' is invalid - Repository '" + repo.getId() + "' will be blacklisted."); repo.setBlacklisted(true); repoUrlBlackListed.add(repo.getUrl()); } } } } } @SuppressWarnings("unchecked") private void renderSectionDependencyRepositoryLocations() { startSection(getI18nString("repo.locations.title")); // Collect Alphabetical Dependencies List<Artifact> alldeps = dependencies.getAllDependencies(); Collections.sort(alldeps, getArtifactComparator()); // Collect Repositories Map<String, ArtifactRepository> repoMap = new HashMap<String, ArtifactRepository>(); populateRepositoryMap(repoMap, repoUtils.getRemoteArtifactRepositories()); for (Artifact artifact : alldeps) { try { MavenProject artifactProject = repoUtils.getMavenProjectFromRepository(artifact); populateRepositoryMap(repoMap, artifactProject.getRemoteArtifactRepositories()); } catch (ProjectBuildingException e) { log.warn("Unable to create Maven project from repository for artifact " + artifact.getId(), e); } } List<String> repoUrlBlackListed = new ArrayList<String>(); blacklistRepositoryMap(repoMap, repoUrlBlackListed); // Render Repository List printRepositories(repoMap, repoUrlBlackListed); // Render Artifacts locations printArtifactsLocations(repoMap, repoUrlBlackListed, alldeps); endSection(); } private void renderSectionDependencyLicenseListing() { startSection(getI18nString("graph.tables.licenses")); printGroupedLicenses(); endSection(); } private void renderDependenciesForScope(String scope, List<Artifact> artifacts, boolean isTransitive) { if (artifacts != null) { boolean withClassifier = hasClassifier(artifacts); boolean withOptional = hasOptional(artifacts); String[] tableHeader = getDependencyTableHeader(withClassifier, withOptional); // can't use straight artifact comparison because we want optional last Collections.sort(artifacts, getArtifactComparator()); String anchorByScope = (isTransitive ? getI18nString("transitive.title") + "_" + scope : getI18nString("title") + "_" + scope); startSection(anchorByScope, scope); paragraph(getI18nString("intro." + scope)); startTable(); tableHeader(tableHeader); for (Artifact artifact : artifacts) { renderArtifactRow(artifact, withClassifier, withOptional); } endTable(); endSection(); } } private Comparator<Artifact> getArtifactComparator() { return new Comparator<Artifact>() { public int compare(Artifact a1, Artifact a2) { // put optional last if (a1.isOptional() && !a2.isOptional()) { return +1; } else if (!a1.isOptional() && a2.isOptional()) { return -1; } else { return a1.compareTo(a2); } } }; } /** * @param artifact not null * @param withClassifier <code>true</code> to include the classifier column, <code>false</code> otherwise. * @param withOptional <code>true</code> to include the optional column, <code>false</code> otherwise. * @see #getDependencyTableHeader(boolean, boolean) */ private void renderArtifactRow(Artifact artifact, boolean withClassifier, boolean withOptional) { String isOptional = artifact.isOptional() ? getI18nString("column.isOptional") : getI18nString("column.isNotOptional"); String url = ProjectInfoReportUtils.getArtifactUrl(artifactFactory, artifact, mavenProjectBuilder, remoteRepositories, localRepository); String artifactIdCell = ProjectInfoReportUtils.getArtifactIdCell(artifact.getArtifactId(), url); MavenProject artifactProject; StringBuilder sb = new StringBuilder(); try { artifactProject = repoUtils.getMavenProjectFromRepository(artifact); @SuppressWarnings("unchecked") List<License> licenses = artifactProject.getLicenses(); for (License license : licenses) { sb.append(ProjectInfoReportUtils.getArtifactIdCell(license.getName(), license.getUrl())); } } catch (ProjectBuildingException e) { log.warn("Unable to create Maven project from repository.", e); } String content[]; if (withClassifier) { content = new String[] { artifact.getGroupId(), artifactIdCell, artifact.getVersion(), artifact.getClassifier(), artifact.getType(), sb.toString(), isOptional }; } else { content = new String[] { artifact.getGroupId(), artifactIdCell, artifact.getVersion(), artifact.getType(), sb.toString(), isOptional }; } tableRow(withOptional, content); } private void printDependencyListing(DependencyNode node) { Artifact artifact = node.getArtifact(); String id = artifact.getId(); String dependencyDetailId = "_dep" + idCounter++; String imgId = "_img" + idCounter++; sink.listItem(); sink.text(id + (StringUtils.isNotEmpty(artifact.getScope()) ? " (" + artifact.getScope() + ") " : " ")); sink.rawText("<img id=\"" + imgId + "\" src=\"" + IMG_INFO_URL + "\" alt=\"Information\" onclick=\"toggleDependencyDetail( '" + dependencyDetailId + "', '" + imgId + "' );\" style=\"cursor: pointer;vertical-align:text-bottom;\"></img>"); printDescriptionsAndURLs(node, dependencyDetailId); if (!node.getChildren().isEmpty()) { boolean toBeIncluded = false; List<DependencyNode> subList = new ArrayList<DependencyNode>(); for (DependencyNode dep : node.getChildren()) { if (dependencies.getAllDependencies().contains(dep.getArtifact())) { subList.add(dep); toBeIncluded = true; } } if (toBeIncluded) { sink.list(); for (DependencyNode dep : subList) { printDependencyListing(dep); } sink.list_(); } } sink.listItem_(); } private void printDescriptionsAndURLs(DependencyNode node, String uid) { Artifact artifact = node.getArtifact(); String id = artifact.getId(); String unknownLicenseMessage = getI18nString("graph.tables.unknown"); sink.rawText("<div id=\"" + uid + "\" style=\"display:none\">"); sink.table(); if (!Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) { try { MavenProject artifactProject = repoUtils.getMavenProjectFromRepository(artifact); String artifactDescription = artifactProject.getDescription(); String artifactUrl = artifactProject.getUrl(); String artifactName = artifactProject.getName(); @SuppressWarnings("unchecked") List<License> licenses = artifactProject.getLicenses(); sink.tableRow(); sink.tableHeaderCell(); sink.text(artifactName); sink.tableHeaderCell_(); sink.tableRow_(); sink.tableRow(); sink.tableCell(); sink.paragraph(); sink.bold(); sink.text(getI18nString("column.description") + ": "); sink.bold_(); if (StringUtils.isNotEmpty(artifactDescription)) { sink.text(artifactDescription); } else { sink.text(getI18nString("index", "nodescription")); } sink.paragraph_(); if (StringUtils.isNotEmpty(artifactUrl)) { sink.paragraph(); sink.bold(); sink.text(getI18nString("column.url") + ": "); sink.bold_(); if (ProjectInfoReportUtils.isArtifactUrlValid(artifactUrl)) { sink.link(artifactUrl); sink.text(artifactUrl); sink.link_(); } else { sink.text(artifactUrl); } sink.paragraph_(); } sink.paragraph(); sink.bold(); sink.text(getI18nString("license", "title") + ": "); sink.bold_(); if (!licenses.isEmpty()) { for (License element : licenses) { String licenseName = element.getName(); String licenseUrl = element.getUrl(); if (licenseUrl != null) { sink.link(licenseUrl); } sink.text(licenseName); if (licenseUrl != null) { sink.link_(); } licenseMap.put(licenseName, artifactName); } } else { sink.text(getI18nString("license", "nolicense")); licenseMap.put(unknownLicenseMessage, artifactName); } sink.paragraph_(); } catch (ProjectBuildingException e) { log.warn("Unable to create Maven project from repository for artifact " + artifact.getId(), e); } } else { sink.tableRow(); sink.tableHeaderCell(); sink.text(id); sink.tableHeaderCell_(); sink.tableRow_(); sink.tableRow(); sink.tableCell(); sink.paragraph(); sink.bold(); sink.text(getI18nString("column.description") + ": "); sink.bold_(); sink.text(getI18nString("index", "nodescription")); sink.paragraph_(); if (artifact.getFile() != null) { sink.paragraph(); sink.bold(); sink.text(getI18nString("column.url") + ": "); sink.bold_(); sink.text(artifact.getFile().getAbsolutePath()); sink.paragraph_(); } } sink.tableCell_(); sink.tableRow_(); sink.table_(); sink.rawText("</div>"); } private void printGroupedLicenses() { for (Map.Entry<String, Object> entry : licenseMap.entrySet()) { String licenseName = entry.getKey(); sink.paragraph(); sink.bold(); if (StringUtils.isEmpty(licenseName)) { sink.text(getI18nString("unamed")); } else { sink.text(licenseName); } sink.text(": "); sink.bold_(); @SuppressWarnings("unchecked") SortedSet<String> projects = (SortedSet<String>) entry.getValue(); for (Iterator<String> iterator = projects.iterator(); iterator.hasNext();) { String projectName = iterator.next(); sink.text(projectName); if (iterator.hasNext()) { sink.text(", "); } } sink.paragraph_(); } } private void printRepositories(Map<String, ArtifactRepository> repoMap, List<String> repoUrlBlackListed) { // i18n String repoid = getI18nString("repo.locations.column.repoid"); String url = getI18nString("repo.locations.column.url"); String release = getI18nString("repo.locations.column.release"); String snapshot = getI18nString("repo.locations.column.snapshot"); String blacklisted = getI18nString("repo.locations.column.blacklisted"); String releaseEnabled = getI18nString("repo.locations.cell.release.enabled"); String releaseDisabled = getI18nString("repo.locations.cell.release.disabled"); String snapshotEnabled = getI18nString("repo.locations.cell.snapshot.enabled"); String snapshotDisabled = getI18nString("repo.locations.cell.snapshot.disabled"); String blacklistedEnabled = getI18nString("repo.locations.cell.blacklisted.enabled"); String blacklistedDisabled = getI18nString("repo.locations.cell.blacklisted.disabled"); // Table header String[] tableHeader; int[] justificationRepo; if (repoUrlBlackListed.isEmpty()) { tableHeader = new String[] { repoid, url, release, snapshot }; justificationRepo = new int[] { Sink.JUSTIFY_LEFT, Sink.JUSTIFY_LEFT, Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER }; } else { tableHeader = new String[] { repoid, url, release, snapshot, blacklisted }; justificationRepo = new int[] { Sink.JUSTIFY_LEFT, Sink.JUSTIFY_LEFT, Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER, Sink.JUSTIFY_CENTER }; } startTable(justificationRepo, false); tableHeader(tableHeader); // Table rows for (ArtifactRepository repo : repoMap.values()) { List<ArtifactRepository> mirroredRepos = getMirroredRepositories(repo); sink.tableRow(); sink.tableCell(); boolean addLineBreak = false; for (ArtifactRepository r : mirroredRepos) { if (addLineBreak) { sink.lineBreak(); } addLineBreak = true; sink.text(r.getId()); } sink.tableCell_(); sink.tableCell(); addLineBreak = false; for (ArtifactRepository r : mirroredRepos) { if (addLineBreak) { sink.lineBreak(); } addLineBreak = true; if (repo.isBlacklisted()) { sink.text(r.getUrl()); } else { sink.link(r.getUrl()); sink.text(r.getUrl()); sink.link_(); } } sink.tableCell_(); ArtifactRepositoryPolicy releasePolicy = repo.getReleases(); tableCell(releasePolicy.isEnabled() ? releaseEnabled : releaseDisabled); ArtifactRepositoryPolicy snapshotPolicy = repo.getSnapshots(); tableCell(snapshotPolicy.isEnabled() ? snapshotEnabled : snapshotDisabled); if (!repoUrlBlackListed.isEmpty()) { tableCell(repoUrlBlackListed.contains(repo.getUrl()) ? blacklistedEnabled : blacklistedDisabled); } sink.tableRow_(); } endTable(); } private Object invoke(Object object, String method) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return object.getClass().getMethod(method).invoke(object); } /** * Get the repos that can be hidden behind a mirror. * * @param repo the repo used to download artifacts * @return the mirrored repositories or a singleton with actual repo if it is not a mirror */ private List<ArtifactRepository> getMirroredRepositories(ArtifactRepository repo) { try { @SuppressWarnings("unchecked") List<ArtifactRepository> mirroredRepos = (List<ArtifactRepository>) invoke(repo, "getMirroredRepositories"); if ((mirroredRepos != null) && (!mirroredRepos.isEmpty())) { return mirroredRepos; } } catch (IllegalArgumentException e) { // ignore: API not available before Maven 3.0.3 } catch (SecurityException e) { // ignore: API not available before Maven 3.0.3 } catch (IllegalAccessException e) { // ignore: API not available before Maven 3.0.3 } catch (InvocationTargetException e) { // ignore: API not available before Maven 3.0.3 } catch (NoSuchMethodException e) { // ignore: API not available before Maven 3.0.3 } // before Maven 3.0.3, we can't do anything: Maven 3.0-alpha to 3.0.2 will show the mirror return Collections.singletonList(repo); } private void printArtifactsLocations(Map<String, ArtifactRepository> repoMap, List<String> repoUrlBlackListed, List<Artifact> alldeps) { // i18n String artifact = getI18nString("repo.locations.column.artifact"); sink.paragraph(); sink.text(getI18nString("repo.locations.artifact.breakdown")); sink.paragraph_(); List<String> repoIdList = new ArrayList<String>(); // removed blacklisted repo for (Map.Entry<String, ArtifactRepository> entry : repoMap.entrySet()) { String repokey = entry.getKey(); ArtifactRepository repo = entry.getValue(); if (!(repo.isBlacklisted() || repoUrlBlackListed.contains(repo.getUrl()))) { repoIdList.add(repokey); } } String[] tableHeader = new String[repoIdList.size() + 1]; int[] justificationRepo = new int[repoIdList.size() + 1]; tableHeader[0] = artifact; justificationRepo[0] = Sink.JUSTIFY_LEFT; int idnum = 1; for (String id : repoIdList) { tableHeader[idnum] = id; justificationRepo[idnum] = Sink.JUSTIFY_CENTER; idnum++; } Map<String, Integer> totalByRepo = new HashMap<String, Integer>(); TotalCell totaldeps = new TotalCell(DEFAULT_DECIMAL_FORMAT); startTable(justificationRepo, false); tableHeader(tableHeader); for (Artifact dependency : alldeps) { totaldeps.incrementTotal(dependency.getScope()); sink.tableRow(); tableCell(dependency.getId()); if (Artifact.SCOPE_SYSTEM.equals(dependency.getScope())) { for (@SuppressWarnings("unused") String repoId : repoIdList) { tableCell("-"); } } else { for (String repokey : repoIdList) { ArtifactRepository repo = repoMap.get(repokey); String depUrl = repoUtils.getDependencyUrlFromRepository(dependency, repo); Integer old = totalByRepo.get(repokey); if (old == null) { old = 0; totalByRepo.put(repokey, old); } boolean dependencyExists = false; // check snapshots in snapshots repository only and releases in release repositories... if ((dependency.isSnapshot() && repo.getSnapshots().isEnabled()) || (!dependency.isSnapshot() && repo.getReleases().isEnabled())) { dependencyExists = repoUtils.dependencyExistsInRepo(repo, dependency); } if (dependencyExists) { sink.tableCell(); if (StringUtils.isNotEmpty(depUrl)) { sink.link(depUrl); } else { sink.text(depUrl); } sink.figure(); sink.figureCaption(); sink.text("Found at " + repo.getUrl()); sink.figureCaption_(); sink.figureGraphics("images/icon_success_sml.gif"); sink.figure_(); sink.link_(); sink.tableCell_(); totalByRepo.put(repokey, old.intValue() + 1); } else { tableCell("-"); } } } sink.tableRow_(); } // Total row // reused key tableHeader[0] = getI18nString("file.details.total"); tableHeader(tableHeader); String[] totalRow = new String[repoIdList.size() + 1]; totalRow[0] = totaldeps.toString(); idnum = 1; for (String repokey : repoIdList) { Integer deps = totalByRepo.get(repokey); totalRow[idnum++] = deps != null ? deps.toString() : "0"; } tableRow(totalRow); endTable(); } /** * @param artifacts not null * @return <code>true</code> if one artifact in the list has a classifier, <code>false</code> otherwise. */ private boolean hasClassifier(List<Artifact> artifacts) { for (Artifact artifact : artifacts) { if (StringUtils.isNotEmpty(artifact.getClassifier())) { return true; } } return false; } /** * @param artifacts not null * @return <code>true</code> if one artifact in the list is optional, <code>false</code> otherwise. */ private boolean hasOptional(List<Artifact> artifacts) { for (Artifact artifact : artifacts) { if (artifact.isOptional()) { return true; } } return false; } /** * @param artifacts not null * @return <code>true</code> if one artifact in the list is sealed, <code>false</code> otherwise. */ private boolean hasSealed(List<Artifact> artifacts) { for (Artifact artifact : artifacts) { // TODO site:run Why do we need to resolve this... if (artifact.getFile() == null) { if (Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) { // can not resolve system scope artifact file continue; } try { repoUtils.resolve(artifact); } catch (ArtifactResolutionException e) { log.error("Artifact: " + artifact.getId() + " has no file.", e); continue; } catch (ArtifactNotFoundException e) { if ((dependencies.getProject().getGroupId().equals(artifact.getGroupId())) && (dependencies.getProject().getArtifactId().equals(artifact.getArtifactId())) && (dependencies.getProject().getVersion().equals(artifact.getVersion()))) { log.warn("The artifact of this project has never been deployed."); } else { log.error("Artifact: " + artifact.getId() + " has no file.", e); } continue; } if (artifact.getFile() == null) { log.error("Artifact: " + artifact.getId() + " has no file, even after resolution."); continue; } } if (JAR_SUBTYPE.contains(artifact.getType().toLowerCase())) { try { JarData jarDetails = dependencies.getJarDependencyDetails(artifact); if (jarDetails.isSealed()) { return true; } } catch (IOException e) { log.error("Artifact: " + artifact.getId() + " caused IOException: " + e.getMessage(), e); } } } return false; } /** * Formats file length with the associated <a href="https://en.wikipedia.org/wiki/Metric_prefix">SI</a> prefix * (GB, MB, kB) and using the pattern <code>###0.00</code> by default. * * @see <a href="https://en.wikipedia.org/wiki/Metric_prefix">https://en.wikipedia.org/wiki/Metric_prefix</a> * @see <a href="https://en.wikipedia.org/wiki/Binary_prefix">https://en.wikipedia.org/wiki/Binary_prefix</a> * @see <a href="https://en.wikipedia.org/wiki/Octet_%28computing%29">https://en.wikipedia.org/wiki/Octet_(computing)</a> */ static class FileDecimalFormat extends DecimalFormat { private static final long serialVersionUID = 4062503546523610081L; private final I18N i18n; private final Locale locale; /** * Default constructor * * @param i18n * @param locale */ public FileDecimalFormat(I18N i18n, Locale locale) { super("###0.00"); this.i18n = i18n; this.locale = locale; } /** {@inheritDoc} */ public StringBuffer format(long fs, StringBuffer result, FieldPosition fieldPosition) { if (fs > 1000 * 1000 * 1000) { result = super.format((float) fs / (1000 * 1000 * 1000), result, fieldPosition); result.append(" ").append(getString("report.dependencies.file.details.column.size.gb")); return result; } if (fs > 1000 * 1000) { result = super.format((float) fs / (1000 * 1000), result, fieldPosition); result.append(" ").append(getString("report.dependencies.file.details.column.size.mb")); return result; } result = super.format((float) fs / (1000), result, fieldPosition); result.append(" ").append(getString("report.dependencies.file.details.column.size.kb")); return result; } private String getString(String key) { return i18n.getString("project-info-report", locale, key); } } /** * Combine total and total by scope in a cell. */ static class TotalCell { static final int SCOPES_COUNT = 5; final DecimalFormat decimalFormat; long total = 0; long totalCompileScope = 0; long totalTestScope = 0; long totalRuntimeScope = 0; long totalProvidedScope = 0; long totalSystemScope = 0; TotalCell(DecimalFormat decimalFormat) { this.decimalFormat = decimalFormat; } void incrementTotal(String scope) { addTotal(1, scope); } static String getScope(int index) { switch (index) { case 0: return Artifact.SCOPE_COMPILE; case 1: return Artifact.SCOPE_TEST; case 2: return Artifact.SCOPE_RUNTIME; case 3: return Artifact.SCOPE_PROVIDED; case 4: return Artifact.SCOPE_SYSTEM; default: return null; } } long getTotal(int index) { switch (index) { case 0: return totalCompileScope; case 1: return totalTestScope; case 2: return totalRuntimeScope; case 3: return totalProvidedScope; case 4: return totalSystemScope; default: return total; } } String getTotalString(int index) { long totalString = getTotal(index); if (totalString <= 0) { return ""; } StringBuilder sb = new StringBuilder(); if (index >= 0) { sb.append(getScope(index)).append(": "); } sb.append(decimalFormat.format(getTotal(index))); return sb.toString(); } void addTotal(long add, String scope) { total += add; if (Artifact.SCOPE_COMPILE.equals(scope)) { totalCompileScope += add; } else if (Artifact.SCOPE_TEST.equals(scope)) { totalTestScope += add; } else if (Artifact.SCOPE_RUNTIME.equals(scope)) { totalRuntimeScope += add; } else if (Artifact.SCOPE_PROVIDED.equals(scope)) { totalProvidedScope += add; } else if (Artifact.SCOPE_SYSTEM.equals(scope)) { totalSystemScope += add; } } /** {@inheritDoc} */ public String toString() { StringBuilder sb = new StringBuilder(); sb.append(decimalFormat.format(total)); sb.append(" ("); boolean needSeparator = false; for (int i = 0; i < SCOPES_COUNT; i++) { if (getTotal(i) > 0) { if (needSeparator) { sb.append(", "); } sb.append(getTotalString(i)); needSeparator = true; } } sb.append(")"); return sb.toString(); } } }