1 package org.apache.maven.shared.dependency.graph.internal.maven30;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
40
41
42
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
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
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 }