| package org.apache.maven.repository.metadata; |
| |
| /* |
| * 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.util.ArrayList; |
| import java.util.List; |
| import java.util.TreeSet; |
| |
| import org.apache.maven.artifact.ArtifactScopeEnum; |
| import org.codehaus.plexus.component.annotations.Component; |
| import org.codehaus.plexus.component.annotations.Requirement; |
| |
| /** |
| * Default conflict resolver.Implements closer newer first policy by default, but could be configured via plexus |
| * |
| * @author <a href="mailto:oleg@codehaus.org">Oleg Gusakov</a> |
| */ |
| @Component( role = GraphConflictResolver.class ) |
| public class DefaultGraphConflictResolver |
| implements GraphConflictResolver |
| { |
| /** |
| * artifact, closer to the entry point, is selected |
| */ |
| @Requirement( role = GraphConflictResolutionPolicy.class ) |
| protected GraphConflictResolutionPolicy policy; |
| |
| // ------------------------------------------------------------------------------------- |
| // ------------------------------------------------------------------------------------- |
| public MetadataGraph resolveConflicts( MetadataGraph graph, ArtifactScopeEnum scope ) |
| throws GraphConflictResolutionException |
| { |
| if ( policy == null ) |
| { |
| throw new GraphConflictResolutionException( "no GraphConflictResolutionPolicy injected" ); |
| } |
| |
| if ( graph == null ) |
| { |
| return null; |
| } |
| |
| final MetadataGraphVertex entry = graph.getEntry(); |
| if ( entry == null ) |
| { |
| return null; |
| } |
| |
| if ( graph.isEmpty() ) |
| { |
| throw new GraphConflictResolutionException( "graph with an entry, but not vertices do not exist" ); |
| } |
| |
| if ( graph.isEmptyEdges() ) |
| { |
| return null; // no edges - nothing to worry about |
| } |
| |
| final TreeSet<MetadataGraphVertex> vertices = graph.getVertices(); |
| |
| try |
| { |
| // edge case - single vertex graph |
| if ( vertices.size() == 1 ) |
| { |
| return new MetadataGraph( entry ); |
| } |
| |
| final ArtifactScopeEnum requestedScope = ArtifactScopeEnum.checkScope( scope ); |
| |
| MetadataGraph res = new MetadataGraph( vertices.size() ); |
| res.setVersionedVertices( false ); |
| res.setScopedVertices( false ); |
| |
| MetadataGraphVertex resEntry = res.addVertex( entry.getMd() ); |
| res.setEntry( resEntry ); |
| |
| res.setScope( requestedScope ); |
| |
| for ( MetadataGraphVertex v : vertices ) |
| { |
| final List<MetadataGraphEdge> ins = graph.getIncidentEdges( v ); |
| final MetadataGraphEdge edge = cleanEdges( v, ins, requestedScope ); |
| |
| if ( edge == null ) |
| { // no edges - don't need this vertex anymore |
| if ( entry.equals( v ) ) |
| { // unless it's an entry point. |
| // currently processing the entry point - it should not have any entry incident edges |
| res.getEntry().getMd().setWhy( "This is a graph entry point. No links." ); |
| } |
| else |
| { |
| // System.out.println("--->"+v.getMd().toDomainString() |
| // +" has been terminated on this entry set\n-------------------\n" |
| // +ins |
| // +"\n-------------------\n" |
| // ); |
| } |
| } |
| else |
| { |
| // System.out.println("+++>"+v.getMd().toDomainString()+" still has "+edge.toString() ); |
| // fill in domain md with actual version data |
| ArtifactMetadata md = v.getMd(); |
| ArtifactMetadata newMd = |
| new ArtifactMetadata( md.getGroupId(), md.getArtifactId(), edge.getVersion(), md.getType(), |
| md.getScopeAsEnum(), md.getClassifier(), edge.getArtifactUri(), |
| edge.getSource() == null ? "" : edge.getSource().getMd().toString(), |
| edge.isResolved(), edge.getTarget() == null ? null |
| : edge.getTarget().getMd().getError() ); |
| MetadataGraphVertex newV = res.addVertex( newMd ); |
| MetadataGraphVertex sourceV = res.addVertex( edge.getSource().getMd() ); |
| |
| res.addEdge( sourceV, newV, edge ); |
| } |
| } |
| // System.err.println("Original graph("+graph.getVertices().size()+"):\n"+graph.toString()); |
| // System.err.println("Cleaned("+requestedScope+") graph("+res.getVertices().size()+"):\n"+res.toString()); |
| // System.err.println("Linked("+requestedScope+") |
| // subgraph("+linkedRes.getVertices().size()+"):\n"+linkedRes.toString()); |
| return findLinkedSubgraph( res ); |
| } |
| catch ( MetadataResolutionException e ) |
| { |
| throw new GraphConflictResolutionException( e ); |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------- |
| private MetadataGraph findLinkedSubgraph( MetadataGraph g ) |
| { |
| if ( g.getVertices().size() == 1 ) |
| { |
| return g; |
| } |
| |
| List<MetadataGraphVertex> visited = new ArrayList<>( g.getVertices().size() ); |
| visit( g.getEntry(), visited, g ); |
| |
| List<MetadataGraphVertex> dropList = new ArrayList<>( g.getVertices().size() ); |
| |
| // collect drop list |
| for ( MetadataGraphVertex v : g.getVertices() ) |
| { |
| if ( !visited.contains( v ) ) |
| { |
| dropList.add( v ); |
| } |
| } |
| |
| if ( dropList.size() < 1 ) |
| { |
| return g; |
| } |
| |
| // now - drop vertices |
| TreeSet<MetadataGraphVertex> vertices = g.getVertices(); |
| for ( MetadataGraphVertex v : dropList ) |
| { |
| vertices.remove( v ); |
| } |
| |
| return g; |
| } |
| |
| // ------------------------------------------------------------------------------------- |
| private void visit( MetadataGraphVertex from, List<MetadataGraphVertex> visited, MetadataGraph graph ) |
| { |
| if ( visited.contains( from ) ) |
| { |
| return; |
| } |
| |
| visited.add( from ); |
| |
| List<MetadataGraphEdge> exitList = graph.getExcidentEdges( from ); |
| // String s = "|---> "+from.getMd().toString()+" - "+(exitList == null ? -1 : exitList.size()) + " exit links"; |
| if ( exitList != null && exitList.size() > 0 ) |
| { |
| for ( MetadataGraphEdge e : graph.getExcidentEdges( from ) ) |
| { |
| visit( e.getTarget(), visited, graph ); |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------- |
| private MetadataGraphEdge cleanEdges( MetadataGraphVertex v, List<MetadataGraphEdge> edges, |
| ArtifactScopeEnum scope ) |
| { |
| if ( edges == null || edges.isEmpty() ) |
| { |
| return null; |
| } |
| |
| if ( edges.size() == 1 ) |
| { |
| MetadataGraphEdge e = edges.get( 0 ); |
| if ( scope.encloses( e.getScope() ) ) |
| { |
| return e; |
| } |
| |
| return null; |
| } |
| |
| MetadataGraphEdge res = null; |
| |
| for ( MetadataGraphEdge e : edges ) |
| { |
| if ( !scope.encloses( e.getScope() ) ) |
| { |
| continue; |
| } |
| |
| if ( res == null ) |
| { |
| res = e; |
| } |
| else |
| { |
| res = policy.apply( e, res ); |
| } |
| } |
| |
| return res; |
| } |
| // ------------------------------------------------------------------------------------- |
| // ------------------------------------------------------------------------------------- |
| } |