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  
27  import org.apache.maven.shared.dependency.graph.internal.maven30.ConflictResolver.ConflictContext;
28  import org.apache.maven.shared.dependency.graph.internal.maven30.ConflictResolver.ConflictItem;
29  import org.apache.maven.shared.dependency.graph.internal.maven30.ConflictResolver.VersionSelector;
30  import org.sonatype.aether.RepositoryException;
31  import org.sonatype.aether.collection.UnsolvableVersionConflictException;
32  import org.sonatype.aether.graph.DependencyFilter;
33  import org.sonatype.aether.graph.DependencyNode;
34  import org.sonatype.aether.util.graph.PathRecordingDependencyVisitor;
35  import org.sonatype.aether.version.Version;
36  import org.sonatype.aether.version.VersionConstraint;
37  
38  /**
39   * This class is a copy of their homonymous in the Eclipse Aether library, adapted to work with Sonatype Aether.
40   * 
41   * @author Gabriel Belingueres
42   * @since 3.1.0
43   */
44  public final class NearestVersionSelector
45      extends VersionSelector
46  {
47  
48      @Override
49      public void selectVersion( ConflictContext context )
50          throws RepositoryException
51      {
52          ConflictGroup group = new ConflictGroup();
53          for ( ConflictItem item : context.getItems() )
54          {
55              DependencyNode node = item.getNode();
56              VersionConstraint constraint = node.getVersionConstraint();
57  
58              boolean backtrack = false;
59              boolean hardConstraint = !constraint.getRanges().isEmpty();
60  //            boolean hardConstraint = constraint.getRange() != null;
61  
62              if ( hardConstraint )
63              {
64                  if ( group.constraints.add( constraint ) )
65                  {
66                      if ( group.winner != null && !constraint.containsVersion( group.winner.getNode().getVersion() ) )
67                      {
68                          backtrack = true;
69                      }
70                  }
71              }
72  
73              if ( isAcceptable( group, node.getVersion() ) )
74              {
75                  group.candidates.add( item );
76  
77                  if ( backtrack )
78                  {
79                      backtrack( group, context );
80                  }
81                  else if ( group.winner == null || isNearer( item, group.winner ) )
82                  {
83                      group.winner = item;
84                  }
85              }
86              else if ( backtrack )
87              {
88                  backtrack( group, context );
89              }
90          }
91          context.setWinner( group.winner );
92      }
93  
94      private void backtrack( ConflictGroup group, ConflictContext context )
95          throws UnsolvableVersionConflictException
96      {
97          group.winner = null;
98  
99          for ( Iterator<ConflictItem> it = group.candidates.iterator(); it.hasNext(); )
100         {
101             ConflictItem candidate = it.next();
102 
103             if ( !isAcceptable( group, candidate.getNode().getVersion() ) )
104             {
105                 it.remove();
106             }
107             else if ( group.winner == null || isNearer( candidate, group.winner ) )
108             {
109                 group.winner = candidate;
110             }
111         }
112 
113         if ( group.winner == null )
114         {
115             throw newFailure( context );
116         }
117     }
118 
119     private boolean isAcceptable( ConflictGroup group, Version version )
120     {
121         for ( VersionConstraint constraint : group.constraints )
122         {
123             if ( !constraint.containsVersion( version ) )
124             {
125                 return false;
126             }
127         }
128         return true;
129     }
130 
131     private boolean isNearer( ConflictItem item1, ConflictItem item2 )
132     {
133         if ( item1.isSibling( item2 ) )
134         {
135             return item1.getNode().getVersion().compareTo( item2.getNode().getVersion() ) > 0;
136         }
137         else
138         {
139             return item1.getDepth() < item2.getDepth();
140         }
141     }
142 
143     private UnsolvableVersionConflictException newFailure( final ConflictContext context )
144     {
145         DependencyFilter filter = ( node, parents ) -> context.isIncluded( node );
146         PathRecordingDependencyVisitor visitor = new PathRecordingDependencyVisitor( filter );
147         context.getRoot().accept( visitor );
148         return new UnsolvableVersionConflictException( visitor.getPaths(), context.conflictId );
149 //        return new UnsolvableVersionConflictException( visitor.getPaths() );
150     }
151 
152     static final class ConflictGroup
153     {
154 
155         final Collection<VersionConstraint> constraints;
156 
157         final Collection<ConflictItem> candidates;
158 
159         ConflictItem winner;
160 
161         ConflictGroup()
162         {
163             constraints = new HashSet<>();
164             candidates = new ArrayList<>( 64 );
165         }
166 
167         @Override
168         public String toString()
169         {
170             return String.valueOf( winner );
171         }
172 
173     }
174 
175 
176 }