001package org.eclipse.aether.internal.impl.collect; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. 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, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.util.Arrays; 023import java.util.Collections; 024import java.util.List; 025 026import org.eclipse.aether.artifact.Artifact; 027import org.eclipse.aether.graph.Dependency; 028import org.eclipse.aether.graph.DependencyCycle; 029import org.eclipse.aether.graph.DependencyNode; 030import org.eclipse.aether.util.artifact.ArtifactIdUtils; 031 032/** 033 * Default implementation of {@link DependencyCycle}. 034 * Internal helper class for collector implementations. 035 */ 036public final class DefaultDependencyCycle 037 implements DependencyCycle 038{ 039 private final List<Dependency> dependencies; 040 041 private final int cycleEntry; 042 043 public DefaultDependencyCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency ) 044 { 045 // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label) 046 int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0; 047 Dependency[] dependencies = new Dependency[nodes.size() - offset + 1]; 048 for ( int i = 0, n = dependencies.length - 1; i < n; i++ ) 049 { 050 DependencyNode node = nodes.get( i + offset ); 051 dependencies[i] = node.getDependency(); 052 // when cycle starts at root artifact as opposed to root dependency, synthesize a dependency 053 if ( dependencies[i] == null ) 054 { 055 dependencies[i] = new Dependency( node.getArtifact(), null ); 056 } 057 } 058 dependencies[dependencies.length - 1] = dependency; 059 this.dependencies = Collections.unmodifiableList( Arrays.asList( dependencies ) ); 060 this.cycleEntry = cycleEntry; 061 } 062 063 @Override 064 public List<Dependency> getPrecedingDependencies() 065 { 066 return dependencies.subList( 0, cycleEntry ); 067 } 068 069 @Override 070 public List<Dependency> getCyclicDependencies() 071 { 072 return dependencies.subList( cycleEntry, dependencies.size() ); 073 } 074 075 /** 076 * Searches for a node associated with the given artifact. A version of the artifact is not considered during the 077 * search. 078 * 079 * @param nodes a list representing single path in the dependency graph. First element is the root. 080 * @param artifact to find among the parent nodes. 081 * @return the index of the node furthest from the root and associated with the given artifact, or {@literal -1} if 082 * there is no such node. 083 */ 084 public static int find( List<DependencyNode> nodes, Artifact artifact ) 085 { 086 087 for ( int i = nodes.size() - 1; i >= 0; i-- ) 088 { 089 DependencyNode node = nodes.get( i ); 090 091 Artifact a = node.getArtifact(); 092 if ( a == null ) 093 { 094 break; 095 } 096 097 if ( !a.getArtifactId().equals( artifact.getArtifactId() ) ) 098 { 099 continue; 100 } 101 if ( !a.getGroupId().equals( artifact.getGroupId() ) ) 102 { 103 continue; 104 } 105 if ( !a.getExtension().equals( artifact.getExtension() ) ) 106 { 107 continue; 108 } 109 if ( !a.getClassifier().equals( artifact.getClassifier() ) ) 110 { 111 continue; 112 } 113 /* 114 * NOTE: While a:1 and a:2 are technically different artifacts, we want to consider the path a:2 -> b:2 -> 115 * a:1 a cycle in the current context. The artifacts themselves might not form a cycle but their producing 116 * projects surely do. Furthermore, conflict resolution will always have to consider a:1 a loser (otherwise 117 * its ancestor a:2 would get pruned and so would a:1) so there is no point in building the sub graph of 118 * a:1. 119 */ 120 121 return i; 122 } 123 124 return -1; 125 } 126 127 @Override 128 public String toString() 129 { 130 StringBuilder buffer = new StringBuilder( 256 ); 131 int i = 0; 132 for ( Dependency dependency : dependencies ) 133 { 134 if ( i++ > 0 ) 135 { 136 buffer.append( " -> " ); 137 } 138 buffer.append( ArtifactIdUtils.toVersionlessId( dependency.getArtifact() ) ); 139 } 140 return buffer.toString(); 141 } 142 143}