--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MetacelloVersionNumber.st Mon Sep 03 11:13:41 2012 +0000
@@ -0,0 +1,344 @@
+"{ Package: 'stx:goodies/metacello' }"
+
+Magnitude subclass:#MetacelloVersionNumber
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ category:'Metacello-Core-Model'
+!
+
+
+!MetacelloVersionNumber class methodsFor:'instance creation'!
+
+fromString: aString
+
+ | new components |
+ components := OrderedCollection new.
+ (aString findTokens: '.') do: [:subString | | strs |
+ strs := subString findTokens: '-'.
+ "first subString token could be an integer"
+ components add: (self extractNumericComponent: strs first).
+ strs size > 1
+ ifTrue: [
+ "remaining are uncoditionally Strings, because of leading $-"
+ components addAll: strs allButFirst ]].
+ new := self new: components size.
+ 1 to: components size do: [:i | new at: i put: (components at: i) ].
+ ^new
+! !
+
+!MetacelloVersionNumber class methodsFor:'private'!
+
+extractNumericComponent: subString
+ "$. separated components are integers"
+
+ | number |
+ number := [subString asNumber]
+ on: Error
+ do: [:ex | ex return: subString ].
+ ^number asString = subString
+ ifTrue: [ number ]
+ ifFalse: [ subString ]
+! !
+
+!MetacelloVersionNumber methodsFor:'accessing'!
+
+approximateBase
+
+ | base |
+ base := self copyFrom: 1 to: self size - 1.
+ base at: base size put: (base at: base size) + 1.
+ ^base
+!
+
+versionString
+
+ | strm |
+ strm := WriteStream on: String new.
+ self printOn: strm.
+ ^strm contents
+! !
+
+!MetacelloVersionNumber methodsFor:'comparing'!
+
+< aMetacelloVersionNumber
+
+ | condensed aCondensed |
+ aMetacelloVersionNumber species = self species
+ ifFalse: [ ^ false ].
+ condensed := self collapseZeros.
+ aCondensed := aMetacelloVersionNumber collapseZeros.
+ (condensed ~~ self or: [ aCondensed ~~ aMetacelloVersionNumber ])
+ ifTrue: [ ^ condensed compareLessThan: aCondensed ].
+ ^ self compareLessThan: aMetacelloVersionNumber
+!
+
+= aMetacelloVersionNumber
+
+ | condensed aCondensed |
+ aMetacelloVersionNumber species = self species
+ ifFalse: [ ^ false ].
+ condensed := self collapseZeros.
+ aCondensed := aMetacelloVersionNumber collapseZeros.
+ (condensed ~~ self or: [ aCondensed ~~ aMetacelloVersionNumber ])
+ ifTrue: [ ^ condensed compareEqualTo: aCondensed ].
+ ^ self compareEqualTo: aMetacelloVersionNumber
+!
+
+hash
+
+"Returns a numeric hash key for the receiver."
+
+| mySize interval hashValue |
+
+(mySize := self size) == 0
+ ifTrue: [ ^15243 ].
+
+"Choose an interval so that we sample at most 5 elements of the receiver"
+interval := ((mySize - 1) // 4) max: 1.
+
+hashValue := 4459.
+1 to: mySize by: interval do: [ :i | | anElement |
+ anElement := self at: i.
+ (anElement isKindOf: SequenceableCollection)
+ ifTrue: [
+ hashValue := (hashValue bitShift: -1) bitXor: anElement size.
+ ]
+ ifFalse: [
+ hashValue := (hashValue bitShift: -1) bitXor: anElement hash.
+ ].
+ ].
+
+^ hashValue abs
+!
+
+match: aVersionPattern
+ "Answer whether the version number of the receiver matches the given pattern string.
+
+ A Metacello version number is made up of version sequences delimited by the characters $. and $-.
+ The $. introduces a numeric version sequence and $- introduces an alphanumeric version sequence.
+
+ A version pattern is made up of version pattern match sequences. also delimited by the characters $.
+ and $-.. Each pattern match sequence is tested against the corresponding version sequence of the
+ receiver, using the 'standard' pattern matching rules. All sequences must answer true for a match.
+
+ The special pattern sequence '?' is a match for the corresponding version sequence and all subsequent
+ version sequences. '?' as the version pattern matches all versions. No more version pattern
+ sequences are permitted once the '?' sequence is used. If used, it is the last version pattern
+ sequence. "
+
+ | patternVersion mySize patternSize |
+ patternVersion := aVersionPattern asMetacelloVersionNumber.
+ mySize := self size.
+ patternSize := patternVersion size.
+ mySize = patternSize
+ ifFalse: [
+ mySize < patternSize ifTrue: [ ^false ].
+ (patternVersion at: patternSize) ~= '?' ifTrue: [ ^false ].
+ mySize := patternSize ].
+ 1 to: mySize do: [:i | | pattern |
+ pattern := (patternVersion at: i) asString.
+ pattern = '?'
+ ifTrue: [i = mySize ifFalse: [ ^self error: 'Invalid version match pattern: ', aVersionPattern printString ]]
+ ifFalse: [ (pattern match: (self at: i) asString) ifFalse: [ ^false ]]].
+ ^true
+"
+ '1.1.1' asMetacelloVersionNumber match: '*.*.*'. -> true
+ '1.1.1' asMetacelloVersionNumber match: '*.#.*'. -> true
+ '1.10.1' asMetacelloVersionNumber match: '*.#.*'. -> false
+ '1.1.1' asMetacelloVersionNumber match: '*.*'. -> false
+ '1.1.1' asMetacelloVersionNumber match: '*.?'. -> true
+ '1.0' asMetacelloVersionNumber match: '1.?'. -> true
+ '2.0' asMetacelloVersionNumber match: '1.?'. -> false
+ '1.1.1' asMetacelloVersionNumber match: '?'. -> true
+ '1' asMetacelloVersionNumber match: '*.?'. -> false
+ '1-alpha5.0' asMetacelloVersionNumber match: '1-alpha*.?'. -> true
+ '1-alpha15.0.1' asMetacelloVersionNumber match: '1-alpha*.?'. -> true
+ '1.1' asMetacelloVersionNumber match: '?.?'. -> ERROR: invalid version match pattern
+"
+!
+
+~> aMetacelloVersionNumber
+
+ aMetacelloVersionNumber size == 1 ifTrue: [ ^false ].
+ ^self >= aMetacelloVersionNumber and: [ self < aMetacelloVersionNumber approximateBase ]
+! !
+
+!MetacelloVersionNumber methodsFor:'converting'!
+
+asMetacelloVersionNumber
+
+ ^self
+! !
+
+!MetacelloVersionNumber methodsFor:'copying'!
+
+copyFrom: start to: stop
+ "Answer a copy of a subset of the receiver, starting from element at
+ index start until element at index stop."
+
+ | newSize new j |
+ newSize := stop - start + 1.
+ new := self species new: newSize.
+ j := 0.
+ start to: stop do: [:i |
+ new at: j + 1 put: (self at: i).
+ j := j + 1 ].
+ ^new
+! !
+
+!MetacelloVersionNumber methodsFor:'enumerating'!
+
+do: aBlock
+ "Refer to the comment in Collection|do:."
+ 1 to: self size do:
+ [:index | aBlock value: (self at: index)]
+!
+
+do: elementBlock separatedBy: separatorBlock
+ "Evaluate the elementBlock for all elements in the receiver,
+ and evaluate the separatorBlock between."
+
+ | beforeFirst |
+ beforeFirst := true.
+ self do:
+ [:each |
+ beforeFirst
+ ifTrue: [beforeFirst := false]
+ ifFalse: [separatorBlock value].
+ elementBlock value: each]
+! !
+
+!MetacelloVersionNumber methodsFor:'operations'!
+
+decrementMinorVersionNumber
+ | int |
+ self size to: 1 by: -1 do: [ :index |
+ (int := self at: index) isString
+ ifFalse: [
+ int > 0
+ ifTrue: [ self at: index put: int - 1 ].
+ ^ self ] ]
+!
+
+incrementMinorVersionNumber
+
+ | int |
+ self size to: 1 by: -1 do: [:index |
+ (int := self at: index) isString
+ ifFalse: [
+ self at: index put: int + 1.
+ ^self ]].
+! !
+
+!MetacelloVersionNumber methodsFor:'printing'!
+
+asString
+ "Answer a string that represents the receiver."
+
+ ^ self printString
+!
+
+printOn: aStream
+
+ | beforeFirst |
+ beforeFirst := true.
+ self do:
+ [:each |
+ beforeFirst
+ ifTrue: [beforeFirst := false]
+ ifFalse: [
+ each isString
+ ifTrue: [ aStream nextPut: $- ]
+ ifFalse: [ aStream nextPut: $. ] ].
+ aStream nextPutAll: each asString ]
+! !
+
+!MetacelloVersionNumber methodsFor:'private'!
+
+collapseZeros
+ "the rule must be that zeros can be collapsed as long as the series of zeros ends in a string term"
+
+ | collection newSize new j lastElementIsStringOrZero canCollapse |
+ (self size = 0 or: [ self at: 1 ]) == 0
+ ifTrue: [ ^ self ].
+ collection := OrderedCollection new.
+ lastElementIsStringOrZero := true.
+ canCollapse := true.
+ self size to: 1 by: -1 do: [ :i |
+ | element |
+ element := self at: i.
+ (canCollapse and: [ element == 0 ])
+ ifTrue: [
+ lastElementIsStringOrZero
+ ifFalse: [
+ canCollapse := false.
+ collection addFirst: element.]]
+ ifFalse: [
+ collection addFirst: element.
+ canCollapse := lastElementIsStringOrZero := element isString ] ].
+ collection size = self size
+ ifTrue: [ ^ self ].
+ newSize := collection size.
+ new := self species new: newSize.
+ j := 0.
+ collection
+ do: [ :element |
+ new at: j + 1 put: element.
+ j := j + 1 ].
+ ^ new
+!
+
+compareEqualTo: aMetacelloVersionNumber
+
+ | mySize |
+ aMetacelloVersionNumber species = self species ifFalse: [ ^false ].
+ mySize := self size.
+ mySize = aMetacelloVersionNumber size
+ ifFalse: [ ^false ].
+ 1 to: mySize do: [:i |
+ (self at: i) = (aMetacelloVersionNumber at: i) ifFalse: [ ^false ]].
+ ^true
+!
+
+compareLessThan: aMetacelloVersionNumber
+
+ | mySize aSize commonSize count more |
+ mySize := self size.
+ aSize := aMetacelloVersionNumber size.
+ commonSize := mySize min: aSize.
+ count := 0.
+ more := true.
+ [ more and: [ count < commonSize ]] whileTrue: [
+ (self at: count + 1) = (aMetacelloVersionNumber at: count + 1)
+ ifTrue: [ count := count + 1 ]
+ ifFalse: [ more := false ]].
+ count < commonSize
+ ifTrue: [
+ ^(self at: count + 1)
+ metacelloVersionComponentLessThan: (aMetacelloVersionNumber at: count + 1) ].
+ mySize < aSize
+ ifTrue: [
+ mySize = 0 ifTrue: [ ^true ].
+ "if the versions at commonSize are equal and the next version slot in aMetacelloVersionNumber
+ is a string, then it's considered that I'm > aMetacelloVersionNumber
+ (i.e., '2.9.9' is greater than '2.9.9-alpha.2')"
+ (self at: commonSize) = (aMetacelloVersionNumber at: commonSize)
+ ifFalse: [ ^true ].
+ ^(aMetacelloVersionNumber at: commonSize+1) isString not]
+ ifFalse: [
+ mySize = aSize ifTrue: [ ^false ].
+ aSize <= 0 ifTrue: [ ^false ].
+ "if the versions at commonSize are equal and the next version slot is a string,
+ then it's considered that I'm < aMetacelloVersionNumber
+ (i.e., '2.9.9-alpha.2' is less than '2.9.9')"
+ (self at: commonSize) = (aMetacelloVersionNumber at: commonSize)
+ ifFalse: [ ^false ].
+ ^(self at: commonSize+1) isString]
+! !
+
+!MetacelloVersionNumber class methodsFor:'documentation'!
+
+version_SVN
+ ^ '$Id:: $'
+! !