View Javadoc
1   package org.apache.maven.shared.dependency.graph.internal.maven30;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.HashSet;
25  import java.util.Iterator;
26  import java.util.List;
27  
28  import org.apache.maven.shared.dependency.graph.internal.maven30.ConflictResolver.ConflictContext;
29  import org.apache.maven.shared.dependency.graph.internal.maven30.ConflictResolver.ConflictItem;
30  import org.apache.maven.shared.dependency.graph.internal.maven30.ConflictResolver.VersionSelector;
31  import org.sonatype.aether.RepositoryException;
32  import org.sonatype.aether.collection.UnsolvableVersionConflictException;
33  import org.sonatype.aether.graph.DependencyFilter;
34  import org.sonatype.aether.graph.DependencyNode;
35  import org.sonatype.aether.util.graph.PathRecordingDependencyVisitor;
36  import org.sonatype.aether.version.Version;
37  import org.sonatype.aether.version.VersionConstraint;
38  
39  /**
40   * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
41   * 
42   * @author Gabriel Belingueres
43   * @since 3.1.0
44   */
45  public final class NearestVersionSelector
46      extends VersionSelector
47  {
48  
49      @Override
50      public void selectVersion( ConflictContext context )
51          throws RepositoryException
52      {
53          ConflictGroup group = new ConflictGroup();
54          for ( ConflictItem item : context.getItems() )
55          {
56              DependencyNode node = item.getNode();
57              VersionConstraint constraint = node.getVersionConstraint();
58  
59              boolean backtrack = false;
60              boolean hardConstraint = !constraint.getRanges().isEmpty();
61  //            boolean hardConstraint = constraint.getRange() != null;
62  
63              if ( hardConstraint )
64              {
65                  if ( group.constraints.add( constraint ) )
66                  {
67                      if ( group.winner != null && !constraint.containsVersion( group.winner.getNode().getVersion() ) )
68                      {
69                          backtrack = true;
70                      }
71                  }
72              }
73  
74              if ( isAcceptable( group, node.getVersion() ) )
75              {
76                  group.candidates.add( item );
77  
78                  if ( backtrack )
79                  {
80                      backtrack( group, context );
81                  }
82                  else if ( group.winner == null || isNearer( item, group.winner ) )
83                  {
84                      group.winner = item;
85                  }
86              }
87              else if ( backtrack )
88              {
89                  backtrack( group, context );
90              }
91          }
92          context.setWinner( group.winner );
93      }
94  
95      private void backtrack( ConflictGroup group, ConflictContext context )
96          throws UnsolvableVersionConflictException
97      {
98          group.winner = null;
99  
100         for ( Iterator<ConflictItem> it = group.candidates.iterator(); it.hasNext(); )
101         {
102             ConflictItem candidate = it.next();
103 
104             if ( !isAcceptable( group, candidate.getNode().getVersion() ) )
105             {
106                 it.remove();
107             }
108             else if ( group.winner == null || isNearer( candidate, group.winner ) )
109             {
110                 group.winner = candidate;
111             }
112         }
113 
114         if ( group.winner == null )
115         {
116             throw newFailure( context );
117         }
118     }
119 
120     private boolean isAcceptable( ConflictGroup group, Version version )
121     {
122         for ( VersionConstraint constraint : group.constraints )
123         {
124             if ( !constraint.containsVersion( version ) )
125             {
126                 return false;
127             }
128         }
129         return true;
130     }
131 
132     private boolean isNearer( ConflictItem item1, ConflictItem item2 )
133     {
134         if ( item1.isSibling( item2 ) )
135         {
136             return item1.getNode().getVersion().compareTo( item2.getNode().getVersion() ) > 0;
137         }
138         else
139         {
140             return item1.getDepth() < item2.getDepth();
141         }
142     }
143 
144     private UnsolvableVersionConflictException newFailure( final ConflictContext context )
145     {
146         DependencyFilter filter = new DependencyFilter()
147         {
148             public boolean accept( DependencyNode node, List<DependencyNode> parents )
149             {
150                 return context.isIncluded( node );
151             }
152         };
153         PathRecordingDependencyVisitor visitor = new PathRecordingDependencyVisitor( filter );
154         context.getRoot().accept( visitor );
155         return new UnsolvableVersionConflictException( visitor.getPaths(), context.conflictId );
156 //        return new UnsolvableVersionConflictException( visitor.getPaths() );
157     }
158 
159     static final class ConflictGroup
160     {
161 
162         final Collection<VersionConstraint> constraints;
163 
164         final Collection<ConflictItem> candidates;
165 
166         ConflictItem winner;
167 
168         ConflictGroup()
169         {
170             constraints = new HashSet<VersionConstraint>();
171             candidates = new ArrayList<ConflictItem>( 64 );
172         }
173 
174         @Override
175         public String toString()
176         {
177             return String.valueOf( winner );
178         }
179 
180     }
181 
182 
183 }