OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library pub_semver.src.version_union; | |
6 | |
7 import 'dart:collection'; | |
8 | |
9 import 'package:collection/collection.dart'; | |
10 | |
11 import 'utils.dart'; | |
12 import 'version.dart'; | |
13 import 'version_constraint.dart'; | |
14 import 'version_range.dart'; | |
15 | |
16 /// A (package-private) version constraint representing a union of multiple | |
17 /// disjoint version constraints. | |
18 /// | |
19 /// An instance of this will only be created if the version can't be represented | |
20 /// as a non-compound value. | |
21 class VersionUnion implements VersionConstraint { | |
22 /// The constraints that compose this union. | |
23 /// | |
24 /// This list has several invariants: | |
25 /// | |
26 /// * It contains only [Version]s and [VersionRange]s. | |
27 /// * Its contents are sorted from lowest to highest matched versions. | |
28 /// * Its contents are disjoint and non-adjacent. In other words, for any two | |
29 /// constraints next to each other in the list, there's some version between | |
30 /// those constraints that they don't match. | |
31 final List<VersionConstraint> constraints; | |
32 | |
33 bool get isEmpty => false; | |
34 | |
35 bool get isAny => false; | |
36 | |
37 /// Returns the union of [constraints]. | |
38 /// | |
39 /// This ensures that an actual [VersionUnion] is only returned if necessary. | |
40 /// It also takes care of sorting and merging the constraints to ensure that | |
41 /// they're disjoint. | |
42 static VersionConstraint create(Iterable<VersionConstraint> constraints) { | |
43 constraints = constraints.expand((constraint) { | |
Bob Nystrom
2015/05/05 20:49:09
Instead of reassigning, how about:
var flattened
nweiz
2015/05/05 22:52:34
Done.
| |
44 if (constraint.isEmpty) return []; | |
45 if (constraint is VersionUnion) return constraint.constraints; | |
46 return [constraint]; | |
47 }).toList(); | |
48 | |
49 if (constraints.isEmpty) return VersionConstraint.empty; | |
50 | |
51 if (constraints.any((constraint) => constraint.isAny)) { | |
52 return VersionConstraint.any; | |
53 } | |
54 | |
55 // Only allow Versions and VersionRanges here so we can more easily reason | |
56 // about everything in [constraints]. _EmptyVersions and VersionUnions are | |
57 // filtered out above. | |
58 for (var constraint in constraints) { | |
59 if (constraint is Version || constraint is VersionRange) continue; | |
60 throw new ArgumentError('Unknown VersionConstraint type $constraint.'); | |
61 } | |
62 | |
63 (constraints as List).sort(compareMax); | |
64 | |
65 var merged = []; | |
66 for (var constraint in constraints) { | |
Bob Nystrom
2015/05/05 20:49:09
// If this constraint doesn't touch the previous o
nweiz
2015/05/05 22:52:34
Done.
| |
67 if (merged.isEmpty || | |
68 (!merged.last.allowsAny(constraint) && | |
69 !areAdjacent(merged.last, constraint))) { | |
70 merged.add(constraint); | |
71 } else { | |
72 merged[merged.length - 1] = merged.last.union(constraint); | |
73 } | |
74 } | |
75 | |
76 if (merged.length == 1) return merged.single; | |
77 return new VersionUnion._(merged); | |
78 } | |
79 | |
80 VersionUnion._(List<VersionConstraint> constraints) | |
81 : constraints = new UnmodifiableListView(constraints); | |
Bob Nystrom
2015/05/05 20:49:09
If this is a package private class, is this worth
nweiz
2015/05/05 22:52:34
Probably not. Removed.
| |
82 | |
83 bool allows(Version version) => | |
84 constraints.any((constraint) => constraint.allows(version)); | |
85 | |
86 bool allowsAll(VersionConstraint other) { | |
87 var ourConstraints = constraints.iterator; | |
88 var theirConstraints = _constraintsFor(other).iterator; | |
89 | |
90 // Because both lists of constraints are ordered by minimum version, we can | |
91 // safely move through them linearly here. | |
92 ourConstraints.moveNext(); | |
93 theirConstraints.moveNext(); | |
94 while (ourConstraints.current != null && theirConstraints.current != null) { | |
Bob Nystrom
2015/05/05 20:49:09
Checking for .current == null feels sketchy to me.
nweiz
2015/05/05 22:52:34
I don't think there's a clean way to do that. Even
| |
95 if (ourConstraints.current.allowsAll(theirConstraints.current)) { | |
96 theirConstraints.moveNext(); | |
97 } else { | |
98 ourConstraints.moveNext(); | |
99 } | |
100 } | |
101 | |
102 // If our constraints have allowed all of their constraints, we'll have | |
103 // consumed all of them. | |
104 return theirConstraints.current == null; | |
105 } | |
106 | |
107 bool allowsAny(VersionConstraint other) { | |
108 var ourConstraints = constraints.iterator; | |
109 var theirConstraints = _constraintsFor(other).iterator; | |
110 | |
111 // Because both lists of constraints are ordered by minimum version, we can | |
112 // safely move through them linearly here. | |
113 ourConstraints.moveNext(); | |
114 theirConstraints.moveNext(); | |
115 while (ourConstraints.current != null && theirConstraints.current != null) { | |
116 if (ourConstraints.current.allowsAny(theirConstraints.current)) { | |
117 return true; | |
118 } | |
119 | |
120 // Move the constraint with the higher max value forward. This ensures | |
121 // that we keep both lists in sync as much as possible. | |
122 if (compareMax(ourConstraints.current, theirConstraints.current) < 0) { | |
123 ourConstraints.moveNext(); | |
124 } else { | |
125 theirConstraints.moveNext(); | |
126 } | |
127 } | |
128 | |
129 return false; | |
130 } | |
131 | |
132 VersionConstraint intersect(VersionConstraint other) { | |
133 var ourConstraints = constraints.iterator; | |
134 var theirConstraints = _constraintsFor(other).iterator; | |
135 | |
136 // Because both lists of constraints are ordered by minimum version, we can | |
137 // safely move through them linearly here. | |
138 var newConstraints = []; | |
139 ourConstraints.moveNext(); | |
140 theirConstraints.moveNext(); | |
141 while (ourConstraints.current != null && theirConstraints.current != null) { | |
142 var intersection = ourConstraints.current | |
143 .intersect(theirConstraints.current); | |
144 | |
145 if (!intersection.isEmpty) newConstraints.add(intersection); | |
146 | |
147 // Move the constraint with the higher max value forward. This ensures | |
148 // that we keep both lists in sync as much as possible, and that large | |
149 // constraints have a change to match multiple small constraints that they | |
Bob Nystrom
2015/05/05 20:49:09
"change" -> "chance".
nweiz
2015/05/05 22:52:34
Done.
| |
150 // contain. | |
151 if (compareMax(ourConstraints.current, theirConstraints.current) < 0) { | |
152 ourConstraints.moveNext(); | |
153 } else { | |
154 theirConstraints.moveNext(); | |
155 } | |
156 } | |
157 | |
158 if (newConstraints.isEmpty) return VersionConstraint.empty; | |
159 if (newConstraints.length == 1) return newConstraints.single; | |
160 | |
161 return new VersionUnion._(newConstraints); | |
162 } | |
163 | |
164 /// Returns [constraint] as a list of constraints. | |
165 /// | |
166 /// This is used to normalize constraints of various types. | |
167 List<VersionConstraint> _constraintsFor(VersionConstraint constraint) { | |
Bob Nystrom
2015/05/05 20:49:09
Make this a function?
nweiz
2015/05/05 22:52:34
What do you mean?
Bob Nystrom
2015/05/06 17:30:58
It's an instance method here, but it doesn't acces
nweiz
2015/05/06 18:18:58
We don't typically do this for private methods in
| |
168 if (constraint.isEmpty) return []; | |
169 if (constraint is VersionUnion) return constraint.constraints; | |
170 if (constraint is Version) return [constraint]; | |
171 if (constraint is VersionRange) return [constraint]; | |
172 throw new ArgumentError('Unknown VersionConstraint type $constraint.'); | |
173 } | |
174 | |
175 VersionConstraint union(VersionConstraint other) => | |
176 new VersionConstraint.unionOf([this, other]); | |
177 | |
178 bool operator ==(other) { | |
179 if (other is! VersionUnion) return false; | |
180 return const ListEquality().equals(constraints, other.constraints); | |
181 } | |
182 | |
183 int get hashCode => const ListEquality().hash(constraints); | |
184 | |
185 String toString() => constraints.join(" or "); | |
186 } | |
OLD | NEW |