001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.util.graph.visitor; 020 021import java.util.ArrayList; 022import java.util.Iterator; 023import java.util.List; 024import java.util.function.Consumer; 025 026import org.eclipse.aether.artifact.Artifact; 027import org.eclipse.aether.graph.Dependency; 028import org.eclipse.aether.graph.DependencyNode; 029import org.eclipse.aether.graph.DependencyVisitor; 030import org.eclipse.aether.util.artifact.ArtifactIdUtils; 031import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; 032import org.eclipse.aether.util.graph.transformer.ConflictResolver; 033 034import static java.util.Objects.requireNonNull; 035 036/** 037 * A dependency visitor that dumps the graph to any {@link Consumer<String>}. Meant for diagnostic and testing, as 038 * it may output the graph to standard output, error or even some logging interface. 039 * 040 * @since 1.9.8 041 */ 042public class DependencyGraphDumper implements DependencyVisitor { 043 044 private final Consumer<String> consumer; 045 046 private final List<ChildInfo> childInfos = new ArrayList<>(); 047 048 public DependencyGraphDumper(Consumer<String> consumer) { 049 this.consumer = requireNonNull(consumer); 050 } 051 052 @Override 053 public boolean visitEnter(DependencyNode node) { 054 consumer.accept(formatIndentation() + formatNode(node)); 055 childInfos.add(new ChildInfo(node.getChildren().size())); 056 return true; 057 } 058 059 private String formatIndentation() { 060 StringBuilder buffer = new StringBuilder(128); 061 for (Iterator<ChildInfo> it = childInfos.iterator(); it.hasNext(); ) { 062 buffer.append(it.next().formatIndentation(!it.hasNext())); 063 } 064 return buffer.toString(); 065 } 066 067 private String formatNode(DependencyNode node) { 068 StringBuilder buffer = new StringBuilder(128); 069 Artifact a = node.getArtifact(); 070 Dependency d = node.getDependency(); 071 buffer.append(a); 072 if (d != null && d.getScope().length() > 0) { 073 buffer.append(" [").append(d.getScope()); 074 if (d.isOptional()) { 075 buffer.append(", optional"); 076 } 077 buffer.append("]"); 078 } 079 String premanaged = DependencyManagerUtils.getPremanagedVersion(node); 080 if (premanaged != null && !premanaged.equals(a.getBaseVersion())) { 081 buffer.append(" (version managed from ").append(premanaged).append(")"); 082 } 083 084 premanaged = DependencyManagerUtils.getPremanagedScope(node); 085 if (premanaged != null && !premanaged.equals(d.getScope())) { 086 buffer.append(" (scope managed from ").append(premanaged).append(")"); 087 } 088 DependencyNode winner = (DependencyNode) node.getData().get(ConflictResolver.NODE_DATA_WINNER); 089 if (winner != null) { 090 if (ArtifactIdUtils.equalsId(a, winner.getArtifact())) { 091 buffer.append(" (nearer exists)"); 092 } else { 093 Artifact w = winner.getArtifact(); 094 buffer.append(" (conflicts with "); 095 if (ArtifactIdUtils.toVersionlessId(a).equals(ArtifactIdUtils.toVersionlessId(w))) { 096 buffer.append(w.getVersion()); 097 } else { 098 buffer.append(w); 099 } 100 buffer.append(")"); 101 } 102 } 103 return buffer.toString(); 104 } 105 106 @Override 107 public boolean visitLeave(DependencyNode node) { 108 if (!childInfos.isEmpty()) { 109 childInfos.remove(childInfos.size() - 1); 110 } 111 if (!childInfos.isEmpty()) { 112 childInfos.get(childInfos.size() - 1).index++; 113 } 114 return true; 115 } 116 117 private static class ChildInfo { 118 119 final int count; 120 121 int index; 122 123 ChildInfo(int count) { 124 this.count = count; 125 } 126 127 public String formatIndentation(boolean end) { 128 boolean last = index + 1 >= count; 129 if (end) { 130 return last ? "\\- " : "+- "; 131 } 132 return last ? " " : "| "; 133 } 134 } 135}