@@ -110,35 +110,89 @@ class SourceLocation extends Location, @location_default {
110110
111111bindingset [ version]
112112private int versionField ( string version , int field ) {
113- exists ( string format |
114- format = "(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)" or
115- format = "(\\d+)\\.(\\d+)\\.(\\d+)" or
116- format = "(\\d+)\\.(\\d+)"
113+ exists ( string format , int i |
114+ format = "(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)|" + "(\\d+)\\.(\\d+)\\.(\\d+)|" + "(\\d+)\\.(\\d+)" and
115+ result = version .regexpCapture ( format , i ) .toInt ( )
117116 |
118- result = version .regexpCapture ( format , field ) .toInt ( )
117+ i = [ 1 , 5 , 8 ] and
118+ field = 1
119+ or
120+ i = [ 2 , 6 , 9 ] and
121+ field = 2
122+ or
123+ i = [ 3 , 7 ] and
124+ field = 3
125+ or
126+ i = 4 and
127+ field = 4
119128 ) and
120129 result >= 0 and
121130 result <= 255
122131}
123132
124133/** An assembly version, for example `4.0.0.0` or `4.5`. */
125134class Version extends string {
135+ private int major ;
136+
126137 bindingset [ this ]
127- Version ( ) { exists ( versionField ( this , 1 ) ) }
138+ Version ( ) { major = versionField ( this , 1 ) }
139+
140+ bindingset [ this ]
141+ private int getVersionField ( int field ) {
142+ field = 1 and
143+ result = major
144+ or
145+ field in [ 2 .. 4 ] and
146+ result = versionField ( this , field )
147+ }
128148
129149 /**
130150 * Gets field `field` of this version.
131151 * If the field is unspecified in the version string, then the result is `0`.
132152 */
133153 bindingset [ this ]
134154 int getField ( int field ) {
135- field in [ 1 .. 4 ] and
136- if exists ( versionField ( this , field ) ) then result = versionField ( this , field ) else result = 0
155+ result = this .getVersionField ( field )
156+ or
157+ field in [ 2 .. 4 ] and
158+ not exists ( this .getVersionField ( field ) ) and
159+ result = 0
160+ }
161+
162+ bindingset [ this ]
163+ private string getCanonicalizedField ( int field ) {
164+ exists ( string s , int length |
165+ s = this .getVersionField ( field ) .toString ( ) and
166+ length = s .length ( )
167+ |
168+ // make each field consist of 3 digits
169+ result = concat ( int i | i in [ 1 .. 3 - length ] | "0" ) + s
170+ )
171+ }
172+
173+ /**
174+ * Gets a canonicalized version of this string, where lexicographical ordering
175+ * corresponds to version ordering.
176+ */
177+ bindingset [ this ]
178+ string getCanonicalizedVersion ( ) {
179+ exists ( string res , int length |
180+ res =
181+ strictconcat ( int field , string s |
182+ s = this .getCanonicalizedField ( field )
183+ |
184+ s , "." order by field
185+ ) and
186+ length = res .length ( )
187+ |
188+ // make each canonicalized version consist of 4 chunks of 3 digits separated by a dot
189+ result = res + concat ( int i | i = [ 1 .. 15 - length ] / 4 and i > 0 | ".000" )
190+ )
137191 }
138192
139193 /** Gets the major version, for example `1` in `1.2.3.4`. */
140194 bindingset [ this ]
141- int getMajor ( ) { result = this . getField ( 1 ) }
195+ int getMajor ( ) { result = major }
142196
143197 /** Gets the major revision, for example `2` in `1.2.3.4`. */
144198 bindingset [ this ]
@@ -164,9 +218,7 @@ class Version extends string {
164218 */
165219 bindingset [ this , other]
166220 predicate isEarlierThan ( Version other ) {
167- exists ( int i | this .getField ( i ) < other .getField ( i ) |
168- forall ( int j | j in [ 1 .. i - 1 ] | this .getField ( j ) = other .getField ( j ) )
169- )
221+ this .getCanonicalizedVersion ( ) < other .getCanonicalizedVersion ( )
170222 }
171223
172224 /**
0 commit comments