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.apache.maven.resolver.examples.util;
020
021import java.io.PrintStream;
022import java.util.ArrayList;
023import java.util.Iterator;
024import java.util.List;
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
034/**
035 * A dependency visitor that dumps the graph to the console.
036 */
037public class ConsoleDependencyGraphDumper implements DependencyVisitor {
038
039    private final PrintStream out;
040
041    private final List<ChildInfo> childInfos = new ArrayList<>();
042
043    public ConsoleDependencyGraphDumper() {
044        this(null);
045    }
046
047    public ConsoleDependencyGraphDumper(PrintStream out) {
048        this.out = (out != null) ? out : System.out;
049    }
050
051    public boolean visitEnter(DependencyNode node) {
052        out.println(formatIndentation() + formatNode(node));
053        childInfos.add(new ChildInfo(node.getChildren().size()));
054        return true;
055    }
056
057    private String formatIndentation() {
058        StringBuilder buffer = new StringBuilder(128);
059        for (Iterator<ChildInfo> it = childInfos.iterator(); it.hasNext(); ) {
060            buffer.append(it.next().formatIndentation(!it.hasNext()));
061        }
062        return buffer.toString();
063    }
064
065    private String formatNode(DependencyNode node) {
066        StringBuilder buffer = new StringBuilder(128);
067        Artifact a = node.getArtifact();
068        Dependency d = node.getDependency();
069        buffer.append(a);
070        if (d != null && d.getScope().length() > 0) {
071            buffer.append(" [").append(d.getScope());
072            if (d.isOptional()) {
073                buffer.append(", optional");
074            }
075            buffer.append("]");
076        }
077        String premanaged = DependencyManagerUtils.getPremanagedVersion(node);
078        if (premanaged != null && !premanaged.equals(a.getBaseVersion())) {
079            buffer.append(" (version managed from ").append(premanaged).append(")");
080        }
081
082        premanaged = DependencyManagerUtils.getPremanagedScope(node);
083        if (premanaged != null && !premanaged.equals(d.getScope())) {
084            buffer.append(" (scope managed from ").append(premanaged).append(")");
085        }
086        DependencyNode winner = (DependencyNode) node.getData().get(ConflictResolver.NODE_DATA_WINNER);
087        if (winner != null && !ArtifactIdUtils.equalsId(a, winner.getArtifact())) {
088            Artifact w = winner.getArtifact();
089            buffer.append(" (conflicts with ");
090            if (ArtifactIdUtils.toVersionlessId(a).equals(ArtifactIdUtils.toVersionlessId(w))) {
091                buffer.append(w.getVersion());
092            } else {
093                buffer.append(w);
094            }
095            buffer.append(")");
096        }
097        return buffer.toString();
098    }
099
100    public boolean visitLeave(DependencyNode node) {
101        if (!childInfos.isEmpty()) {
102            childInfos.remove(childInfos.size() - 1);
103        }
104        if (!childInfos.isEmpty()) {
105            childInfos.get(childInfos.size() - 1).index++;
106        }
107        return true;
108    }
109
110    private static class ChildInfo {
111
112        final int count;
113
114        int index;
115
116        ChildInfo(int count) {
117            this.count = count;
118        }
119
120        public String formatIndentation(boolean end) {
121            boolean last = index + 1 >= count;
122            if (end) {
123                return last ? "\\- " : "+- ";
124            }
125            return last ? "   " : "|  ";
126        }
127    }
128}