[Pkg] The Trunk: Collections-kfr.9.mcz
commits at source.squeak.org
commits at source.squeak.org
Mon Aug 29 13:03:09 UTC 2016
Tim Felgentreff uploaded a new version of Collections to project The Trunk:
http://source.squeak.org/trunk/Collections-kfr.9.mcz
==================== Summary ====================
Name: Collections-kfr.9
Author: kfr
Time: 5 December 2012, 4:58:43 pm
UUID: cbcd8469-2a1a-e247-848b-b3d559df006c
Ancestors: Collections-kfr.8
Fix long standing bug with printing small numbers ie 1.2245678e-16
Copied method from Squeak 4.4
==================== Snapshot ====================
SystemOrganization addCategory: #'Collections-Abstract'!
SystemOrganization addCategory: #'Collections-Arrayed'!
SystemOrganization addCategory: #'Collections-Sequenceable'!
SystemOrganization addCategory: #'Collections-SkipLists'!
SystemOrganization addCategory: #'Collections-Streams'!
SystemOrganization addCategory: #'Collections-Strings'!
SystemOrganization addCategory: #'Collections-Support'!
SystemOrganization addCategory: #'Collections-Text'!
SystemOrganization addCategory: #'Collections-Unordered'!
SystemOrganization addCategory: #'Collections-Weak'!
Magnitude subclass: #Character
instanceVariableNames: 'value'
classVariableNames: 'CharacterTable ClassificationTable LetterBits LowercaseBit UppercaseBit'
poolDictionaries: ''
category: 'Collections-Strings'!
!Character commentStamp: 'ar 4/9/2005 22:35' prior: 0!
I represent a character by storing its associated Unicode. The first 256 characters are created uniquely, so that all instances of latin1 characters ($R, for example) are identical.
The code point is based on Unicode. Since Unicode is 21-bit wide character set, we have several bits available for other information. As the Unicode Standard states, a Unicode code point doesn't carry the language information. This is going to be a problem with the languages so called CJK (Chinese, Japanese, Korean. Or often CJKV including Vietnamese). Since the characters of those languages are unified and given the same code point, it is impossible to display a bare Unicode code point in an inspector or such tools. To utilize the extra available bits, we use them for identifying the languages. Since the old implementation uses the bits to identify the character encoding, the bits are sometimes called "encoding tag" or neutrally "leading char", but the bits rigidly denotes the concept of languages.
The other languages can have the language tag if you like. This will help to break the large default font (font set) into separately loadable chunk of fonts. However, it is open to the each native speakers and writers to decide how to define the character equality, since the same Unicode code point may have different language tag thus simple #= comparison may return false.
I represent a character by storing its associated ASCII code (extended to 256 codes). My instances are created uniquely, so that all instances of a character ($R, for example) are identical.!
----- Method: Character class>>allByteCharacters (in category 'instance creation') -----
allByteCharacters
"Answer all the characters that can be encoded in a byte"
^ (0 to: 255) collect: [:v | Character value: v]
!
----- Method: Character class>>allCharacters (in category 'instance creation') -----
allCharacters
"This name is obsolete since only the characters that will fit in a byte can be queried"
^self allByteCharacters
!
----- Method: Character class>>alphabet (in category 'constants') -----
alphabet
"($a to: $z) as: String"
^ 'abcdefghijklmnopqrstuvwxyz' copy!
----- Method: Character class>>arrowDown (in category 'accessing untypeable characters') -----
arrowDown
^ self value: 31!
----- Method: Character class>>arrowLeft (in category 'accessing untypeable characters') -----
arrowLeft
^ self value: 28!
----- Method: Character class>>arrowRight (in category 'accessing untypeable characters') -----
arrowRight
^ self value: 29!
----- Method: Character class>>arrowUp (in category 'accessing untypeable characters') -----
arrowUp
^ self value: 30!
----- Method: Character class>>backspace (in category 'accessing untypeable characters') -----
backspace
"Answer the Character representing a backspace."
^self value: 8!
----- Method: Character class>>characterTable (in category 'constants') -----
characterTable
"Answer the class variable in which unique Characters are stored."
^CharacterTable!
----- Method: Character class>>cr (in category 'accessing untypeable characters') -----
cr
"Answer the Character representing a carriage return."
^self value: 13!
----- Method: Character class>>delete (in category 'accessing untypeable characters') -----
delete
^ self value: 127!
----- Method: Character class>>digitValue: (in category 'instance creation') -----
digitValue: x
"Answer the Character whose digit value is x. For example, answer $9 for
x=9, $0 for x=0, $A for x=10, $Z for x=35."
| index |
index _ x asInteger.
^CharacterTable at:
(index < 10
ifTrue: [48 + index]
ifFalse: [55 + index])
+ 1!
----- Method: Character class>>end (in category 'accessing untypeable characters') -----
end
^ self value: 4!
----- Method: Character class>>enter (in category 'accessing untypeable characters') -----
enter
"Answer the Character representing enter."
^self value: 3!
----- Method: Character class>>escape (in category 'accessing untypeable characters') -----
escape
"Answer the ASCII ESC character"
^self value: 27!
----- Method: Character class>>euro (in category 'accessing untypeable characters') -----
euro
"The Euro currency sign, that E with two dashes. The key code is a wild guess"
^ Character value: 219!
----- Method: Character class>>home (in category 'accessing untypeable characters') -----
home
^ self value: 1!
----- Method: Character class>>initialize (in category 'class initialization') -----
initialize
"Create the table of unique Characters."
" self initializeClassificationTable"!
----- Method: Character class>>initializeClassificationTable (in category 'class initialization') -----
initializeClassificationTable
"
Initialize the classification table. The classification table is a
compact encoding of upper and lower cases of characters with
- bits 0-7: The lower case value of this character.
- bits 8-15: The upper case value of this character.
- bit 16: lowercase bit (e.g., isLowercase == true)
- bit 17: uppercase bit (e.g., isUppercase == true)
"
| ch1 ch2 |
LowercaseBit := 1 bitShift: 16.
UppercaseBit := 1 bitShift: 17.
"Initialize the letter bits (e.g., isLetter == true)"
LetterBits := LowercaseBit bitOr: UppercaseBit.
ClassificationTable := Array new: 256.
"Initialize the defaults (neither lower nor upper case)"
0 to: 255 do:[:i|
ClassificationTable at: i+1 put: (i bitShift: 8) + i.
].
"Initialize character pairs (upper-lower case)"
#(
"Basic roman"
($A $a) ($B $b) ($C $c) ($D $d)
($E $e) ($F $f) ($G $g) ($H $h)
($I $i) ($J $j) ($K $k) ($L $l)
($M $m) ($N $n) ($O $o) ($P $p)
($Q $q) ($R $r) ($S $s) ($T $t)
($U $u) ($V $v) ($W $w) ($X $x)
($Y $y) ($Z $z)
"International"
($Ä $ä) ($Å $å) ($Ç $ç) ($É $é)
($Ñ $ñ) ($Ö $ö) ($Ü $ü) ($À $à)
($Ã $ã) ($Õ $õ) ($ $) ($Æ $æ)
"International - Spanish"
($Á $á) ($Í $í) ($Ó $ó) ($Ú $ú)
"International - PLEASE CHECK"
($È $è) ($Ì $ì) ($Ò $ò) ($Ù $ù)
($Ë $ë) ($Ï $ï)
($Â $â) ($Ê $ê) ($Î $î) ($Ô $ô) ($Û $û)
) do:[:pair|
ch1 := pair first asciiValue.
ch2 := pair last asciiValue.
ClassificationTable at: ch1+1 put: (ch1 bitShift: 8) + ch2 + UppercaseBit.
ClassificationTable at: ch2+1 put: (ch1 bitShift: 8) + ch2 + LowercaseBit.
].
"Initialize a few others for which we only have lower case versions."
#($ß $Ø $ø $ÿ) do:[:char|
ch1 := char asciiValue.
ClassificationTable at: ch1+1 put: (ch1 bitShift: 8) + ch1 + LowercaseBit.
].
!
----- Method: Character class>>insert (in category 'accessing untypeable characters') -----
insert
^ self value: 5!
----- Method: Character class>>leadingChar:code: (in category 'instance creation') -----
leadingChar: leadChar code: code
code >= 16r400000 ifTrue: [
self error: 'code is out of range'.
].
leadChar >= 256 ifTrue: [
self error: 'lead is out of range'.
].
^self value: (leadChar bitShift: 22) + code.!
----- Method: Character class>>lf (in category 'accessing untypeable characters') -----
lf
"Answer the Character representing a linefeed."
^self value: 10!
----- Method: Character class>>linefeed (in category 'accessing untypeable characters') -----
linefeed
"Answer the Character representing a linefeed."
^self value: 10!
----- Method: Character class>>nbsp (in category 'accessing untypeable characters') -----
nbsp
"non-breakable space."
^ Character value: 202!
----- Method: Character class>>new (in category 'instance creation') -----
new
"Creating new characters is not allowed."
self error: 'cannot create new characters'!
----- Method: Character class>>newPage (in category 'accessing untypeable characters') -----
newPage
"Answer the Character representing a form feed."
^self value: 12!
----- Method: Character class>>pageDown (in category 'accessing untypeable characters') -----
pageDown
^ self value: 12!
----- Method: Character class>>pageUp (in category 'accessing untypeable characters') -----
pageUp
^ self value: 11!
----- Method: Character class>>separators (in category 'instance creation') -----
separators
^ #(32 "space"
13 "cr"
9 "tab"
10 "line feed"
12 "form feed")
collect: [:v | Character value: v]
!
----- Method: Character class>>space (in category 'accessing untypeable characters') -----
space
"Answer the Character representing a space."
^self value: 32!
----- Method: Character class>>tab (in category 'accessing untypeable characters') -----
tab
"Answer the Character representing a tab."
^self value: 9!
----- Method: Character class>>value: (in category 'instance creation') -----
value: anInteger
"Answer the Character whose value is anInteger."
anInteger > 255 ifTrue: [^self basicNew setValue: anInteger].
^ CharacterTable at: anInteger + 1.
!
----- Method: Character>>< (in category 'comparing') -----
< aCharacter
"Answer true if the receiver's value < aCharacter's value."
^self asciiValue < aCharacter asciiValue!
----- Method: Character>>= (in category 'comparing') -----
= aCharacter
"Primitive. Answer true if the receiver and the argument are the same
object (have the same object pointer) and false otherwise. Optional. See
Object documentation whatIsAPrimitive."
^ self == aCharacter or:[
aCharacter isCharacter and: [self asciiValue = aCharacter asciiValue]]!
----- Method: Character>>> (in category 'comparing') -----
> aCharacter
"Answer true if the receiver's value > aCharacter's value."
^self asciiValue > aCharacter asciiValue!
----- Method: Character>>asCharacter (in category 'converting') -----
asCharacter
"Answer the receiver itself."
^self!
----- Method: Character>>asIRCLowercase (in category 'converting') -----
asIRCLowercase
"convert to lowercase, using IRC's rules"
self == $[ ifTrue: [ ^ ${ ].
self == $] ifTrue: [ ^ $} ].
self == $\ ifTrue: [ ^ $| ].
^self asLowercase!
----- Method: Character>>asInteger (in category 'converting') -----
asInteger
"Answer the value of the receiver."
^value!
----- Method: Character>>asLowercase (in category 'converting') -----
asLowercase
"If the receiver is uppercase, answer its matching lowercase Character."
"A tentative implementation. Eventually this should consult the Unicode table."
| v |
v _ self charCode.
(((8r101 <= v and: [v <= 8r132]) or: [16rC0 <= v and: [v <= 16rD6]]) or: [16rD8 <= v and: [v <= 16rDE]])
ifTrue: [^ Character value: value + 8r40]
ifFalse: [^ self]!
----- Method: Character>>asString (in category 'converting') -----
asString
^ String with: self!
----- Method: Character>>asSymbol (in category 'converting') -----
asSymbol
"Answer a Symbol consisting of the receiver as the only element."
^Symbol internCharacter: self!
----- Method: Character>>asText (in category 'converting') -----
asText
^ self asString asText!
----- Method: Character>>asUnicode (in category 'converting') -----
asUnicode
| table charset v |
self leadingChar = 0 ifTrue: [^ value].
charset _ EncodedCharSet charsetAt: self leadingChar.
charset isCharset ifFalse: [^ self charCode].
table _ charset ucsTable.
table isNil ifTrue: [^ 16rFFFD].
v _ table at: self charCode + 1.
v = -1 ifTrue: [^ 16rFFFD].
^ v.
!
----- Method: Character>>asUnicodeChar (in category 'converting') -----
asUnicodeChar
"@@@ FIXME: Make this use asUnicode and move it to its lonely sender @@@"
| table charset v |
self leadingChar = 0 ifTrue: [^ value].
charset _ EncodedCharSet charsetAt: self leadingChar.
charset isCharset ifFalse: [^ self].
table _ charset ucsTable.
table isNil ifTrue: [^ Character value: 16rFFFD].
v _ table at: self charCode + 1.
v = -1 ifTrue: [^ Character value: 16rFFFD].
^ Character leadingChar: charset unicodeLeadingChar code: v.!
----- Method: Character>>asUppercase (in category 'converting') -----
asUppercase
"If the receiver is lowercase, answer its matching uppercase Character."
"A tentative implementation. Eventually this should consult the Unicode table."
| v |
v _ self charCode.
(((8r141 <= v and: [v <= 8r172]) or: [16rE0 <= v and: [v <= 16rF6]]) or: [16rF8 <= v and: [v <= 16rFE]])
ifTrue: [^ Character value: value - 8r40]
ifFalse: [^ self]
!
----- Method: Character>>asciiValue (in category 'accessing') -----
asciiValue
"Answer the value of the receiver that represents its ascii encoding."
^value!
----- Method: Character>>basicSqueakToIso (in category 'converting') -----
basicSqueakToIso
| asciiValue |
value < 128 ifTrue: [^ self].
value > 255 ifTrue: [^ self].
asciiValue _ #(196 197 199 201 209 214 220 225 224 226 228 227 229 231 233 232 234 235 237 236 238 239 241 243 242 244 246 245 250 249 251 252 134 176 162 163 167 149 182 223 174 169 153 180 168 128 198 216 129 177 138 141 165 181 142 143 144 154 157 170 186 158 230 248 191 161 172 166 131 173 178 171 187 133 160 192 195 213 140 156 150 151 147 148 145 146 247 179 253 159 185 164 139 155 188 189 135 183 130 132 137 194 202 193 203 200 205 206 207 204 211 212 190 210 218 219 217 208 136 152 175 215 221 222 184 240 254 255 256 ) at: self asciiValue - 127.
^ Character value: asciiValue.
!
----- Method: Character>>canBeGlobalVarInitial (in category 'testing') -----
canBeGlobalVarInitial
^ (EncodedCharSet charsetAt: self leadingChar) canBeGlobalVarInitial: self.
!
----- Method: Character>>canBeNonGlobalVarInitial (in category 'testing') -----
canBeNonGlobalVarInitial
^ (EncodedCharSet charsetAt: self leadingChar) canBeNonGlobalVarInitial: self.
!
----- Method: Character>>charCode (in category 'accessing') -----
charCode
^ (value bitAnd: 16r3FFFFF).
!
----- Method: Character>>clone (in category 'copying') -----
clone
"Answer with the receiver, because Characters are unique."!
----- Method: Character>>comeFullyUpOnReload: (in category 'object fileIn') -----
comeFullyUpOnReload: smartRefStream
"Use existing an Character. Don't use the new copy."
^ self class value: value!
----- Method: Character>>copy (in category 'copying') -----
copy
"Answer with the receiver because Characters are unique."!
----- Method: Character>>deepCopy (in category 'copying') -----
deepCopy
"Answer with the receiver because Characters are unique."!
----- Method: Character>>digitValue (in category 'accessing') -----
digitValue
"Answer 0-9 if the receiver is $0-$9, 10-35 if it is $A-$Z, and < 0
otherwise. This is used to parse literal numbers of radix 2-36."
^ (EncodedCharSet charsetAt: self leadingChar) digitValue: self.
!
----- Method: Character>>hash (in category 'comparing') -----
hash
"Hash is reimplemented because = is implemented."
^value!
----- Method: Character>>hex (in category 'printing') -----
hex
^value hex!
----- Method: Character>>isAlphaNumeric (in category 'testing') -----
isAlphaNumeric
"Answer whether the receiver is a letter or a digit."
^self isLetter or: [self isDigit]!
----- Method: Character>>isCharacter (in category 'testing') -----
isCharacter
^ true.
!
----- Method: Character>>isDigit (in category 'testing') -----
isDigit
^ (EncodedCharSet charsetAt: self leadingChar) isDigit: self.
!
----- Method: Character>>isLetter (in category 'testing') -----
isLetter
^ (EncodedCharSet charsetAt: self leadingChar) isLetter: self.
!
----- Method: Character>>isLiteral (in category 'printing') -----
isLiteral
^true!
----- Method: Character>>isLowercase (in category 'testing') -----
isLowercase
^ (EncodedCharSet charsetAt: self leadingChar) isLowercase: self.
!
----- Method: Character>>isOctetCharacter (in category 'testing') -----
isOctetCharacter
^ value < 256.
!
----- Method: Character>>isSafeForHTTP (in category 'testing') -----
isSafeForHTTP
"whether a character is 'safe', or needs to be escaped when used, eg, in a URL"
"[GG] See http://www.faqs.org/rfcs/rfc1738.html. ~ is unsafe and has been removed"
^ self charCode < 128
and: [self isAlphaNumeric
or: ['.-_' includes: (Character value: self charCode)]]!
----- Method: Character>>isSeparator (in category 'testing') -----
isSeparator
"Answer whether the receiver is one of the separator characters--space,
cr, tab, line feed, or form feed."
value = 32 ifTrue: [^true]. "space"
value = 13 ifTrue: [^true]. "cr"
value = 9 ifTrue: [^true]. "tab"
value = 10 ifTrue: [^true]. "line feed"
value = 12 ifTrue: [^true]. "form feed"
^false!
----- Method: Character>>isSpecial (in category 'testing') -----
isSpecial
"Answer whether the receiver is one of the special characters"
^'+-/\*~<>=@,%|&?!!' includes: self!
----- Method: Character>>isTraditionalDomestic (in category 'testing') -----
isTraditionalDomestic
"Yoshiki's note about #isUnicode says:
[This method] is for the backward compatibility when we had domestic
traditional encodings for CJK languages. To support loading the
projects in traditional domestic encodings (From Nihongo4), and load
some changesets. Once we decided to get rid of classes like JISX0208
from the EncodedCharSet table, the need for isUnicode will not be
necessary.
I (Andreas) decided to change the name from isUnicode to #isTraditionalDomestic
since I found isUnicode to be horribly confusing (how could the character *not*
be Unicode after all?). But still, we should remove this method in due time."
^ ((EncodedCharSet charsetAt: self leadingChar) isKindOf: LanguageEnvironment class) not!
----- Method: Character>>isUppercase (in category 'testing') -----
isUppercase
^ (EncodedCharSet charsetAt: self leadingChar) isUppercase: self.
!
----- Method: Character>>isVowel (in category 'testing') -----
isVowel
"Answer whether the receiver is one of the vowels, AEIOU, in upper or
lower case."
^'AEIOU' includes: self asUppercase!
----- Method: Character>>isoToSqueak (in category 'converting') -----
isoToSqueak
^self "no longer needed"!
----- Method: Character>>leadingChar (in category 'accessing') -----
leadingChar
^ (value bitAnd: (16r3FC00000)) bitShift: -22.
!
----- Method: Character>>macToSqueak (in category 'converting') -----
macToSqueak
"Convert the receiver from MacRoman to Squeak encoding"
| asciiValue |
value < 128 ifTrue: [^ self].
value > 255 ifTrue: [^ self].
asciiValue _ #(196 197 199 201 209 214 220 225 224 226 228 227 229 231 233 232 234 235 237 236 238 239 241 243 242 244 246 245 250 249 251 252 134 176 162 163 167 149 182 223 174 169 153 180 168 128 198 216 129 177 138 141 165 181 142 143 144 154 157 170 186 158 230 248 191 161 172 166 131 173 178 171 187 133 160 192 195 213 140 156 150 151 147 148 145 146 247 179 255 159 185 164 139 155 188 189 135 183 130 132 137 194 202 193 203 200 205 206 207 204 211 212 190 210 218 219 217 208 136 152 175 215 221 222 184 240 253 254 ) at: self asciiValue - 127.
^ Character value: asciiValue.
!
----- Method: Character>>objectForDataStream: (in category 'object fileIn') -----
objectForDataStream: refStrm
"I am being collected for inclusion in a segment. Do not include Characters!! Let them be in outPointers."
refStrm insideASegment
ifFalse: ["Normal use" ^ self]
ifTrue: ["recording objects to go into an ImageSegment"
"remove it from references. Do not trace."
refStrm references removeKey: self ifAbsent: [].
^ nil]
!
----- Method: Character>>printOn: (in category 'printing') -----
printOn: aStream
aStream nextPut: $$.
aStream nextPut: self!
----- Method: Character>>printOnStream: (in category 'printing') -----
printOnStream: aStream
aStream print:'$', (String with:self).!
----- Method: Character>>setValue: (in category 'private') -----
setValue: newValue
value ifNotNil:[^self error:'Characters are immutable'].
value _ newValue.!
----- Method: Character>>sissSequence (in category 'converting') -----
sissSequence
"This method omits the language tags. So, shouldn't be used for WideChars casually."
| masked s low high escapeBlock |
masked _ value bitAnd: 16r1FFFFF.
masked = 7 ifTrue: [^ '\a'].
masked = 8 ifTrue: [^ '\b'].
masked = 9 ifTrue: [^ '\t'].
masked = 10 ifTrue: [^ '\n'].
masked = 11 ifTrue: [^ '\v'].
masked = 12 ifTrue: [^ '\f'].
masked = 13 ifTrue: [^ '\r'].
masked = 27 ifTrue: [^ '\e'].
masked = 34 ifTrue: [^ '\"'].
"masked = 39 ifTrue: [^ '\''']."
masked = 92 ifTrue: [^ '\\'].
(32 <= masked and: [masked < 128]) ifTrue: [
^ self asString.
].
escapeBlock _ [:marker :digits |
s _ String new: digits + 2.
s at: 1 put: $\.
s at: 2 put: marker.
digits + 2 to: 3 by: -1 do: [:i |
s at: i put: ('0123456789ABCDEF' at: (masked \\ 16) + 1).
masked _ masked bitShift: -4
].
^ s
].
(masked < 32 or: [masked > 127 and: [masked < 256]]) ifTrue: [
escapeBlock value: $x value: 2.
].
((256 <= masked) and: [masked <= 16rFFFF]) ifTrue: [
escapeBlock value: $u value: 4.
].
low _ (masked \\ 16r400) + 16rDC00.
high _ (masked // 16r400) + 16rD800.
^ (Character value: high) sissSequence, (Character value: low) sissSequence.
!
----- Method: Character>>sissUnescape (in category 'converting') -----
sissUnescape
self = $a ifTrue: [^ Character value: 7].
self = $b ifTrue: [^ Character value: 8].
self = $t ifTrue: [^ Character value: 9].
self = $n ifTrue: [^ Character value: 10].
self = $v ifTrue: [^ Character value: 11].
self = $f ifTrue: [^ Character value: 12].
self = $r ifTrue: [^ Character value: 13].
self = $e ifTrue: [^ Character value: 27].
^ self.
!
----- Method: Character>>squeakToIso (in category 'converting') -----
squeakToIso
^self "no longer needed"!
----- Method: Character>>squeakToMac (in category 'converting') -----
squeakToMac
"Convert the receiver from Squeak to MacRoman encoding."
value < 128 ifTrue: [^ self].
value > 255 ifTrue: [^ self].
^ Character value: (#(
173 176 226 196 227 201 160 224 246 228 178 220 206 179 182 183 "80-8F"
184 212 213 210 211 165 208 209 247 170 185 221 207 186 189 217 "90-9F"
202 193 162 163 219 180 195 164 172 169 187 199 194 197 168 248 "A0-AF"
161 177 198 215 171 181 166 225 252 218 188 200 222 223 240 192 "B0-BF"
203 231 229 204 128 129 174 130 233 131 230 232 237 234 235 236 "C0-CF"
245 132 241 238 239 205 133 249 175 244 242 243 134 250 251 167 "D0-DF"
136 135 137 139 138 140 190 141 143 142 144 145 147 146 148 149 "E0-EF"
253 150 152 151 153 155 154 214 191 157 156 158 159 254 255 216 "F0-FF"
) at: value - 127)
!
----- Method: Character>>storeBinaryOn: (in category 'printing') -----
storeBinaryOn: aStream
"Store the receiver on a binary (file) stream"
value < 256
ifTrue:[aStream basicNextPut: self]
ifFalse:[Stream nextInt32Put: value].!
----- Method: Character>>storeOn: (in category 'printing') -----
storeOn: aStream
"Character literals are preceded by '$'."
aStream nextPut: $$; nextPut: self!
----- Method: Character>>to: (in category 'converting') -----
to: other
"Answer with a collection in ascii order -- $a to: $z"
^ (self asciiValue to: other asciiValue) collect:
[:ascii | Character value: ascii]!
----- Method: Character>>tokenish (in category 'testing') -----
tokenish
"Answer whether the receiver is a valid token-character--letter, digit, or
colon."
^self isLetter or: [self isDigit or: [self = $:]]!
----- Method: Character>>veryDeepCopyWith: (in category 'copying') -----
veryDeepCopyWith: deepCopier
"Return self. I can't be copied."!
Magnitude subclass: #LookupKey
instanceVariableNames: 'key'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Support'!
!LookupKey commentStamp: '<historical>' prior: 0!
I represent a key for looking up entries in a data structure. Subclasses of me, such as Association, typically represent dictionary entries.!
LookupKey subclass: #Association
instanceVariableNames: 'value'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Support'!
!Association commentStamp: '<historical>' prior: 0!
I represent a pair of associated objects--a key and a value. My instances can serve as entries in a dictionary.!
----- Method: Association class>>key:value: (in category 'instance creation') -----
key: newKey value: newValue
"Answer an instance of me with the arguments as the key and value of
the association."
^ self basicNew key: newKey value: newValue.!
----- Method: Association>>= (in category 'comparing') -----
= anAssociation
^ super = anAssociation and: [value = anAssociation value]!
----- Method: Association>>byteEncode: (in category 'filter streaming') -----
byteEncode: aStream
aStream writeAssocation:self.!
----- Method: Association>>hash (in category 'comparing') -----
hash
"Hash is reimplemented because = is implemented."
^key hash bitXor: value hash.!
----- Method: Association>>isSpecialWriteBinding (in category 'testing') -----
isSpecialWriteBinding
"Return true if this variable binding is write protected, e.g., should not be accessed primitively but rather by sending #value: messages"
^false!
----- Method: Association>>isVariableBinding (in category 'testing') -----
isVariableBinding
"Return true if I represent a literal variable binding"
^true!
----- Method: Association>>key:value: (in category 'accessing') -----
key: aKey value: anObject
"Store the arguments as the variables of the receiver."
key _ aKey.
value _ anObject!
----- Method: Association>>objectForDataStream: (in category 'objects from disk') -----
objectForDataStream: refStrm
| dp |
"I am about to be written on an object file. If I am a known global, write a proxy that will hook up with the same resource in the destination system."
^ (Smalltalk associationAt: key ifAbsent: [nil]) == self
ifTrue: [dp _ DiskProxy global: #Smalltalk selector: #associationOrUndeclaredAt:
args: (Array with: key).
refStrm replace: self with: dp.
dp]
ifFalse: [self]!
----- Method: Association>>printOn: (in category 'printing') -----
printOn: aStream
super printOn: aStream.
aStream nextPutAll: '->'.
value printOn: aStream!
----- Method: Association>>propertyListOn: (in category 'printing') -----
propertyListOn: aStream
aStream write:key; print:'='; write:value.
!
----- Method: Association>>storeOn: (in category 'printing') -----
storeOn: aStream
"Store in the format (key->value)"
aStream nextPut: $(.
key storeOn: aStream.
aStream nextPutAll: '->'.
value storeOn: aStream.
aStream nextPut: $)!
----- Method: Association>>value (in category 'accessing') -----
value
"Answer the value of the receiver."
^value!
----- Method: Association>>value: (in category 'accessing') -----
value: anObject
"Store the argument, anObject, as the value of the receiver."
value _ anObject!
Association subclass: #WeakKeyAssociation
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Support'!
!WeakKeyAssociation commentStamp: '<historical>' prior: 0!
I am an association holding only weakly on my key.!
----- Method: WeakKeyAssociation>>< (in category 'comparing') -----
< aLookupKey
"Refer to the comment in Magnitude|<."
^self key < aLookupKey key!
----- Method: WeakKeyAssociation>>= (in category 'comparing') -----
= aLookupKey
self species = aLookupKey species
ifTrue: [^self key = aLookupKey key]
ifFalse: [^false]!
----- Method: WeakKeyAssociation>>hash (in category 'comparing') -----
hash
"Hash is reimplemented because = is implemented."
^self key hash!
----- Method: WeakKeyAssociation>>hashMappedBy: (in category 'comparing') -----
hashMappedBy: map
"Answer what my hash would be if oops changed according to map."
^self key hashMappedBy: map!
----- Method: WeakKeyAssociation>>identityHashMappedBy: (in category 'comparing') -----
identityHashMappedBy: map
"Answer what my hash would be if oops changed according to map."
^ self key identityHashMappedBy: map!
----- Method: WeakKeyAssociation>>key (in category 'accessing') -----
key
^key isNil
ifTrue:[nil]
ifFalse:[key at: 1]!
----- Method: WeakKeyAssociation>>key: (in category 'accessing') -----
key: aKey
key := WeakArray with: aKey!
----- Method: WeakKeyAssociation>>key:value: (in category 'accessing') -----
key: aKey value: anObject
key := WeakArray with: aKey.
value := anObject.!
----- Method: WeakKeyAssociation>>printOn: (in category 'printing') -----
printOn: aStream
self key printOn: aStream.
aStream nextPutAll: '->'.
self value printOn: aStream!
----- Method: WeakKeyAssociation>>storeOn: (in category 'printing') -----
storeOn: aStream
aStream
nextPut: $(;
nextPutAll: self class name;
nextPutAll:' key: '.
self key storeOn: aStream.
aStream nextPutAll: ' value: '.
self value storeOn: aStream.
aStream nextPut: $)!
----- Method: LookupKey class>>key: (in category 'instance creation') -----
key: aKey
"Answer an instance of me with the argument as the lookup up."
^self basicNew key: aKey!
----- Method: LookupKey>>< (in category 'comparing') -----
< aLookupKey
"Refer to the comment in Magnitude|<."
^key < aLookupKey key!
----- Method: LookupKey>>= (in category 'comparing') -----
= aLookupKey
self species = aLookupKey species
ifTrue: [^key = aLookupKey key]
ifFalse: [^false]!
----- Method: LookupKey>>beBindingOfType:announcing: (in category 'bindings') -----
beBindingOfType: aClass announcing: aBool
"Make the receiver a global binding of the given type"
| old new |
(Smalltalk associationAt: self key) == self
ifFalse:[^self error:'Not a global variable binding'].
self class == aClass ifTrue:[^self].
old _ self.
new _ aClass key: self key value: self value.
old become: new.
"NOTE: Now self == read-only (e.g., the new binding)"
^self recompileBindingsAnnouncing: aBool!
----- Method: LookupKey>>beReadOnlyBinding (in category 'bindings') -----
beReadOnlyBinding
"Make the receiver (a global read-write binding) be a read-only binding"
^self beReadOnlyBindingAnnouncing: true!
----- Method: LookupKey>>beReadOnlyBindingAnnouncing: (in category 'bindings') -----
beReadOnlyBindingAnnouncing: aBool
"Make the receiver (a global read-write binding) be a read-only binding"
^self beBindingOfType: ReadOnlyVariableBinding announcing: aBool!
----- Method: LookupKey>>beReadWriteBinding (in category 'bindings') -----
beReadWriteBinding
"Make the receiver (a global read-only binding) be a read-write binding"
^self beReadWriteBindingAnnouncing: true!
----- Method: LookupKey>>beReadWriteBindingAnnouncing: (in category 'bindings') -----
beReadWriteBindingAnnouncing: aBool
"Make the receiver (a global read-write binding) be a read-write binding"
^self beBindingOfType: Association announcing: aBool!
----- Method: LookupKey>>canAssign (in category 'accessing') -----
canAssign
^ true!
----- Method: LookupKey>>hash (in category 'comparing') -----
hash
"Hash is reimplemented because = is implemented."
^key hash!
----- Method: LookupKey>>hashMappedBy: (in category 'comparing') -----
hashMappedBy: map
"Answer what my hash would be if oops changed according to map."
^key hashMappedBy: map!
----- Method: LookupKey>>identityHashMappedBy: (in category 'comparing') -----
identityHashMappedBy: map
"Answer what my hash would be if oops changed according to map."
^ key identityHashMappedBy: map!
----- Method: LookupKey>>isVariableBinding (in category 'testing') -----
isVariableBinding
"Return true if I represent a literal variable binding"
^true!
----- Method: LookupKey>>key (in category 'accessing') -----
key
"Answer the lookup key of the receiver."
^key!
----- Method: LookupKey>>key: (in category 'accessing') -----
key: anObject
"Store the argument, anObject, as the lookup key of the receiver."
key _ anObject!
----- Method: LookupKey>>name (in category 'accessing') -----
name
^ self key isString
ifTrue: [self key]
ifFalse: [self key printString]!
----- Method: LookupKey>>printOn: (in category 'printing') -----
printOn: aStream
key printOn: aStream!
----- Method: LookupKey>>recompileBindingsAnnouncing: (in category 'bindings') -----
recompileBindingsAnnouncing: aBool
"Make the receiver (a global read-write binding) be a read-only binding"
aBool
ifTrue:
[Utilities informUserDuring:
[:bar |
(self systemNavigation allCallsOn: self) do:
[:mref |
bar value: 'Recompiling ' , mref asStringOrText.
mref actualClass recompile: mref methodSymbol]]]
ifFalse:
[(self systemNavigation allCallsOn: self)
do: [:mref | mref actualClass recompile: mref methodSymbol]]!
----- Method: LookupKey>>writeOnFilterStream: (in category 'filter streaming') -----
writeOnFilterStream: aStream
aStream write:key.!
LookupKey subclass: #ReadOnlyVariableBinding
instanceVariableNames: 'value'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Support'!
----- Method: ReadOnlyVariableBinding class>>key:value: (in category 'instance creation') -----
key: key value: aValue
^self new privateSetKey: key value: aValue!
----- Method: ReadOnlyVariableBinding>>canAssign (in category 'accessing') -----
canAssign
^ false!
----- Method: ReadOnlyVariableBinding>>isSpecialWriteBinding (in category 'testing') -----
isSpecialWriteBinding
"Return true if this variable binding is write protected, e.g., should not be accessed primitively but rather by sending #value: messages"
^true!
----- Method: ReadOnlyVariableBinding>>privateSetKey:value: (in category 'private') -----
privateSetKey: aKey value: aValue
key _ aKey.
value _ aValue!
----- Method: ReadOnlyVariableBinding>>value (in category 'accessing') -----
value
^value!
----- Method: ReadOnlyVariableBinding>>value: (in category 'accessing') -----
value: aValue
(AttemptToWriteReadOnlyGlobal signal: 'Cannot store into read-only bindings') == true ifTrue:[
value _ aValue.
].!
LookupKey weakSubclass: #WeakValueAssociation
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Support'!
!WeakValueAssociation commentStamp: '<historical>' prior: 0!
I am a lookup key (acting like an association but) holding only weakly on my value.!
----- Method: WeakValueAssociation class>>key:value: (in category 'instance creation') -----
key: anObject value: bObject
^ self new key: anObject value: bObject!
----- Method: WeakValueAssociation class>>new (in category 'as yet unclassified') -----
new
^ self new: 1!
----- Method: WeakValueAssociation>>key:value: (in category 'accessing') -----
key: aKey value: anObject
"Store the arguments as the variables of the receiver."
key _ aKey.
self value: anObject!
----- Method: WeakValueAssociation>>value (in category 'accessing') -----
value
^self at: 1!
----- Method: WeakValueAssociation>>value: (in category 'accessing') -----
value: anObject
"Store the argument, anObject, as the value of the receiver."
self at: 1 put: anObject!
Object subclass: #Collection
instanceVariableNames: ''
classVariableNames: 'MutexForPicking RandomForPicking'
poolDictionaries: ''
category: 'Collections-Abstract'!
!Collection commentStamp: '<historical>' prior: 0!
I am the abstract superclass of all classes that represent a group of elements.!
Collection subclass: #Bag
instanceVariableNames: 'contents'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!Bag commentStamp: '<historical>' prior: 0!
I represent an unordered collection of possibly duplicate elements.
I store these elements in a dictionary, tallying up occurrences of equal objects. Because I store an occurrence only once, my clients should beware that objects they store will not necessarily be retrieved such that == is true. If the client cares, a subclass of me should be created.!
----- Method: Bag class>>contentsClass (in category 'instance creation') -----
contentsClass
^Dictionary!
----- Method: Bag class>>new (in category 'instance creation') -----
new
^ self new: 4!
----- Method: Bag class>>new: (in category 'instance creation') -----
new: nElements
^ super new setContents: (self contentsClass new: nElements)!
----- Method: Bag class>>newFrom: (in category 'instance creation') -----
newFrom: aCollection
"Answer an instance of me containing the same elements as aCollection."
^ self withAll: aCollection
"Examples:
Bag newFrom: {1. 2. 3. 3}
{1. 2. 3. 3} as: Bag
"!
----- Method: Bag>>= (in category 'comparing') -----
= aBag
"Two bags are equal if
(a) they are the same 'kind' of thing.
(b) they have the same size.
(c) each element occurs the same number of times in both of them"
(aBag isKindOf: Bag) ifFalse: [^false].
self size = aBag size ifFalse: [^false].
contents associationsDo: [:assoc|
(aBag occurrencesOf: assoc key) = assoc value
ifFalse: [^false]].
^true
!
----- Method: Bag>>add: (in category 'adding') -----
add: newObject
"Include newObject as one of the receiver's elements. Answer newObject."
^ self add: newObject withOccurrences: 1!
----- Method: Bag>>add:withOccurrences: (in category 'adding') -----
add: newObject withOccurrences: anInteger
"Add newObject anInteger times to the receiver. Answer newObject."
contents at: newObject put: (contents at: newObject ifAbsent: [0]) + anInteger.
^ newObject!
----- Method: Bag>>asBag (in category 'converting') -----
asBag
^ self!
----- Method: Bag>>asSet (in category 'converting') -----
asSet
"Answer a set with the elements of the receiver."
^ contents keys!
----- Method: Bag>>at: (in category 'accessing') -----
at: index
self errorNotKeyed!
----- Method: Bag>>at:put: (in category 'accessing') -----
at: index put: anObject
self errorNotKeyed!
----- Method: Bag>>copy (in category 'copying') -----
copy
^ self shallowCopy setContents: contents copy!
----- Method: Bag>>cumulativeCounts (in category 'accessing') -----
cumulativeCounts
"Answer with a collection of cumulative percents covered by elements so far."
| s n |
s _ self size / 100.0. n _ 0.
^ self sortedCounts asArray collect:
[:a | n _ n + a key. (n / s roundTo: 0.1) -> a value]!
----- Method: Bag>>do: (in category 'enumerating') -----
do: aBlock
"Refer to the comment in Collection|do:."
contents associationsDo: [:assoc | assoc value timesRepeat: [aBlock value: assoc key]]!
----- Method: Bag>>includes: (in category 'testing') -----
includes: anObject
"Refer to the comment in Collection|includes:."
^contents includesKey: anObject!
----- Method: Bag>>occurrencesOf: (in category 'testing') -----
occurrencesOf: anObject
"Refer to the comment in Collection|occurrencesOf:."
(self includes: anObject)
ifTrue: [^contents at: anObject]
ifFalse: [^0]!
----- Method: Bag>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: exceptionBlock
"Refer to the comment in Collection|remove:ifAbsent:."
| count |
count _ contents at: oldObject ifAbsent: [^ exceptionBlock value].
count = 1
ifTrue: [contents removeKey: oldObject]
ifFalse: [contents at: oldObject put: count - 1].
^ oldObject!
----- Method: Bag>>setContents: (in category 'private') -----
setContents: aDictionary
contents _ aDictionary!
----- Method: Bag>>size (in category 'accessing') -----
size
"Answer how many elements the receiver contains."
| tally |
tally _ 0.
contents do: [:each | tally _ tally + each].
^ tally!
----- Method: Bag>>sortedCounts (in category 'accessing') -----
sortedCounts
"Answer with a collection of counts with elements, sorted by decreasing
count."
| counts |
counts _ SortedCollection sortBlock: [:x :y | x >= y].
contents associationsDo:
[:assn |
counts add: (Association key: assn value value: assn key)].
^ counts!
----- Method: Bag>>sortedElements (in category 'accessing') -----
sortedElements
"Answer with a collection of elements with counts, sorted by element."
| elements |
elements _ SortedCollection new.
contents associationsDo: [:assn | elements add: assn].
^elements!
Bag subclass: #IdentityBag
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!IdentityBag commentStamp: '<historical>' prior: 0!
Like a Bag, except that items are compared with #== instead of #= .
See the comment of IdentitySet for more information.
!
----- Method: IdentityBag class>>contentsClass (in category 'instance creation') -----
contentsClass
^IdentityDictionary!
Collection subclass: #CharacterSet
instanceVariableNames: 'map'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Support'!
!CharacterSet commentStamp: '<historical>' prior: 0!
A set of characters. Lookups for inclusion are very fast.!
----- Method: CharacterSet class>>allCharacters (in category 'instance creation') -----
allCharacters
"return a set containing all characters"
| set |
set _ self empty.
0 to: 255 do: [ :ascii | set add: (Character value: ascii) ].
^set!
----- Method: CharacterSet class>>empty (in category 'instance creation') -----
empty
"return an empty set of characters"
^self new!
----- Method: CharacterSet class>>newFrom: (in category 'instance creation') -----
newFrom: aCollection
| newCollection |
newCollection _ self new.
newCollection addAll: aCollection.
^newCollection!
----- Method: CharacterSet class>>nonSeparators (in category 'instance creation') -----
nonSeparators
"return a set containing everything but the whitespace characters"
^self separators complement!
----- Method: CharacterSet class>>separators (in category 'instance creation') -----
separators
"return a set containing just the whitespace characters"
| set |
set _ self empty.
set addAll: Character separators.
^set!
----- Method: CharacterSet>>= (in category 'comparison') -----
= anObject
^self species == anObject species and: [
self byteArrayMap = anObject byteArrayMap ]!
----- Method: CharacterSet>>add: (in category 'collection ops') -----
add: aCharacter
map at: aCharacter asciiValue+1 put: 1.!
----- Method: CharacterSet>>byteArrayMap (in category 'private') -----
byteArrayMap
"return a ByteArray mapping each ascii value to a 1 if that ascii value is in the set, and a 0 if it isn't. Intended for use by primitives only"
^map!
----- Method: CharacterSet>>complement (in category 'conversion') -----
complement
"return a character set containing precisely the characters the receiver does not"
| set |
set _ CharacterSet allCharacters.
self do: [ :c | set remove: c ].
^set!
----- Method: CharacterSet>>do: (in category 'collection ops') -----
do: aBlock
"evaluate aBlock with each character in the set"
Character allByteCharacters do: [ :c |
(self includes: c) ifTrue: [ aBlock value: c ] ]
!
----- Method: CharacterSet>>hash (in category 'comparison') -----
hash
^self byteArrayMap hash!
----- Method: CharacterSet>>includes: (in category 'collection ops') -----
includes: aCharacter
^(map at: aCharacter asciiValue + 1) > 0!
----- Method: CharacterSet>>initialize (in category 'private') -----
initialize
map _ ByteArray new: 256 withAll: 0.!
----- Method: CharacterSet>>remove: (in category 'collection ops') -----
remove: aCharacter
map at: aCharacter asciiValue + 1 put: 0!
----- Method: CharacterSet>>species (in category 'comparison') -----
species
^CharacterSet!
----- Method: Collection class>>initialize (in category 'class initialization') -----
initialize
"Set up a Random number generator to be used by atRandom when the
user does not feel like creating his own Random generator."
RandomForPicking _ Random new.
MutexForPicking _ Semaphore forMutualExclusion.
Smalltalk addToStartUpList: Collection.
!
----- Method: Collection class>>mutexForPicking (in category 'private') -----
mutexForPicking
^ MutexForPicking!
----- Method: Collection class>>ofSize: (in category 'instance creation') -----
ofSize: n
"Create a new collection of size n with nil as its elements.
This method exists because OrderedCollection new: n creates an
empty collection, not one of size n."
^ self new: n!
----- Method: Collection class>>randomForPicking (in category 'private') -----
randomForPicking
^ RandomForPicking!
----- Method: Collection class>>startUp (in category 'private') -----
startUp
RandomForPicking seed: (Time totalSeconds) hash asFloat.
!
----- Method: Collection class>>with: (in category 'instance creation') -----
with: anObject
"Answer an instance of me containing anObject."
^ self new
add: anObject;
yourself!
----- Method: Collection class>>with:with: (in category 'instance creation') -----
with: firstObject with: secondObject
"Answer an instance of me containing the two arguments as elements."
^ self new
add: firstObject;
add: secondObject;
yourself!
----- Method: Collection class>>with:with:with: (in category 'instance creation') -----
with: firstObject with: secondObject with: thirdObject
"Answer an instance of me containing the three arguments as elements."
^ self new
add: firstObject;
add: secondObject;
add: thirdObject;
yourself!
----- Method: Collection class>>with:with:with:with: (in category 'instance creation') -----
with: firstObject with: secondObject with: thirdObject with: fourthObject
"Answer an instance of me, containing the four arguments as the elements."
^ self new
add: firstObject;
add: secondObject;
add: thirdObject;
add: fourthObject;
yourself!
----- Method: Collection class>>with:with:with:with:with: (in category 'instance creation') -----
with: firstObject with: secondObject with: thirdObject with: fourthObject with: fifthObject
"Answer an instance of me, containing the five arguments as the elements."
^ self new
add: firstObject;
add: secondObject;
add: thirdObject;
add: fourthObject;
add: fifthObject;
yourself!
----- Method: Collection class>>with:with:with:with:with:with: (in category 'instance creation') -----
with: firstObject with: secondObject with: thirdObject with: fourthObject with: fifthObject with: sixthObject
"Answer an instance of me, containing the six arguments as the elements."
^ self new
add: firstObject;
add: secondObject;
add: thirdObject;
add: fourthObject;
add: fifthObject;
add: sixthObject;
yourself!
----- Method: Collection class>>withAll: (in category 'instance creation') -----
withAll: aCollection
"Create a new collection containing all the elements from aCollection."
^ (self new: aCollection size)
addAll: aCollection;
yourself!
----- Method: Collection>>* (in category 'arithmetic') -----
* arg
^ arg adaptToCollection: self andSend: #*!
----- Method: Collection>>+ (in category 'arithmetic') -----
+ arg
^ arg adaptToCollection: self andSend: #+!
----- Method: Collection>>, (in category 'copying') -----
, aCollection
^self copy addAll: aCollection; yourself!
----- Method: Collection>>- (in category 'arithmetic') -----
- arg
^ arg adaptToCollection: self andSend: #-!
----- Method: Collection>>/ (in category 'arithmetic') -----
/ arg
^ arg adaptToCollection: self andSend: #/!
----- Method: Collection>>// (in category 'arithmetic') -----
// arg
^ arg adaptToCollection: self andSend: #//!
----- Method: Collection>>\\ (in category 'arithmetic') -----
\\ arg
^ arg adaptToCollection: self andSend: #\\!
----- Method: Collection>>abs (in category 'math functions') -----
abs
"Absolute value of all elements in the collection"
^ self collect: [:a | a abs]!
----- Method: Collection>>adaptToCollection:andSend: (in category 'adapting') -----
adaptToCollection: rcvr andSend: selector
"If I am involved in arithmetic with another Collection, return a Collection of
the results of each element combined with the scalar in that expression."
rcvr isSequenceable & self isSequenceable ifFalse:
[self error: 'Only sequenceable collections may be combined arithmetically'].
^ rcvr with: self collect:
[:rcvrElement :myElement | rcvrElement perform: selector with: myElement]!
----- Method: Collection>>adaptToComplex:andSend: (in category 'adapting') -----
adaptToComplex: rcvr andSend: selector
"If I am involved in arithmetic with a scalar, return a Collection of
the results of each element combined with the scalar in that expression."
^ self collect: [:element | rcvr perform: selector with: element]!
----- Method: Collection>>adaptToNumber:andSend: (in category 'adapting') -----
adaptToNumber: rcvr andSend: selector
"If I am involved in arithmetic with a scalar, return a Collection of
the results of each element combined with the scalar in that expression."
^ self collect: [:element | rcvr perform: selector with: element]!
----- Method: Collection>>adaptToPoint:andSend: (in category 'adapting') -----
adaptToPoint: rcvr andSend: selector
"If I am involved in arithmetic with a scalar, return a Collection of
the results of each element combined with the scalar in that expression."
^ self collect: [:element | rcvr perform: selector with: element]!
----- Method: Collection>>adaptToString:andSend: (in category 'adapting') -----
adaptToString: rcvr andSend: selector
"If I am involved in arithmetic with a String, convert it to a Number."
^ rcvr asNumber perform: selector with: self!
----- Method: Collection>>add: (in category 'adding') -----
add: newObject
"Include newObject as one of the receiver's elements. Answer newObject.
ArrayedCollections cannot respond to this message."
self subclassResponsibility!
----- Method: Collection>>add:withOccurrences: (in category 'adding') -----
add: newObject withOccurrences: anInteger
"Add newObject anInteger times to the receiver. Answer newObject."
anInteger timesRepeat: [self add: newObject].
^ newObject!
----- Method: Collection>>addAll: (in category 'adding') -----
addAll: aCollection
"Include all the elements of aCollection as the receiver's elements. Answer
aCollection. Actually, any object responding to #do: can be used as argument."
aCollection do: [:each | self add: each].
^ aCollection!
----- Method: Collection>>addIfNotPresent: (in category 'adding') -----
addIfNotPresent: anObject
"Include anObject as one of the receiver's elements, but only if there
is no such element already. Anwser anObject."
(self includes: anObject) ifFalse: [self add: anObject].
^ anObject!
----- Method: Collection>>allSatisfy: (in category 'enumerating') -----
allSatisfy: aBlock
"Evaluate aBlock with the elements of the receiver.
If aBlock returns false for any element return false.
Otherwise return true."
self do: [:each | (aBlock value: each) ifFalse: [^ false]].
^ true!
----- Method: Collection>>anyOne (in category 'accessing') -----
anyOne
"Answer a representative sample of the receiver. This method can
be helpful when needing to preinfer the nature of the contents of
semi-homogeneous collections."
self emptyCheck.
self do: [:each | ^ each]!
----- Method: Collection>>anySatisfy: (in category 'enumerating') -----
anySatisfy: aBlock
"Evaluate aBlock with the elements of the receiver.
If aBlock returns true for any element return true.
Otherwise return false."
self do: [:each | (aBlock value: each) ifTrue: [^ true]].
^ false!
----- Method: Collection>>arcCos (in category 'math functions') -----
arcCos
^self collect: [:each | each arcCos]!
----- Method: Collection>>arcSin (in category 'math functions') -----
arcSin
^self collect: [:each | each arcSin]!
----- Method: Collection>>arcTan (in category 'math functions') -----
arcTan
^self collect: [:each | each arcTan]!
----- Method: Collection>>asArray (in category 'converting') -----
asArray
"Answer an Array whose elements are the elements of the receiver.
Implementation note: Cannot use ''Array withAll: self'' as that only
works for SequenceableCollections which support the replacement
primitive."
| array index |
array _ Array new: self size.
index _ 0.
self do: [:each | array at: (index _ index + 1) put: each].
^ array!
----- Method: Collection>>asBag (in category 'converting') -----
asBag
"Answer a Bag whose elements are the elements of the receiver."
^ Bag withAll: self!
----- Method: Collection>>asByteArray (in category 'converting') -----
asByteArray
"Answer a ByteArray whose elements are the elements of the receiver.
Implementation note: Cannot use ''ByteArray withAll: self'' as that only
works for SequenceableCollections which support the replacement
primitive."
| array index |
array _ ByteArray new: self size.
index _ 0.
self do: [:each | array at: (index _ index + 1) put: each].
^ array!
----- Method: Collection>>asCharacterSet (in category 'converting') -----
asCharacterSet
"Answer a CharacterSet whose elements are the unique elements of the receiver.
The reciever should only contain characters."
^ CharacterSet newFrom: self!
----- Method: Collection>>asIdentitySet (in category 'converting') -----
asIdentitySet
^(IdentitySet new: self size) addAll: self; yourself!
----- Method: Collection>>asIdentitySkipList (in category 'converting') -----
asIdentitySkipList
"Answer a IdentitySkipList whose elements are the elements of the
receiver. The sort order is the default less than or equal."
^ self as: IdentitySkipList!
----- Method: Collection>>asOrderedCollection (in category 'converting') -----
asOrderedCollection
"Answer an OrderedCollection whose elements are the elements of the
receiver. The order in which elements are added depends on the order
in which the receiver enumerates its elements. In the case of unordered
collections, the ordering is not necessarily the same for multiple
requests for the conversion."
^ self as: OrderedCollection!
----- Method: Collection>>asSet (in category 'converting') -----
asSet
"Answer a Set whose elements are the unique elements of the receiver."
^ Set withAll: self!
----- Method: Collection>>asSkipList (in category 'converting') -----
asSkipList
"Answer a SkipList whose elements are the elements of the
receiver. The sort order is the default less than or equal."
^ self as: SkipList!
----- Method: Collection>>asSkipList: (in category 'converting') -----
asSkipList: aSortBlock
"Answer a SkipList whose elements are the elements of the
receiver. The sort order is defined by the argument, aSortBlock."
| skipList |
skipList _ SortedCollection new: self size.
skipList sortBlock: aSortBlock.
skipList addAll: self.
^ skipList!
----- Method: Collection>>asSortedArray (in category 'converting') -----
asSortedArray
"Return a copy of the receiver in sorted order, as an Array. 6/10/96 sw"
^ self asSortedCollection asArray!
----- Method: Collection>>asSortedCollection (in category 'converting') -----
asSortedCollection
"Answer a SortedCollection whose elements are the elements of the
receiver. The sort order is the default less than or equal."
^ self as: SortedCollection!
----- Method: Collection>>asSortedCollection: (in category 'converting') -----
asSortedCollection: aSortBlock
"Answer a SortedCollection whose elements are the elements of the
receiver. The sort order is defined by the argument, aSortBlock."
| aSortedCollection |
aSortedCollection _ SortedCollection new: self size.
aSortedCollection sortBlock: aSortBlock.
aSortedCollection addAll: self.
^ aSortedCollection!
----- Method: Collection>>associationsDo: (in category 'enumerating') -----
associationsDo: aBlock
"Evaluate aBlock for each of the receiver's elements (key/value
associations). If any non-association is within, the error is not caught now,
but later, when a key or value message is sent to it."
self do: aBlock!
----- Method: Collection>>atRandom (in category 'accessing') -----
atRandom
"Answer a random element of the receiver. Uses a shared random
number generator owned by class Collection. If you use this a lot,
define your own instance of Random and use #atRandom:. Causes
an error if self has no elements."
^ self class mutexForPicking critical: [
self atRandom: self class randomForPicking ]
"Examples:
#('one' 'or' 'the' 'other') atRandom
(1 to: 10) atRandom
'Just pick one of these letters at random' atRandom
#(3 7 4 9 21) asSet atRandom (just to show it also works for Sets)
"!
----- Method: Collection>>average (in category 'math functions') -----
average
^ self sum / self size!
----- Method: Collection>>capacity (in category 'accessing') -----
capacity
"Answer the current capacity of the receiver."
^ self size!
----- Method: Collection>>ceiling (in category 'math functions') -----
ceiling
^ self collect: [:a | a ceiling]!
----- Method: Collection>>collect: (in category 'enumerating') -----
collect: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Collect the resulting values into a collection like the receiver. Answer
the new collection."
| newCollection |
newCollection _ self species new.
self do: [:each | newCollection add: (aBlock value: each)].
^ newCollection!
----- Method: Collection>>collect:thenDo: (in category 'enumerating') -----
collect: collectBlock thenDo: doBlock
"Utility method to improve readability."
^ (self collect: collectBlock) do: doBlock!
----- Method: Collection>>collect:thenSelect: (in category 'enumerating') -----
collect: collectBlock thenSelect: selectBlock
"Utility method to improve readability."
^ (self collect: collectBlock) select: selectBlock!
----- Method: Collection>>contents (in category 'filter streaming') -----
contents
^ self!
----- Method: Collection>>copyWith: (in category 'copying') -----
copyWith: newElement
"Answer a new collection with newElement added (as last
element if sequenceable)."
^ self copy
add: newElement;
yourself!
----- Method: Collection>>copyWithDependent: (in category 'copying') -----
copyWithDependent: newElement
"Answer a new collection with newElement added (as last
element if sequenceable)."
^self copyWith: newElement!
----- Method: Collection>>copyWithout: (in category 'copying') -----
copyWithout: oldElement
"Answer a copy of the receiver that does not contain any
elements equal to oldElement."
^ self reject: [:each | each = oldElement]
"Examples:
'fred the bear' copyWithout: $e
#(2 3 4 5 5 6) copyWithout: 5
"!
----- Method: Collection>>copyWithoutAll: (in category 'copying') -----
copyWithoutAll: aCollection
"Answer a copy of the receiver that does not contain any elements
equal to those in aCollection."
^ self reject: [:each | aCollection includes: each]!
----- Method: Collection>>cos (in category 'math functions') -----
cos
^self collect: [:each | each cos]!
----- Method: Collection>>count: (in category 'enumerating') -----
count: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Answer the number of elements that answered true."
| sum |
sum _ 0.
self do: [:each | (aBlock value: each) ifTrue: [sum _ sum + 1]].
^ sum!
----- Method: Collection>>degreeCos (in category 'math functions') -----
degreeCos
^self collect: [:each | each degreeCos]!
----- Method: Collection>>degreeSin (in category 'math functions') -----
degreeSin
^self collect: [:each | each degreeSin]!
----- Method: Collection>>detect: (in category 'enumerating') -----
detect: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Answer the first element for which aBlock evaluates to true."
^ self detect: aBlock ifNone: [self errorNotFound: aBlock]!
----- Method: Collection>>detect:ifNone: (in category 'enumerating') -----
detect: aBlock ifNone: exceptionBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Answer the first element for which aBlock evaluates to true. If none
evaluate to true, then evaluate the argument, exceptionBlock."
self do: [:each | (aBlock value: each) ifTrue: [^ each]].
^ exceptionBlock value!
----- Method: Collection>>detectMax: (in category 'enumerating') -----
detectMax: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Answer the element for which aBlock evaluates to the highest magnitude.
If collection empty, return nil. This method might also be called elect:."
| maxElement maxValue val |
self do: [:each |
maxValue == nil
ifFalse: [
(val _ aBlock value: each) > maxValue ifTrue: [
maxElement _ each.
maxValue _ val]]
ifTrue: ["first element"
maxElement _ each.
maxValue _ aBlock value: each].
"Note that there is no way to get the first element that works
for all kinds of Collections. Must test every one."].
^ maxElement!
----- Method: Collection>>detectMin: (in category 'enumerating') -----
detectMin: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Answer the element for which aBlock evaluates to the lowest number.
If collection empty, return nil."
| minElement minValue val |
self do: [:each |
minValue == nil
ifFalse: [
(val _ aBlock value: each) < minValue ifTrue: [
minElement _ each.
minValue _ val]]
ifTrue: ["first element"
minElement _ each.
minValue _ aBlock value: each].
"Note that there is no way to get the first element that works
for all kinds of Collections. Must test every one."].
^ minElement!
----- Method: Collection>>detectSum: (in category 'enumerating') -----
detectSum: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Return the sum of the answers."
| sum |
sum _ 0.
self do: [:each |
sum _ (aBlock value: each) + sum].
^ sum!
----- Method: Collection>>difference: (in category 'enumerating') -----
difference: aCollection
"Answer the set theoretic difference of two collections."
^ self reject: [:each | aCollection includes: each]!
----- Method: Collection>>do: (in category 'enumerating') -----
do: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument."
self subclassResponsibility!
----- Method: Collection>>do:separatedBy: (in category 'enumerating') -----
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]!
----- Method: Collection>>do:without: (in category 'enumerating') -----
do: aBlock without: anItem
"Enumerate all elements in the receiver.
Execute aBlock for those elements that are not equal to the given item"
^ self do: [:each | anItem = each ifFalse: [aBlock value: each]]!
----- Method: Collection>>emptyCheck (in category 'private') -----
emptyCheck
self isEmpty ifTrue: [self errorEmptyCollection]!
----- Method: Collection>>errorEmptyCollection (in category 'private') -----
errorEmptyCollection
self error: 'this collection is empty'!
----- Method: Collection>>errorNoMatch (in category 'private') -----
errorNoMatch
self error: 'collection sizes do not match'!
----- Method: Collection>>errorNotFound: (in category 'private') -----
errorNotFound: anObject
"Actually, this should raise a special Exception not just an error."
self error: 'Object is not in the collection.'!
----- Method: Collection>>errorNotKeyed (in category 'private') -----
errorNotKeyed
self error: ('Instances of {1} do not respond to keyed accessing messages.' translated format: {self class name})
!
----- Method: Collection>>exp (in category 'math functions') -----
exp
^self collect: [:each | each exp]!
----- Method: Collection>>explorerContents (in category 'enumerating') -----
explorerContents
^self explorerContentsWithIndexCollect: [:value :index |
ObjectExplorerWrapper
with: value
name: index printString
model: self]!
----- Method: Collection>>explorerContentsWithIndexCollect: (in category 'enumerating') -----
explorerContentsWithIndexCollect: twoArgBlock
^ self asOrderedCollection withIndexCollect: twoArgBlock
!
----- Method: Collection>>flattenOnStream: (in category 'filter streaming') -----
flattenOnStream: aStream
^ aStream writeCollection: self!
----- Method: Collection>>floor (in category 'math functions') -----
floor
^ self collect: [:a | a floor]!
----- Method: Collection>>groupBy:having: (in category 'enumerating') -----
groupBy: keyBlock having: selectBlock
"Like in SQL operation - Split the recievers contents into collections of
elements for which keyBlock returns the same results, and return those
collections allowed by selectBlock. keyBlock should return an Integer."
| result key |
result _ PluggableDictionary integerDictionary.
self do:
[:e |
key _ keyBlock value: e.
(result includesKey: key)
ifFalse: [result at: key put: OrderedCollection new].
(result at: key)
add: e].
^ result _ result select: selectBlock!
----- Method: Collection>>hash (in category 'comparing') -----
hash
"Answer an integer hash value for the receiver such that,
-- the hash value of an unchanged object is constant over time, and
-- two equal objects have equal hash values"
| hash |
hash _ self species hash.
self size <= 10 ifTrue:
[self do: [:elem | hash _ hash bitXor: elem hash]].
^hash bitXor: self size hash!
----- Method: Collection>>identityIncludes: (in category 'testing') -----
identityIncludes: anObject
"Answer whether anObject is one of the receiver's elements."
self do: [:each | anObject == each ifTrue: [^true]].
^false!
----- Method: Collection>>ifEmpty: (in category 'testing') -----
ifEmpty: aBlock
"Evaluate the block if I'm empty"
^ self isEmpty ifTrue: aBlock!
----- Method: Collection>>ifEmpty:ifNotEmpty: (in category 'testing') -----
ifEmpty: emptyBlock ifNotEmpty: notEmptyBlock
"Evaluate emptyBlock if I'm empty, notEmptyBlock otherwise"
" If the notEmptyBlock has an argument, eval with the receiver as its argument"
^ self isEmpty ifTrue: emptyBlock ifFalse: [notEmptyBlock valueWithPossibleArgument: self]!
----- Method: Collection>>ifEmpty:ifNotEmptyDo: (in category 'testing') -----
ifEmpty: emptyBlock ifNotEmptyDo: notEmptyBlock
"Evaluate emptyBlock if I'm empty, notEmptyBlock otherwise"
"Evaluate the notEmptyBlock with the receiver as its argument"
^ self isEmpty ifTrue: emptyBlock ifFalse: [notEmptyBlock value: self]!
----- Method: Collection>>ifNotEmpty: (in category 'testing') -----
ifNotEmpty: aBlock
"Evaluate the given block unless the receiver is empty.
If the block has an argument, eval with the receiver as its argument,
but it might be better to use ifNotEmptyDo: to make the code easier to
understand"
^self isEmpty ifFalse: [aBlock valueWithPossibleArgument: self].
!
----- Method: Collection>>ifNotEmpty:ifEmpty: (in category 'testing') -----
ifNotEmpty: notEmptyBlock ifEmpty: emptyBlock
"Evaluate emptyBlock if I'm empty, notEmptyBlock otherwise
If the notEmptyBlock has an argument, eval with the receiver as its argument"
^ self isEmpty ifFalse: [notEmptyBlock valueWithPossibleArgument: self] ifTrue: emptyBlock!
----- Method: Collection>>ifNotEmptyDo: (in category 'testing') -----
ifNotEmptyDo: aBlock
"Evaluate the given block with the receiver as its argument."
^self isEmpty ifFalse: [aBlock value: self].
!
----- Method: Collection>>ifNotEmptyDo:ifEmpty: (in category 'testing') -----
ifNotEmptyDo: notEmptyBlock ifEmpty: emptyBlock
"Evaluate emptyBlock if I'm empty, notEmptyBlock otherwise
Evaluate the notEmptyBlock with the receiver as its argument"
^ self isEmpty ifFalse: [notEmptyBlock value: self] ifTrue: emptyBlock!
----- Method: Collection>>includes: (in category 'testing') -----
includes: anObject
"Answer whether anObject is one of the receiver's elements."
^ self anySatisfy: [:each | each = anObject]!
----- Method: Collection>>includesAllOf: (in category 'testing') -----
includesAllOf: aCollection
"Answer whether all the elements of aCollection are in the receiver."
aCollection do: [:elem | (self includes: elem) ifFalse: [^ false]].
^ true!
----- Method: Collection>>includesAnyOf: (in category 'testing') -----
includesAnyOf: aCollection
"Answer whether any element of aCollection is one of the receiver's elements."
aCollection do: [:elem | (self includes: elem) ifTrue: [^ true]].
^ false!
----- Method: Collection>>includesSubstringAnywhere: (in category 'testing') -----
includesSubstringAnywhere: testString
"Answer whether the receiver includes, anywhere in its nested structure, a string that has testString as a substring"
self do:
[:element |
(element isString)
ifTrue:
[(element includesSubString: testString) ifTrue: [^ true]].
(element isCollection)
ifTrue:
[(element includesSubstringAnywhere: testString) ifTrue: [^ true]]].
^ false
"#(first (second third) ((allSentMessages ('Elvis' includes:)))) includesSubstringAnywhere: 'lvi'"!
----- Method: Collection>>inject:into: (in category 'enumerating') -----
inject: thisValue into: binaryBlock
"Accumulate a running value associated with evaluating the argument,
binaryBlock, with the current value of the argument, thisValue, and the
receiver as block arguments. For instance, to sum the numeric elements
of a collection, aCollection inject: 0 into: [:subTotal :next | subTotal +
next]."
| nextValue |
nextValue _ thisValue.
self do: [:each | nextValue _ binaryBlock value: nextValue value: each].
^nextValue!
----- Method: Collection>>intersection: (in category 'enumerating') -----
intersection: aCollection
"Answer the set theoretic intersection of two collections."
^ self select: [:each | aCollection includes: each]!
----- Method: Collection>>isCollection (in category 'testing') -----
isCollection
"Return true if the receiver is some sort of Collection and responds to basic collection messages such as #size and #do:"
^true!
----- Method: Collection>>isEmpty (in category 'testing') -----
isEmpty
"Answer whether the receiver contains any elements."
^self size = 0!
----- Method: Collection>>isEmptyOrNil (in category 'testing') -----
isEmptyOrNil
"Answer whether the receiver contains any elements, or is nil. Useful in numerous situations where one wishes the same reaction to an empty collection or to nil"
^ self isEmpty!
----- Method: Collection>>isSequenceable (in category 'testing') -----
isSequenceable
^ false!
----- Method: Collection>>isZero (in category 'testing') -----
isZero
"Answer whether the receiver is zero"
^ false!
----- Method: Collection>>ln (in category 'math functions') -----
ln
^self collect: [:each | each ln]!
----- Method: Collection>>log (in category 'math functions') -----
log
^ self collect: [:each | each log]!
----- Method: Collection>>max (in category 'math functions') -----
max
^ self inject: self anyOne into: [:max :each | max max: each]!
----- Method: Collection>>median (in category 'math functions') -----
median
^ self asSortedCollection median!
----- Method: Collection>>min (in category 'math functions') -----
min
^ self inject: self anyOne into: [:min :each | min min: each]!
----- Method: Collection>>negated (in category 'math functions') -----
negated
"Negated value of all elements in the collection"
^ self collect: [:a | a negated]!
----- Method: Collection>>noneSatisfy: (in category 'enumerating') -----
noneSatisfy: aBlock
"Evaluate aBlock with the elements of the receiver.
If aBlock returns false for all elements return true.
Otherwise return false"
self do: [:item | (aBlock value: item) ifTrue: [^ false]].
^ true!
----- Method: Collection>>notEmpty (in category 'testing') -----
notEmpty
"Answer whether the receiver contains any elements."
^ self isEmpty not!
----- Method: Collection>>occurrencesOf: (in category 'testing') -----
occurrencesOf: anObject
"Answer how many of the receiver's elements are equal to anObject."
| tally |
tally _ 0.
self do: [:each | anObject = each ifTrue: [tally _ tally + 1]].
^tally!
----- Method: Collection>>printElementsOn: (in category 'printing') -----
printElementsOn: aStream
aStream nextPut: $(.
self do: [:element | aStream print: element; space].
self isEmpty ifFalse: [aStream skip: -1].
aStream nextPut: $)!
----- Method: Collection>>printNameOn: (in category 'printing') -----
printNameOn: aStream
super printOn: aStream!
----- Method: Collection>>printOn: (in category 'printing') -----
printOn: aStream
"Append a sequence of characters that identify the receiver to aStream."
self printNameOn: aStream.
self printElementsOn: aStream!
----- Method: Collection>>raisedTo: (in category 'arithmetic') -----
raisedTo: arg
^ arg adaptToCollection: self andSend: #raisedTo:!
----- Method: Collection>>range (in category 'math functions') -----
range
^ self max - self min!
----- Method: Collection>>reciprocal (in category 'math functions') -----
reciprocal
"Return the reciever full of reciprocated elements"
^ self collect: [:a | a reciprocal]!
----- Method: Collection>>reject: (in category 'enumerating') -----
reject: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Collect into a new collection like the receiver only those elements for
which aBlock evaluates to false. Answer the new collection."
^self select: [:element | (aBlock value: element) == false]!
----- Method: Collection>>reject:thenDo: (in category 'enumerating') -----
reject: rejectBlock thenDo: doBlock
"Utility method to improve readability."
^ (self reject: rejectBlock) do: doBlock!
----- Method: Collection>>remove: (in category 'removing') -----
remove: oldObject
"Remove oldObject from the receiver's elements. Answer oldObject
unless no element is equal to oldObject, in which case, raise an error.
ArrayedCollections cannot respond to this message."
^ self remove: oldObject ifAbsent: [self errorNotFound: oldObject]!
----- Method: Collection>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: anExceptionBlock
"Remove oldObject from the receiver's elements. If several of the
elements are equal to oldObject, only one is removed. If no element is
equal to oldObject, answer the result of evaluating anExceptionBlock.
Otherwise, answer the argument, oldObject. ArrayedCollections cannot
respond to this message."
self subclassResponsibility!
----- Method: Collection>>removeAll: (in category 'removing') -----
removeAll: aCollection
"Remove each element of aCollection from the receiver. If successful for
each, answer aCollection. Otherwise create an error notification.
ArrayedCollections cannot respond to this message."
aCollection do: [:each | self remove: each].
^ aCollection!
----- Method: Collection>>removeAllFoundIn: (in category 'removing') -----
removeAllFoundIn: aCollection
"Remove each element of aCollection which is present in the receiver
from the receiver. Answer aCollection. No error is raised if an element
isn't found. ArrayedCollections cannot respond to this message."
aCollection do: [:each | self remove: each ifAbsent: []].
^ aCollection!
----- Method: Collection>>removeAllSuchThat: (in category 'removing') -----
removeAllSuchThat: aBlock
"Evaluate aBlock for each element and remove all that elements from
the receiver for that aBlock evaluates to true. Use a copy to enumerate
collections whose order changes when an element is removed (i.e. Sets)."
self copy do: [:each | (aBlock value: each) ifTrue: [self remove: each]]!
----- Method: Collection>>roundTo: (in category 'math functions') -----
roundTo: quantum
^self collect: [ :ea | ea roundTo: quantum ]!
----- Method: Collection>>rounded (in category 'math functions') -----
rounded
^ self collect: [:a | a rounded]!
----- Method: Collection>>select: (in category 'enumerating') -----
select: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Collect into a new collection like the receiver, only those elements for
which aBlock evaluates to true. Answer the new collection."
| newCollection |
newCollection _ self species new.
self do: [:each | (aBlock value: each) ifTrue: [newCollection add: each]].
^newCollection!
----- Method: Collection>>select:thenCollect: (in category 'enumerating') -----
select: selectBlock thenCollect: collectBlock
"Utility method to improve readability."
^ (self select: selectBlock) collect: collectBlock!
----- Method: Collection>>select:thenDo: (in category 'enumerating') -----
select: selectBlock thenDo: doBlock
"Utility method to improve readability."
^ (self select: selectBlock) do: doBlock!
----- Method: Collection>>sign (in category 'math functions') -----
sign
^self collect: [:each | each sign]!
----- Method: Collection>>sin (in category 'math functions') -----
sin
^self collect: [:each | each sin]!
----- Method: Collection>>size (in category 'accessing') -----
size
"Answer how many elements the receiver contains."
| tally |
tally _ 0.
self do: [:each | tally _ tally + 1].
^ tally!
----- Method: Collection>>sqrt (in category 'math functions') -----
sqrt
^ self collect: [:each | each sqrt]!
----- Method: Collection>>squared (in category 'math functions') -----
squared
^ self collect: [:each | each * each]!
----- Method: Collection>>storeOn: (in category 'printing') -----
storeOn: aStream
"Refer to the comment in Object|storeOn:."
| noneYet |
aStream nextPutAll: '(('.
aStream nextPutAll: self class name.
aStream nextPutAll: ' new)'.
noneYet _ true.
self do:
[:each |
noneYet
ifTrue: [noneYet _ false]
ifFalse: [aStream nextPut: $;].
aStream nextPutAll: ' add: '.
aStream store: each].
noneYet ifFalse: [aStream nextPutAll: '; yourself'].
aStream nextPut: $)!
----- Method: Collection>>sum (in category 'math functions') -----
sum
"This is implemented using a variant of the normal inject:into: pattern.
The reason for this is that it is not known whether we're in the normal
number line, i.e. whether 0 is a good initial value for the sum.
Consider a collection of measurement objects, 0 would be the unitless
value and would not be appropriate to add with the unit-ed objects."
| sum sample |
sample _ self anyOne.
sum _ self inject: sample into: [:accum :each | accum + each].
^ sum - sample!
----- Method: Collection>>tan (in category 'math functions') -----
tan
^self collect: [:each | each tan]!
----- Method: Collection>>toBraceStack: (in category 'private') -----
toBraceStack: itsSize
"Push receiver's elements onto the stack of thisContext sender. Error if receiver does
not have itsSize elements or if receiver is unordered.
Do not call directly: this is called by {a. b} _ ... constructs."
self size ~= itsSize ifTrue:
[self error: 'Trying to store ', self size printString,
' values into ', itsSize printString, ' variables.'].
thisContext sender push: itsSize fromIndexable: self!
----- Method: Collection>>truncated (in category 'math functions') -----
truncated
^ self collect: [:a | a truncated]!
----- Method: Collection>>union: (in category 'enumerating') -----
union: aCollection
"Answer the set theoretic union of two collections."
^ self asSet addAll: aCollection; yourself!
----- Method: Collection>>write: (in category 'filter streaming') -----
write: anObject
^ self add: anObject!
Collection subclass: #Matrix
instanceVariableNames: 'nrows ncols contents'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!Matrix commentStamp: '<historical>' prior: 0!
I represent a two-dimensional array, rather like Array2D.
There are three main differences between me and Array2D:
(1) Array2D inherits from ArrayedCollection, but isn't one. A lot of things that should work
do not work in consequence of this.
(2) Array2D uses "at: column at: row" index order, which means that nothing you write using
it is likely to work either. I use the almost universal "at: row at: column" order, so it is
much easier to adapt code from other languages without going doolally.
(3) Array2D lets you specify the class of the underlying collection, I don't.
Structure:
nrows : a non-negative integer saying how many rows there are.
ncols : a non-negative integer saying how many columns there are.
contents : an Array holding the elements in row-major order. That is, for a 2x3 array
the contents are (11 12 13 21 22 23). Array2D uses column major order.
You can specify the class of 'contents' when you create a new Array2D,
but Matrix always gives you an Array.
There is a reason for this. In strongly typed languages like Haskell and Clean,
'unboxed arrays' save you both space AND time. But in Squeak, while
WordArray and FloatArray and so on do save space, it costs time to use them.
A LOT of time. I've measured aFloatArray sum running nearly twice as slow as
anArray sum. The reason is that whenever you fetch an element from an Array,
that's all that happens, but when you fetch an element from aFloatArray, a whole
new Float gets allocated to hold the value. This takes time and churns memory.
So the paradox is that if you want fast numerical stuff, DON'T use unboxed arrays!!
Another reason for always insisting on an Array is that letting it be something
else would make things like #, and #,, rather more complicated. Always using Array
is the simplest thing that could possibly work, and it works rather well.
I was trying to patch Array2D to make more things work, but just couldn't get my head
around the subscript order. That's why I made Matrix.
Element-wise matrix arithmetic works; you can freely mix matrices and numbers but
don't try to mix matrices and arrays (yet).
Matrix multiplication, using the symbol +* (derived from APL's +.x), works between
(Matrix or Array) +* (Matrix or Array). Don't try to use a number as an argument of +*.
Matrix * Number and Number * Matrix work fine, so you don't need +* with numbers.
Still to come: oodles of stuff. Gaussian elimination maybe, other stuff probably not.
!
----- Method: Matrix class>>column: (in category 'instance creation') -----
column: aCollection
"Should this be called #fromColumn:?"
^self rows: aCollection size columns: 1 contents: aCollection asArray shallowCopy!
----- Method: Matrix class>>diagonal: (in category 'instance creation') -----
diagonal: aCollection
|r i|
r _ self zeros: aCollection size.
i _ 0.
aCollection do: [:each | i _ i+1. r at: i at: i put: each].
^r!
----- Method: Matrix class>>identity: (in category 'instance creation') -----
identity: n
|r|
r _ self zeros: n.
1 to: n do: [:i | r at: i at: i put: 1].
^r!
----- Method: Matrix class>>new: (in category 'instance creation') -----
new: dim
"Answer a dim*dim matrix. Is this an abuse of #new:? The argument is NOT a size."
^self rows: dim columns: dim!
----- Method: Matrix class>>new:element: (in category 'instance creation') -----
new: dim element: element
"Answer a dim*dim matrix with all elements set to element.
Is this an abuse of #new:? The argument is NOT a size."
^self rows: dim columns: dim element: element!
----- Method: Matrix class>>new:tabulate: (in category 'instance creation') -----
new: dim tabulate: aBlock
"Answer a dim*dim matrix where it at: i at: j is aBlock value: i value: j."
^self rows: dim columns: dim tabulate: aBlock!
----- Method: Matrix class>>ones: (in category 'instance creation') -----
ones: n
^self new: n element: 1
!
----- Method: Matrix class>>row: (in category 'instance creation') -----
row: aCollection
"Should this be called #fromRow:?"
^self rows: 1 columns: aCollection size contents: aCollection asArray shallowCopy!
----- Method: Matrix class>>rows:columns: (in category 'instance creation') -----
rows: rows columns: columns
^self rows: rows columns: columns contents: (Array new: rows*columns)!
----- Method: Matrix class>>rows:columns:contents: (in category 'private') -----
rows: rows columns: columns contents: contents
^self new rows: rows columns: columns contents: contents!
----- Method: Matrix class>>rows:columns:element: (in category 'instance creation') -----
rows: rows columns: columns element: element
^self rows: rows columns: columns
contents: ((Array new: rows*columns) atAllPut: element; yourself)!
----- Method: Matrix class>>rows:columns:tabulate: (in category 'instance creation') -----
rows: rows columns: columns tabulate: aBlock
"Answer a new Matrix of the given dimensions where
result at: i at: j is aBlock value: i value: j"
|a i|
a _ Array new: rows*columns.
i _ 0.
1 to: rows do: [:row |
1 to: columns do: [:column |
a at: (i _ i+1) put: (aBlock value: row value: column)]].
^self rows: rows columns: columns contents: a
!
----- Method: Matrix class>>zeros: (in category 'instance creation') -----
zeros: n
^self new: n element: 0!
----- Method: Matrix>>+* (in category 'arithmetic') -----
+* aCollection
"Premultiply aCollection by self. aCollection should be an Array or Matrix.
The name of this method is APL's +.x squished into Smalltalk syntax."
^aCollection preMultiplyByMatrix: self
!
----- Method: Matrix>>, (in category 'copying') -----
, aMatrix
"Answer a new matrix having the same number of rows as the receiver and aMatrix,
its columns being the columns of the receiver followed by the columns of aMatrix."
|newCont newCols anArray oldCols a b c|
self assert: [nrows = aMatrix rowCount].
newCont _ Array new: self size + aMatrix size.
anArray _ aMatrix privateContents.
oldCols _ aMatrix columnCount.
newCols _ ncols + oldCols.
a _ b _ c _ 1.
1 to: nrows do: [:r |
newCont replaceFrom: a to: a+ncols-1 with: contents startingAt: b.
newCont replaceFrom: a+ncols to: a+newCols-1 with: anArray startingAt: c.
a _ a + newCols.
b _ b + ncols.
c _ c + oldCols].
^self class rows: nrows columns: newCols contents: newCont
!
----- Method: Matrix>>,, (in category 'copying') -----
,, aMatrix
"Answer a new matrix having the same number of columns as the receiver and aMatrix,
its rows being the rows of the receiver followed by the rows of aMatrix."
self assert: [ncols = aMatrix columnCount].
^self class rows: nrows + aMatrix rowCount columns: ncols
contents: contents , aMatrix privateContents
!
----- Method: Matrix>>= (in category 'comparing') -----
= aMatrix
^aMatrix class == self class and: [
aMatrix rowCount = nrows and: [
aMatrix columnCount = ncols and: [
aMatrix privateContents = contents]]]!
----- Method: Matrix>>add: (in category 'adding') -----
add: newObject
self shouldNotImplement!
----- Method: Matrix>>anyOne (in category 'accessing') -----
anyOne
^contents anyOne!
----- Method: Matrix>>asArray (in category 'converting') -----
asArray
^contents shallowCopy!
----- Method: Matrix>>asBag (in category 'converting') -----
asBag
^contents asBag!
----- Method: Matrix>>asByteArray (in category 'converting') -----
asByteArray
^contents asByteArray!
----- Method: Matrix>>asCharacterSet (in category 'converting') -----
asCharacterSet
^contents asCharacterSet!
----- Method: Matrix>>asFloatArray (in category 'converting') -----
asFloatArray
^contents asFloatArray!
----- Method: Matrix>>asIdentitySet (in category 'converting') -----
asIdentitySet
^contents asIdentitySet!
----- Method: Matrix>>asIntegerArray (in category 'converting') -----
asIntegerArray
^contents asIntegerArray!
----- Method: Matrix>>asOrderedCollection (in category 'converting') -----
asOrderedCollection
^contents asOrderedCollection!
----- Method: Matrix>>asSet (in category 'converting') -----
asSet
^contents asSet!
----- Method: Matrix>>asSortedArray (in category 'converting') -----
asSortedArray
^contents asSortedArray!
----- Method: Matrix>>asSortedCollection (in category 'converting') -----
asSortedCollection
^contents asSortedCollection!
----- Method: Matrix>>asSortedCollection: (in category 'converting') -----
asSortedCollection: aBlock
^contents asSortedCollection: aBlock!
----- Method: Matrix>>asWordArray (in category 'converting') -----
asWordArray
^contents asWordArray!
----- Method: Matrix>>at:at: (in category 'accessing') -----
at: row at: column
^contents at: (self indexForRow: row andColumn: column)!
----- Method: Matrix>>at:at:ifInvalid: (in category 'accessing') -----
at: r at: c ifInvalid: v
"If r,c is a valid index for this matrix, answer the corresponding element.
Otherwise, answer v."
(r between: 1 and: nrows) ifFalse: [^v].
(c between: 1 and: ncols) ifFalse: [^v].
^contents at: (r-1)*ncols + c
!
----- Method: Matrix>>at:at:incrementBy: (in category 'accessing') -----
at: row at: column incrementBy: value
"Array2D>>at:at:add: was the origin of this method, but in Smalltalk add:
generally suggests adding an element to a collection, not doing a sum.
This method, and SequenceableCollection>>at:incrementBy: that supports
it, have been renamed to reveal their intention more clearly."
^contents at: (self indexForRow: row andColumn: column) incrementBy: value!
----- Method: Matrix>>at:at:put: (in category 'accessing') -----
at: row at: column put: value
^contents at: (self indexForRow: row andColumn: column) put: value!
----- Method: Matrix>>atAllPut: (in category 'accessing') -----
atAllPut: value
contents atAllPut: value!
----- Method: Matrix>>atColumn: (in category 'accessing rows/columns') -----
atColumn: column
|p|
p _ (self indexForRow: 1 andColumn: column)-ncols.
^(1 to: nrows) collect: [:row | contents at: (p _ p+ncols)]
!
----- Method: Matrix>>atColumn:put: (in category 'accessing rows/columns') -----
atColumn: column put: aCollection
|p|
aCollection size = nrows ifFalse: [self error: 'wrong column size'].
p _ (self indexForRow: 1 andColumn: column)-ncols.
aCollection do: [:each | contents at: (p _ p+ncols) put: each].
^aCollection
!
----- Method: Matrix>>atRandom (in category 'accessing') -----
atRandom
^contents atRandom
!
----- Method: Matrix>>atRandom: (in category 'accessing') -----
atRandom: aGenerator
^contents atRandom: aGenerator!
----- Method: Matrix>>atRow: (in category 'accessing rows/columns') -----
atRow: row
(row between: 1 and: nrows)
ifFalse: [self error: '1st subscript out of range'].
^contents copyFrom: (row-1)*ncols+1 to: row*ncols!
----- Method: Matrix>>atRow:put: (in category 'accessing rows/columns') -----
atRow: row put: aCollection
|p|
aCollection size = ncols ifFalse: [self error: 'wrong row size'].
p _ (self indexForRow: row andColumn: 1)-1.
aCollection do: [:each | contents at: (p _ p+1) put: each].
^aCollection!
----- Method: Matrix>>atRows:columns: (in category 'accessing submatrices') -----
atRows: rs columns: cs
"Answer a Matrix obtained by slicing the receiver.
rs and cs should be sequenceable collections of positive integers."
^self class rows: rs size columns: cs size tabulate: [:r :c |
self at: (rs at: r) at: (cs at: c)]!
----- Method: Matrix>>atRows:to:columns:to: (in category 'accessing submatrices') -----
atRows: r1 to: r2 columns: c1 to: c2
"Answer a submatrix [r1..r2][c1..c2] of the receiver."
|rd cd|
rd _ r1 - 1.
cd _ c1 - 1.
^self class rows: r2-rd columns: c2-cd tabulate: [:r :c| self at: r+rd at: c+cd]
!
----- Method: Matrix>>atRows:to:columns:to:ifInvalid: (in category 'accessing submatrices') -----
atRows: r1 to: r2 columns: c1 to: c2 ifInvalid: element
"Answer a submatrix [r1..r2][c1..c2] of the receiver.
Portions of the result outside the bounds of the original matrix
are filled in with element."
|rd cd|
rd _ r1 - 1.
cd _ c1 - 1.
^self class rows: r2-rd columns: c2-cd tabulate: [:r :c| self at: r+rd at: c+cd ifInvalid: element]
!
----- Method: Matrix>>atRows:to:columns:to:put: (in category 'accessing submatrices') -----
atRows: r1 to: r2 columns: c1 to: c2 put: aMatrix
"Set the [r1..r2][c1..c2] submatrix of the receiver
from the [1..r2-r1+1][1..c2-c1+1] submatrix of aMatrix.
As long as aMatrix responds to at:at: and accepts arguments in the range shown,
we don't care if it is bigger or even if it is a Matrix at all."
|rd cd|
rd _ r1 - 1.
cd _ c1 - 1.
r1 to: r2 do: [:r |
c1 to: c2 do: [:c |
self at: r at: c put: (aMatrix at: r-rd at: c-cd)]].
^aMatrix
!
----- Method: Matrix>>collect: (in category 'enumerating') -----
collect: aBlock
"Answer a new matrix with transformed elements; transformations should be independent."
^self class rows: nrows columns: ncols contents: (contents collect: aBlock)!
----- Method: Matrix>>columnCount (in category 'accessing') -----
columnCount
^ncols!
----- Method: Matrix>>copy (in category 'copying') -----
copy
^self class rows: nrows columns: ncols contents: contents copy!
----- Method: Matrix>>diagonal (in category 'accessing rows/columns') -----
diagonal
"Answer (1 to: (nrows min: ncols)) collect: [:i | self at: i at: i]"
|i|
i _ ncols negated.
^(1 to: (nrows min: ncols)) collect: [:j | contents at: (i _ i + ncols + 1)]!
----- Method: Matrix>>difference: (in category 'enumerating') -----
difference: aCollection
"Union is in because the result is always a Set.
Difference and intersection are out because the result is like the receiver,
and with irregular seleection that cannot be."
self shouldNotImplement!
----- Method: Matrix>>do: (in category 'enumerating') -----
do: aBlock
"Pass elements to aBlock one at a time in row-major order."
contents do: aBlock!
----- Method: Matrix>>hash (in category 'comparing') -----
hash
"I'm really not sure what would be a good hash function here.
The essential thing is that it must be compatible with #=, and
this satisfies that requirement."
^contents hash!
----- Method: Matrix>>identityIncludes: (in category 'testing') -----
identityIncludes: anObject
^contents identityIncludes: anObject!
----- Method: Matrix>>identityIndexOf: (in category 'accessing') -----
identityIndexOf: anElement
^self identityIndexOf: anElement ifAbsent: [0 at 0]
!
----- Method: Matrix>>identityIndexOf:ifAbsent: (in category 'accessing') -----
identityIndexOf: anElement ifAbsent: anExceptionBlock
^self rowAndColumnForIndex:
(contents identityIndexOf: anElement ifAbsent: [^anExceptionBlock value])
!
----- Method: Matrix>>includes: (in category 'testing') -----
includes: anObject
^contents includes: anObject!
----- Method: Matrix>>includesAllOf: (in category 'testing') -----
includesAllOf: aCollection
^contents includesAllOf: aCollection!
----- Method: Matrix>>includesAnyOf: (in category 'testing') -----
includesAnyOf: aCollection
^contents includesAnyOf: aCollection!
----- Method: Matrix>>indexForRow:andColumn: (in category 'private') -----
indexForRow: row andColumn: column
(row between: 1 and: nrows)
ifFalse: [self error: '1st subscript out of range'].
(column between: 1 and: ncols)
ifFalse: [self error: '2nd subscript out of range'].
^(row-1) * ncols + column!
----- Method: Matrix>>indexOf: (in category 'accessing') -----
indexOf: anElement
"If there are integers r, c such that (self at: r at: c) = anElement,
answer some such r at c, otherwise answer 0 at 0. This kind of perverse
result is provided by analogy with SequenceableCollection>>indexOf:.
The order in which the receiver are searched is UNSPECIFIED except
that it is the same as the order used by #indexOf:ifAbsent: and #readStream."
^self indexOf: anElement ifAbsent: [0 at 0]
!
----- Method: Matrix>>indexOf:ifAbsent: (in category 'accessing') -----
indexOf: anElement ifAbsent: anExceptionBlock
"If there are integers r, c such that (self at: r at: c) = anElement,
answer some such r at c, otherwise answer the result of anExceptionBlock."
^self rowAndColumnForIndex:
(contents indexOf: anElement ifAbsent: [^anExceptionBlock value])
!
----- Method: Matrix>>indicesCollect: (in category 'enumerating') -----
indicesCollect: aBlock
|r i|
r _ Array new: nrows * ncols.
i _ 0.
1 to: nrows do: [:row |
1 to: ncols do: [:column |
r at: (i _ i+1) put: (aBlock value: row value: column)]].
^self class rows: nrows columns: ncols contents: r!
----- Method: Matrix>>indicesDo: (in category 'enumerating') -----
indicesDo: aBlock
1 to: nrows do: [:row |
1 to: ncols do: [:column |
aBlock value: row value: column]].!
----- Method: Matrix>>indicesInject:into: (in category 'enumerating') -----
indicesInject: start into: aBlock
|current|
current _ start.
1 to: nrows do: [:row |
1 to: ncols do: [:column |
current _ aBlock value: current value: row value: column]].
^current!
----- Method: Matrix>>intersection: (in category 'enumerating') -----
intersection: aCollection
"Union is in because the result is always a Set.
Difference and intersection are out because the result is like the receiver,
and with irregular seleection that cannot be."
self shouldNotImplement!
----- Method: Matrix>>isSequenceable (in category 'testing') -----
isSequenceable
"LIE so that arithmetic on matrices will work.
What matters for arithmetic is not that there should be random indexing
but that the structure should be stable and independent of the values of
the elements. #isSequenceable is simply the wrong question to ask."
^true!
----- Method: Matrix>>occurrencesOf: (in category 'testing') -----
occurrencesOf: anObject
^contents occurrencesOf: anObject!
----- Method: Matrix>>preMultiplyByArray: (in category 'arithmetic') -----
preMultiplyByArray: a
"Answer a +* self where a is an Array."
nrows = 1 ifFalse: [self error: 'dimensions do not conform'].
^Matrix rows: a size columns: ncols tabulate: [:row :col |
(a at: row) * (contents at: col)]
!
----- Method: Matrix>>preMultiplyByMatrix: (in category 'arithmetic') -----
preMultiplyByMatrix: m
"Answer m +* self where m is a Matrix."
|s|
nrows = m columnCount ifFalse: [self error: 'dimensions do not conform'].
^Matrix rows: m rowCount columns: ncols tabulate: [:row :col |
s _ 0.
1 to: nrows do: [:k | s _ (m at: row at: k) * (self at: k at: col) + s].
s]!
----- Method: Matrix>>privateContents (in category 'private') -----
privateContents
"Only used in #, #,, and #= so far.
It used to be called #contents, but that clashes with Collection>>contents."
^contents!
----- Method: Matrix>>readStream (in category 'converting') -----
readStream
"Answer a ReadStream that returns all the elements of the receiver
in some UNSPECIFIED order."
^ReadStream on: contents!
----- Method: Matrix>>reject: (in category 'enumerating') -----
reject: aBlock
self shouldNotImplement!
----- Method: Matrix>>remove:ifAbsent: (in category 'removing') -----
remove: anObject ifAbsent: anExceptionBlock
self shouldNotImplement!
----- Method: Matrix>>replaceAll:with: (in category 'accessing') -----
replaceAll: oldObject with: newObject
contents replaceAll: oldObject with: newObject!
----- Method: Matrix>>rowAndColumnForIndex: (in category 'private') -----
rowAndColumnForIndex: index
|t|
t _ index - 1.
^(t // ncols + 1)@(t \\ ncols + 1)!
----- Method: Matrix>>rowCount (in category 'accessing') -----
rowCount
^nrows!
----- Method: Matrix>>rows:columns:contents: (in category 'private') -----
rows: rows columns: columns contents: anArray
self assert: [rows isInteger and: [rows >= 0]].
self assert: [columns isInteger and: [columns >= 0]].
self assert: [rows * columns = anArray size].
nrows _ rows.
ncols _ columns.
contents _ anArray.
^self!
----- Method: Matrix>>select: (in category 'enumerating') -----
select: aBlock
self shouldNotImplement!
----- Method: Matrix>>shallowCopy (in category 'copying') -----
shallowCopy
^self class rows: nrows columns: ncols contents: contents shallowCopy!
----- Method: Matrix>>shuffled (in category 'copying') -----
shuffled
^self class rows: nrows columns: ncols contents: (contents shuffled)!
----- Method: Matrix>>shuffledBy: (in category 'copying') -----
shuffledBy: aRandom
^self class rows: nrows columns: ncols contents: (contents shuffledBy: aRandom)!
----- Method: Matrix>>size (in category 'accessing') -----
size
^contents size!
----- Method: Matrix>>storeOn: (in category 'printing') -----
storeOn: aStream
aStream nextPut: $(; nextPutAll: self class name;
nextPutAll: ' rows: '; store: nrows;
nextPutAll: ' columns: '; store: ncols;
nextPutAll: ' contents: '; store: contents;
nextPut: $)!
----- Method: Matrix>>swap:at:with:at: (in category 'accessing') -----
swap: r1 at: c1 with: r2 at: c2
contents swap: (self indexForRow: r1 andColumn: c1)
with: (self indexForRow: r2 andColumn: c2)!
----- Method: Matrix>>swapColumn:withColumn: (in category 'accessing rows/columns') -----
swapColumn: anIndex withColumn: anotherIndex
|a b|
a _ self indexForRow: 1 andColumn: anIndex.
b _ self indexForRow: 1 andColumn: anotherIndex.
nrows timesRepeat: [
contents swap: a with: b.
a _ a + ncols.
b _ b + ncols].
!
----- Method: Matrix>>swapRow:withRow: (in category 'accessing rows/columns') -----
swapRow: anIndex withRow: anotherIndex
|a b|
a _ self indexForRow: anIndex andColumn: 1.
b _ self indexForRow: anotherIndex andColumn: 1.
ncols timesRepeat: [
contents swap: a with: b.
a _ a + 1.
b _ b + 1].
!
----- Method: Matrix>>transposed (in category 'accessing rows/columns') -----
transposed
self assert: [nrows = ncols].
^self indicesCollect: [:row :column | self at: column at: row]!
----- Method: Matrix>>with:collect: (in category 'enumerating') -----
with: aCollection collect: aBlock
"aCollection must support #at:at: and be at least as large as the receiver."
^self withIndicesCollect: [:each :row :column |
aBlock value: each value: (aCollection at: row at: column)]
!
----- Method: Matrix>>with:do: (in category 'enumerating') -----
with: aCollection do: aBlock
"aCollection must support #at:at: and be at least as large as the receiver."
self withIndicesDo: [:each :row :column |
aBlock value: each value: (aCollection at: row at: column)].
!
----- Method: Matrix>>with:inject:into: (in category 'enumerating') -----
with: aCollection inject: startingValue into: aBlock
"aCollection must support #at:at: and be at least as large as the receiver."
^self withIndicesInject: startingValue into: [:value :each :row :column |
aBlock value: value value: each value: (aCollection at: row at: column)]!
----- Method: Matrix>>withIndicesCollect: (in category 'enumerating') -----
withIndicesCollect: aBlock
|i r|
i _ 0.
r _ contents shallowCopy.
1 to: nrows do: [:row |
1 to: ncols do: [:column |
i _ i+1.
r at: i put: (aBlock value: (r at: i) value: row value: column)]].
^self class rows: nrows columns: ncols contents: r
!
----- Method: Matrix>>withIndicesDo: (in category 'enumerating') -----
withIndicesDo: aBlock
|i|
i _ 0.
1 to: nrows do: [:row |
1 to: ncols do: [:column |
aBlock value: (contents at: (i _ i+1)) value: row value: column]].
!
----- Method: Matrix>>withIndicesInject:into: (in category 'enumerating') -----
withIndicesInject: start into: aBlock
|i current|
i _ 0.
current _ start.
1 to: nrows do: [:row |
1 to: ncols do: [:column |
current _ aBlock value: current value: (contents at: (i _ i+1))
value: row value: column]].
^current!
Collection subclass: #SequenceableCollection
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Abstract'!
!SequenceableCollection commentStamp: '<historical>' prior: 0!
I am an abstract superclass for collections that have a well-defined order associated with their elements. Thus each element is externally-named by integers referred to as indices.!
SequenceableCollection subclass: #ArrayedCollection
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Abstract'!
!ArrayedCollection commentStamp: '<historical>' prior: 0!
I am an abstract collection of elements with a fixed range of integers (from 1 to n>=0) as external keys.!
ArrayedCollection variableSubclass: #Array
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Arrayed'!
!Array commentStamp: '<historical>' prior: 0!
I present an ArrayedCollection whose elements are objects.!
----- Method: Array class>>braceStream: (in category 'brace support') -----
braceStream: nElements
"This method is used in compilation of brace constructs.
It MUST NOT be deleted or altered."
^ WriteStream basicNew braceArray: (self new: nElements)
!
----- Method: Array class>>braceWith: (in category 'brace support') -----
braceWith: a
"This method is used in compilation of brace constructs.
It MUST NOT be deleted or altered."
| array |
array _ self new: 1.
array at: 1 put: a.
^ array!
----- Method: Array class>>braceWith:with: (in category 'brace support') -----
braceWith: a with: b
"This method is used in compilation of brace constructs.
It MUST NOT be deleted or altered."
| array |
array _ self new: 2.
array at: 1 put: a.
array at: 2 put: b.
^ array!
----- Method: Array class>>braceWith:with:with: (in category 'brace support') -----
braceWith: a with: b with: c
"This method is used in compilation of brace constructs.
It MUST NOT be deleted or altered."
| array |
array _ self new: 3.
array at: 1 put: a.
array at: 2 put: b.
array at: 3 put: c.
^ array!
----- Method: Array class>>braceWith:with:with:with: (in category 'brace support') -----
braceWith: a with: b with: c with: d
"This method is used in compilation of brace constructs.
It MUST NOT be deleted or altered."
| array |
array _ self new: 4.
array at: 1 put: a.
array at: 2 put: b.
array at: 3 put: c.
array at: 4 put: d.
^ array!
----- Method: Array class>>braceWithNone (in category 'brace support') -----
braceWithNone
"This method is used in compilation of brace constructs.
It MUST NOT be deleted or altered."
^ self new: 0!
----- Method: Array class>>ccg:emitLoadFor:from:on: (in category 'plugin generation') -----
ccg: cg emitLoadFor: aString from: anInteger on: aStream
cg emitLoad: aString asIntPtrFrom: anInteger on: aStream!
----- Method: Array class>>ccg:prolog:expr:index: (in category 'plugin generation') -----
ccg: cg prolog: aBlock expr: aString index: anInteger
^cg
ccgLoad: aBlock
expr: aString
asIntPtrFrom: anInteger
andThen: (cg ccgValBlock: 'isIndexable')!
----- Method: Array class>>ccgDeclareCForVar: (in category 'plugin generation') -----
ccgDeclareCForVar: aSymbolOrString
^'int *', aSymbolOrString!
----- Method: Array class>>new: (in category 'instance creation') -----
new: sizeRequested
"Answer an instance of this class with the number of indexable
variables specified by the argument, sizeRequested.
This is a shortcut (direct call of primitive, no #initialize, for performance"
<primitive: 71> "This method runs primitively if successful"
^ self basicNew: sizeRequested "Exceptional conditions will be handled in basicNew:"
!
----- Method: Array>>+* (in category 'arithmetic') -----
+* aCollection
"Premultiply aCollection by self. aCollection should be an Array or Matrix.
The name of this method is APL's +.x squished into Smalltalk syntax."
^aCollection preMultiplyByArray: self
!
----- Method: Array>>asArray (in category 'converting') -----
asArray
"Answer with the receiver itself."
^ self!
----- Method: Array>>atWrap: (in category 'accessing') -----
atWrap: index
"Optimized to go through the primitive if possible"
<primitive: 60>
^ self at: index - 1 \\ self size + 1!
----- Method: Array>>atWrap:put: (in category 'accessing') -----
atWrap: index put: anObject
"Optimized to go through the primitive if possible"
<primitive: 61>
^ self at: index - 1 \\ self size + 1 put: anObject!
----- Method: Array>>byteEncode: (in category 'filter streaming') -----
byteEncode:aStream
aStream writeArray:self.
!
----- Method: Array>>copy (in category 'copying') -----
copy
^ self clone.
!
----- Method: Array>>copyWithDependent: (in category 'copying') -----
copyWithDependent: newElement
self size = 0 ifTrue:[^DependentsArray with: newElement].
^self copyWith: newElement!
----- Method: Array>>elementsExchangeIdentityWith: (in category 'converting') -----
elementsExchangeIdentityWith: otherArray
"This primitive performs a bulk mutation, causing all pointers to the elements of this array to be replaced by pointers to the corresponding elements of otherArray. At the same time, all pointers to the elements of otherArray are replaced by pointers to the corresponding elements of this array. The identityHashes remain with the pointers rather than with the objects so that objects in hashed structures should still be properly indexed after the mutation."
<primitive: 128>
otherArray class == Array ifFalse: [^ self error: 'arg must be array'].
self size = otherArray size ifFalse: [^ self error: 'arrays must be same size'].
(self anySatisfy: [:obj | obj class == SmallInteger]) ifTrue: [^ self error: 'can''t become SmallIntegers'].
(otherArray anySatisfy: [:obj | obj class == SmallInteger]) ifTrue: [^ self error: 'can''t become SmallIntegers'].
self with: otherArray do:[:a :b| a == b ifTrue:[^self error:'can''t become yourself']].
"Must have failed because not enough space in forwarding table (see ObjectMemory-prepareForwardingTableForBecoming:with:twoWay:). Do GC and try again only once"
(Smalltalk bytesLeft: true) = Smalltalk primitiveGarbageCollect
ifTrue: [^ self primitiveFailed].
^ self elementsExchangeIdentityWith: otherArray!
----- Method: Array>>elementsForwardIdentityTo: (in category 'converting') -----
elementsForwardIdentityTo: otherArray
"This primitive performs a bulk mutation, causing all pointers to the elements of this array to be replaced by pointers to the corresponding elements of otherArray. The identityHashes remain with the pointers rather than with the objects so that the objects in this array should still be properly indexed in any existing hashed structures after the mutation."
<primitive: 72>
self primitiveFailed!
----- Method: Array>>elementsForwardIdentityTo:copyHash: (in category 'converting') -----
elementsForwardIdentityTo: otherArray copyHash: copyHash
"This primitive performs a bulk mutation, causing all pointers to the elements of this array to be replaced by pointers to the corresponding elements of otherArray. The identityHashes remain with the pointers rather than with the objects so that the objects in this array should still be properly indexed in any existing hashed structures after the mutation."
<primitive: 249>
self primitiveFailed!
----- Method: Array>>evalStrings (in category 'converting') -----
evalStrings
"Allows you to construct literal arrays.
#(true false nil '5 at 6' 'Set new' '''text string''') evalStrings
gives an array with true, false, nil, a Point, a Set, and a String
instead of just a bunch of Symbols"
| it |
^ self collect: [:each |
it _ each.
each == #true ifTrue: [it _ true].
each == #false ifTrue: [it _ false].
each == #nil ifTrue: [it _ nil].
(each isString and:[each isSymbol not]) ifTrue: [
it _ Compiler evaluate: each].
each class == Array ifTrue: [it _ it evalStrings].
it]!
----- Method: Array>>hasLiteral: (in category 'private') -----
hasLiteral: literal
"Answer true if literal is identical to any literal in this array, even
if imbedded in further array structure. This method is only intended
for private use by CompiledMethod hasLiteralSymbol:"
| lit |
1 to: self size do:
[:index |
(lit _ self at: index) == literal ifTrue: [^ true].
(lit class == Array and: [lit hasLiteral: literal]) ifTrue: [^ true]].
^ false!
----- Method: Array>>hasLiteralSuchThat: (in category 'private') -----
hasLiteralSuchThat: litBlock
"Answer true if litBlock returns true for any literal in this array, even if imbedded in further array structure. This method is only intended for private use by CompiledMethod hasLiteralSuchThat:"
| lit |
1 to: self size do:
[:index | lit _ self at: index.
(litBlock value: lit) ifTrue: [^ true].
(lit class == Array and: [lit hasLiteralSuchThat: litBlock]) ifTrue: [^ true]].
^false!
----- Method: Array>>hashMappedBy: (in category 'comparing') -----
hashMappedBy: map
"Answer what my hash would be if oops changed according to map."
self size = 0 ifTrue: [^self hash].
^(self first hashMappedBy: map) + (self last hashMappedBy: map)!
----- Method: Array>>isArray (in category 'testing') -----
isArray
^true!
----- Method: Array>>isLiteral (in category 'testing') -----
isLiteral
^ self allSatisfy: [:each | each isLiteral]!
----- Method: Array>>literalEqual: (in category 'comparing') -----
literalEqual: other
self class == other class ifFalse: [^ false].
self size = other size ifFalse: [^ false].
self with: other do: [:e1 :e2 |
(e1 literalEqual: e2) ifFalse: [^ false]].
^ true!
----- Method: Array>>literalStringsDo: (in category 'translating') -----
literalStringsDo: aBlock
"Assuming the receiver receiver is a literal, evaluate aBlock with all Strings (but not Symbols) within it."
self do: [:each | each literalStringsDo: aBlock]!
----- Method: Array>>objectForDataStream: (in category 'file in/out') -----
objectForDataStream: refStrm
| dp |
"I am about to be written on an object file. If I am one of two shared global arrays, write a proxy instead."
self == (TextConstants at: #DefaultTabsArray) ifTrue: [
dp _ DiskProxy global: #TextConstants selector: #at: args: #(DefaultTabsArray).
refStrm replace: self with: dp.
^ dp].
self == (TextConstants at: #DefaultMarginTabsArray) ifTrue: [
dp _ DiskProxy global: #TextConstants selector: #at: args: #(DefaultMarginTabsArray).
refStrm replace: self with: dp.
^ dp].
^ super objectForDataStream: refStrm!
----- Method: Array>>preMultiplyByArray: (in category 'arithmetic') -----
preMultiplyByArray: a
"Answer a+*self where a is an Array. Arrays are always understood as column vectors,
so an n element Array is an n*1 Array. This multiplication is legal iff self size = 1."
self size = 1 ifFalse: [self error: 'dimensions do not conform'].
^a * self first!
----- Method: Array>>preMultiplyByMatrix: (in category 'arithmetic') -----
preMultiplyByMatrix: m
"Answer m+*self where m is a Matrix."
|s|
m columnCount = self size ifFalse: [self error: 'dimensions do not conform'].
^(1 to: m rowCount) collect: [:row |
s _ 0.
1 to: self size do: [:k | s _ (m at: row at: k) * (self at: k) + s].
s]!
----- Method: Array>>printOn: (in category 'printing') -----
printOn: aStream
aStream nextPut: $#.
self printElementsOn: aStream!
----- Method: Array>>replaceFrom:to:with:startingAt: (in category 'private') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
"Primitive. This destructively replaces elements from start to stop in the receiver starting at index, repStart, in the collection, replacement. Answer the receiver. Range checks are performed in the primitive only. Optional. See Object documentation whatIsAPrimitive."
<primitive: 105>
super replaceFrom: start to: stop with: replacement startingAt: repStart!
----- Method: Array>>shallowCopy (in category 'copying') -----
shallowCopy
^ self clone.
!
----- Method: Array>>storeOn: (in category 'printing') -----
storeOn: aStream
"Use the literal form if possible."
self isLiteral
ifTrue:
[aStream nextPut: $#; nextPut: $(.
self do:
[:element |
element printOn: aStream.
aStream space].
aStream nextPut: $)]
ifFalse: [super storeOn: aStream]!
----- Method: Array>>storeOnStream: (in category 'filter streaming') -----
storeOnStream:aStream
self isLiteral ifTrue: [super storeOnStream:aStream] ifFalse:[aStream writeCollection:self].
!
----- Method: Array>>translatedNoop (in category 'translating') -----
translatedNoop
"This is correspondence gettext_noop() in gettext."
^ self!
Array weakSubclass: #WeakArray
instanceVariableNames: ''
classVariableNames: 'FinalizationDependents FinalizationLock FinalizationProcess FinalizationSemaphore IsFinalizationSupported'
poolDictionaries: ''
category: 'Collections-Weak'!
!WeakArray commentStamp: '<historical>' prior: 0!
WeakArray is an array which holds only weakly on its elements. This means whenever an object is only referenced by instances of WeakArray it will be garbage collected.!
----- Method: WeakArray class>>addWeakDependent: (in category 'accessing') -----
addWeakDependent: anObject
| finished index weakDependent |
self isFinalizationSupported ifFalse:[^self].
FinalizationLock critical:[
finished := false.
index := 0.
[index := index + 1.
finished not and:[index <= FinalizationDependents size]] whileTrue:[
weakDependent := FinalizationDependents at: index.
weakDependent isNil ifTrue:[
FinalizationDependents at: index put: anObject.
finished := true.
].
].
finished ifFalse:[
"Grow linearly"
FinalizationDependents := FinalizationDependents, (WeakArray new: 10).
FinalizationDependents at: index put: anObject.
].
] ifError:[:msg :rcvr| rcvr error: msg].!
----- Method: WeakArray class>>finalizationProcess (in category 'private') -----
finalizationProcess
[true] whileTrue:
[FinalizationSemaphore wait.
FinalizationLock critical:
[FinalizationDependents do:
[:weakDependent |
weakDependent ifNotNil:
[weakDependent finalizeValues.
"***Following statement is required to keep weakDependent
from holding onto its value as garbage.***"
weakDependent _ nil]]]
ifError:
[:msg :rcvr | rcvr error: msg].
].
!
----- Method: WeakArray class>>initialize (in category 'class initialization') -----
initialize
"WeakArray initialize"
"Do we need to initialize specialObjectsArray?"
Smalltalk specialObjectsArray size < 42
ifTrue:[Smalltalk recreateSpecialObjectsArray].
Smalltalk addToStartUpList: self.
self restartFinalizationProcess.!
----- Method: WeakArray class>>isFinalizationSupported (in category 'accessing') -----
isFinalizationSupported
"Check if this VM supports the finalization mechanism"
| tempObject |
IsFinalizationSupported ifNotNil:[^IsFinalizationSupported].
tempObject _ WeakArray new: 1.
"Check if the class format 4 is correctly understood by the VM.
If the weak class support is not installed then the VM will report
any weak class as containing 32bit words - not pointers"
(tempObject at: 1) = nil
ifFalse:[^IsFinalizationSupported _false].
"Check if objects are correctly freed"
self pvtCreateTemporaryObjectIn: tempObject.
Smalltalk garbageCollect.
^IsFinalizationSupported _ (tempObject at: 1) == nil!
----- Method: WeakArray class>>pvtCreateTemporaryObjectIn: (in category 'private') -----
pvtCreateTemporaryObjectIn: tempObject
"We have to create the temporary object in a separate stack frame"
tempObject at: 1 put: Object new!
----- Method: WeakArray class>>removeWeakDependent: (in category 'accessing') -----
removeWeakDependent: anObject
self isFinalizationSupported ifFalse:[^self].
FinalizationLock critical:[
1 to: FinalizationDependents size do:[:i|
((FinalizationDependents at: i) == anObject) ifTrue:[
FinalizationDependents at: i put: nil.
].
].
] ifError:[:msg :rcvr| rcvr error: msg].!
----- Method: WeakArray class>>restartFinalizationProcess (in category 'private') -----
restartFinalizationProcess
"kill any old process, just in case"
FinalizationProcess
ifNotNil: [FinalizationProcess terminate.
FinalizationProcess := nil].
"Check if Finalization is supported by this VM"
IsFinalizationSupported := nil.
self isFinalizationSupported
ifFalse: [^ self].
FinalizationSemaphore := Smalltalk specialObjectsArray at: 42.
FinalizationDependents ifNil: [FinalizationDependents := WeakArray new: 10].
FinalizationLock := Semaphore forMutualExclusion.
FinalizationProcess := [self finalizationProcess]
forkAt: Processor userInterruptPriority!
----- Method: WeakArray class>>runningFinalizationProcess (in category 'accessing') -----
runningFinalizationProcess
"Answer the FinalizationProcess I am running, if any"
^FinalizationProcess!
----- Method: WeakArray class>>startUp: (in category 'system startup') -----
startUp: resuming
resuming ifFalse: [ ^self ].
self restartFinalizationProcess.!
----- Method: ArrayedCollection class>>ccg:generateCoerceToOopFrom:on: (in category 'plugin generation') -----
ccg: cg generateCoerceToOopFrom: aNode on: aStream
self instSize > 0 ifTrue:
[self error: 'cannot auto-coerce arrays with named instance variables'].
cg generateCoerceToObjectFromPtr: aNode on: aStream!
----- Method: ArrayedCollection class>>ccg:generateCoerceToValueFrom:on: (in category 'plugin generation') -----
ccg: cg generateCoerceToValueFrom: aNode on: aStream
cg
generateCoerceToPtr: (self ccgDeclareCForVar: '')
fromObject: aNode on: aStream!
----- Method: ArrayedCollection class>>new (in category 'instance creation') -----
new
"Answer a new instance of me, with size = 0."
^self new: 0!
----- Method: ArrayedCollection class>>new:withAll: (in category 'instance creation') -----
new: size withAll: value
"Answer an instance of me, with number of elements equal to size, each
of which refers to the argument, value."
^(self new: size) atAllPut: value!
----- Method: ArrayedCollection class>>newFrom: (in category 'instance creation') -----
newFrom: aCollection
"Answer an instance of me containing the same elements as aCollection."
| newArray |
newArray _ self new: aCollection size.
1 to: aCollection size do: [:i | newArray at: i put: (aCollection at: i)].
^ newArray
" Array newFrom: {1. 2. 3}
{1. 2. 3} as: Array
{1. 2. 3} as: ByteArray
{$c. $h. $r} as: String
{$c. $h. $r} as: Text
"!
----- Method: ArrayedCollection class>>newFromStream: (in category 'instance creation') -----
newFromStream: s
"Only meant for my subclasses that are raw bits and word-like. For quick unpack form the disk."
| len |
self isPointers | self isWords not ifTrue: [^ super newFromStream: s].
"super may cause an error, but will not be called."
s next = 16r80 ifTrue:
["A compressed format. Could copy what BitMap does, or use a
special sound compression format. Callers normally compress their own way."
^ self error: 'not implemented'].
s skip: -1.
len _ s nextInt32.
^ s nextWordsInto: (self basicNew: len)!
----- Method: ArrayedCollection class>>with: (in category 'instance creation') -----
with: anObject
"Answer a new instance of me, containing only anObject."
| newCollection |
newCollection _ self new: 1.
newCollection at: 1 put: anObject.
^newCollection!
----- Method: ArrayedCollection class>>with:with: (in category 'instance creation') -----
with: firstObject with: secondObject
"Answer a new instance of me, containing firstObject and secondObject."
| newCollection |
newCollection _ self new: 2.
newCollection at: 1 put: firstObject.
newCollection at: 2 put: secondObject.
^newCollection!
----- Method: ArrayedCollection class>>with:with:with: (in category 'instance creation') -----
with: firstObject with: secondObject with: thirdObject
"Answer a new instance of me, containing only the three arguments as
elements."
| newCollection |
newCollection _ self new: 3.
newCollection at: 1 put: firstObject.
newCollection at: 2 put: secondObject.
newCollection at: 3 put: thirdObject.
^newCollection!
----- Method: ArrayedCollection class>>with:with:with:with: (in category 'instance creation') -----
with: firstObject with: secondObject with: thirdObject with: fourthObject
"Answer a new instance of me, containing only the three arguments as
elements."
| newCollection |
newCollection _ self new: 4.
newCollection at: 1 put: firstObject.
newCollection at: 2 put: secondObject.
newCollection at: 3 put: thirdObject.
newCollection at: 4 put: fourthObject.
^newCollection!
----- Method: ArrayedCollection class>>with:with:with:with:with: (in category 'instance creation') -----
with: firstObject with: secondObject with: thirdObject with: fourthObject with: fifthObject
"Answer a new instance of me, containing only the five arguments as
elements."
| newCollection |
newCollection _ self new: 5.
newCollection at: 1 put: firstObject.
newCollection at: 2 put: secondObject.
newCollection at: 3 put: thirdObject.
newCollection at: 4 put: fourthObject.
newCollection at: 5 put: fifthObject.
^newCollection!
----- Method: ArrayedCollection class>>with:with:with:with:with:with: (in category 'instance creation') -----
with: firstObject with: secondObject with: thirdObject with: fourthObject with: fifthObject with: sixthObject
"Answer a new instance of me, containing only the 6 arguments as elements."
| newCollection |
newCollection _ self new: 6.
newCollection at: 1 put: firstObject.
newCollection at: 2 put: secondObject.
newCollection at: 3 put: thirdObject.
newCollection at: 4 put: fourthObject.
newCollection at: 5 put: fifthObject.
newCollection at: 6 put: sixthObject.
^ newCollection!
----- Method: ArrayedCollection class>>withAll: (in category 'instance creation') -----
withAll: aCollection
"Create a new collection containing all the elements from aCollection."
^ (self new: aCollection size) replaceFrom: 1 to: aCollection size with: aCollection!
----- Method: ArrayedCollection>>add: (in category 'adding') -----
add: newObject
self shouldNotImplement!
----- Method: ArrayedCollection>>asSortedArray (in category 'converting') -----
asSortedArray
self isSorted ifTrue: [^ self asArray].
^ super asSortedArray!
----- Method: ArrayedCollection>>byteSize (in category 'objects from disk') -----
byteSize
^self basicSize * self bytesPerBasicElement
!
----- Method: ArrayedCollection>>bytesPerBasicElement (in category 'objects from disk') -----
bytesPerBasicElement
"Answer the number of bytes that each of my basic elements requires.
In other words:
self basicSize * self bytesPerBasicElement
should equal the space required on disk by my variable sized representation."
^self class isBytes ifTrue: [ 1 ] ifFalse: [ 4 ]!
----- Method: ArrayedCollection>>bytesPerElement (in category 'objects from disk') -----
bytesPerElement
^self class isBytes ifTrue: [ 1 ] ifFalse: [ 4 ].
!
----- Method: ArrayedCollection>>defaultElement (in category 'private') -----
defaultElement
^nil!
----- Method: ArrayedCollection>>flattenOnStream: (in category 'filter streaming') -----
flattenOnStream: aStream
aStream writeArrayedCollection: self!
----- Method: ArrayedCollection>>isSorted (in category 'sorting') -----
isSorted
"Return true if the receiver is sorted by the given criterion.
Optimization for isSortedBy: [:a :b | a <= b]."
| lastElm elm |
self isEmpty ifTrue: [^ true].
lastElm _ self first.
2 to: self size do:
[:index |
elm _ self at: index.
lastElm <= elm ifFalse: [^ false].
lastElm _ elm].
^ true!
----- Method: ArrayedCollection>>isSortedBy: (in category 'sorting') -----
isSortedBy: aBlock
"Return true if the receiver is sorted by the given criterion."
| lastElm elm |
self isEmpty ifTrue: [^ true].
lastElm _ self first.
2 to: self size do:
[:index |
elm _ self at: index.
(aBlock value: lastElm value: elm) ifFalse: [^ false].
lastElm _ elm].
^ true!
----- Method: ArrayedCollection>>mergeFirst:middle:last:into:by: (in category 'sorting') -----
mergeFirst: first middle: middle last: last into: dst by: aBlock
"Private. Merge the sorted ranges [first..middle] and [middle+1..last]
of the receiver into the range [first..last] of dst."
| i1 i2 val1 val2 out |
i1 _ first.
i2 _ middle + 1.
val1 _ self at: i1.
val2 _ self at: i2.
out _ first - 1. "will be pre-incremented"
"select 'lower' half of the elements based on comparator"
[(i1 <= middle) and: [i2 <= last]] whileTrue:
[(aBlock value: val1 value: val2)
ifTrue: [dst at: (out _ out + 1) put: val1.
val1 _ self at: (i1 _ i1 + 1)]
ifFalse: [dst at: (out _ out + 1) put: val2.
i2 _ i2 + 1.
i2 <= last ifTrue: [val2 _ self at: i2]]].
"copy the remaining elements"
i1 <= middle
ifTrue: [dst replaceFrom: out + 1 to: last with: self startingAt: i1]
ifFalse: [dst replaceFrom: out + 1 to: last with: self startingAt: i2]!
----- Method: ArrayedCollection>>mergeSortFrom:to:by: (in category 'sorting') -----
mergeSortFrom: startIndex to: stopIndex by: aBlock
"Sort the given range of indices using the mergesort algorithm.
Mergesort is a worst-case O(N log N) sorting algorithm that usually
does only half as many comparisons as heapsort or quicksort."
"Details: recursively split the range to be sorted into two halves,
mergesort each half, then merge the two halves together. An extra
copy of the data is used as temporary storage and successive merge
phases copy data back and forth between the receiver and this copy.
The recursion is set up so that the final merge is performed into the
receiver, resulting in the receiver being completely sorted."
self size <= 1 ifTrue: [^ self]. "nothing to do"
startIndex = stopIndex ifTrue: [^ self].
self assert: [startIndex >= 1 and: [startIndex < stopIndex]]. "bad start index"
self assert: [stopIndex <= self size]. "bad stop index"
self
mergeSortFrom: startIndex
to: stopIndex
src: self clone
dst: self
by: aBlock!
----- Method: ArrayedCollection>>mergeSortFrom:to:src:dst:by: (in category 'sorting') -----
mergeSortFrom: first to: last src: src dst: dst by: aBlock
"Private. Split the range to be sorted in half, sort each half, and
merge the two half-ranges into dst."
| middle |
first = last ifTrue: [^ self].
middle _ (first + last) // 2.
self mergeSortFrom: first to: middle src: dst dst: src by: aBlock.
self mergeSortFrom: middle + 1 to: last src: dst dst: src by: aBlock.
src mergeFirst: first middle: middle last: last into: dst by: aBlock!
----- Method: ArrayedCollection>>restoreEndianness (in category 'objects from disk') -----
restoreEndianness
"This word object was just read in from a stream. It was stored in Big Endian (Mac) format. Reverse the byte order if the current machine is Little Endian.
We only intend this for non-pointer arrays. Do nothing if I contain pointers."
self class isPointers | self class isWords not ifTrue: [^self].
SmalltalkImage current isLittleEndian
ifTrue:
[Bitmap
swapBytesIn: self
from: 1
to: self basicSize]!
----- Method: ArrayedCollection>>size (in category 'accessing') -----
size
"Answer how many elements the receiver contains."
<primitive: 62>
^ self basicSize!
----- Method: ArrayedCollection>>sort (in category 'sorting') -----
sort
"Sort this array into ascending order using the '<=' operator."
self sort: [:a :b | a <= b]!
----- Method: ArrayedCollection>>sort: (in category 'sorting') -----
sort: aSortBlock
"Sort this array using aSortBlock. The block should take two arguments
and return true if the first element should preceed the second one."
self
mergeSortFrom: 1
to: self size
by: aSortBlock!
----- Method: ArrayedCollection>>storeElementsFrom:to:on: (in category 'private') -----
storeElementsFrom: firstIndex to: lastIndex on: aStream
| noneYet defaultElement arrayElement |
noneYet _ true.
defaultElement _ self defaultElement.
firstIndex to: lastIndex do:
[:index |
arrayElement _ self at: index.
arrayElement = defaultElement
ifFalse:
[noneYet
ifTrue: [noneYet _ false]
ifFalse: [aStream nextPut: $;].
aStream nextPutAll: ' at: '.
aStream store: index.
aStream nextPutAll: ' put: '.
aStream store: arrayElement]].
^noneYet!
----- Method: ArrayedCollection>>storeOn: (in category 'printing') -----
storeOn: aStream
aStream nextPutAll: '(('.
aStream nextPutAll: self class name.
aStream nextPutAll: ' new: '.
aStream store: self size.
aStream nextPut: $).
(self storeElementsFrom: 1 to: self size on: aStream)
ifFalse: [aStream nextPutAll: '; yourself'].
aStream nextPut: $)!
----- Method: ArrayedCollection>>swapBytesFrom:to: (in category 'objects from disk') -----
swapBytesFrom: start to: stop
"Perform a bigEndian/littleEndian byte reversal of my words.
We only intend this for non-pointer arrays. Do nothing if I contain pointers."
| hack blt |
self deprecated: 'Use BitMap class>>swapBytesIn:from:to:'.
self class isPointers | self class isWords not ifTrue: [^ self].
"The implementation is a hack, but fast for large ranges"
hack _ Form new hackBits: self.
blt _ (BitBlt toForm: hack) sourceForm: hack.
blt combinationRule: Form reverse. "XOR"
blt sourceY: start-1; destY: start-1; height: stop-start+1; width: 1.
blt sourceX: 0; destX: 3; copyBits. "Exchange bytes 0 and 3"
blt sourceX: 3; destX: 0; copyBits.
blt sourceX: 0; destX: 3; copyBits.
blt sourceX: 1; destX: 2; copyBits. "Exchange bytes 1 and 2"
blt sourceX: 2; destX: 1; copyBits.
blt sourceX: 1; destX: 2; copyBits.
!
----- Method: ArrayedCollection>>swapHalves (in category 'objects from disk') -----
swapHalves
"A normal switch in endianness (byte order in words) reverses the order of 4 bytes. That is not correct for SoundBuffers, which use 2-bytes units. If a normal switch has be done, this method corrects it further by swapping the two halves of the long word.
This method is only used for 16-bit quanities in SoundBuffer, ShortIntegerArray, etc."
| hack blt |
"The implementation is a hack, but fast for large ranges"
hack _ Form new hackBits: self.
blt _ (BitBlt toForm: hack) sourceForm: hack.
blt combinationRule: Form reverse. "XOR"
blt sourceY: 0; destY: 0; height: self size; width: 2.
blt sourceX: 0; destX: 2; copyBits. "Exchange bytes 0&1 with 2&3"
blt sourceX: 2; destX: 0; copyBits.
blt sourceX: 0; destX: 2; copyBits.!
----- Method: ArrayedCollection>>writeOn: (in category 'objects from disk') -----
writeOn: aStream
"Store the array of bits onto the argument, aStream. (leading byte ~= 16r80) identifies this as raw bits (uncompressed). Always store in Big Endian (Mac) byte order. Do the writing at BitBlt speeds. We only intend this for non-pointer arrays. Do nothing if I contain pointers."
self class isPointers | self class isWords not ifTrue: [^ super writeOn: aStream].
"super may cause an error, but will not be called."
aStream nextInt32Put: self basicSize.
aStream nextWordsPutAll: self.!
----- Method: ArrayedCollection>>writeOnGZIPByteStream: (in category 'objects from disk') -----
writeOnGZIPByteStream: aStream
"We only intend this for non-pointer arrays. Do nothing if I contain pointers."
self class isPointers | self class isWords not ifTrue: [^ super writeOnGZIPByteStream: aStream].
"super may cause an error, but will not be called."
aStream nextPutAllWordArray: self!
ArrayedCollection variableByteSubclass: #ByteArray
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Arrayed'!
!ByteArray commentStamp: '<historical>' prior: 0!
I represent an ArrayedCollection whose elements are integers between 0 and 255.
!
----- Method: ByteArray class>>ccg:emitLoadFor:from:on: (in category 'plugin generation') -----
ccg: cg emitLoadFor: aString from: anInteger on: aStream
cg emitLoad: aString asCharPtrFrom: anInteger on: aStream!
----- Method: ByteArray class>>ccg:prolog:expr:index: (in category 'plugin generation') -----
ccg: cg prolog: aBlock expr: aString index: anInteger
^cg
ccgLoad: aBlock
expr: aString
asCharPtrFrom: anInteger
andThen: (cg ccgValBlock: 'isBytes')!
----- Method: ByteArray class>>ccgDeclareCForVar: (in category 'plugin generation') -----
ccgDeclareCForVar: aSymbolOrString
^'char *', aSymbolOrString!
----- Method: ByteArray class>>hashBytes:startingWith: (in category 'byte based hash') -----
hashBytes: aByteArray startingWith: speciesHash
"Answer the hash of a byte-indexed collection,
using speciesHash as the initial value.
See SmallInteger>>hashMultiply.
The primitive should be renamed at a
suitable point in the future"
| byteArraySize hash low |
<primitive: 'primitiveStringHash' module: 'MiscPrimitivePlugin'>
self var: #aHash declareC: 'int speciesHash'.
self var: #aByteArray declareC: 'unsigned char *aByteArray'.
byteArraySize _ aByteArray size.
hash _ speciesHash bitAnd: 16rFFFFFFF.
1 to: byteArraySize do: [:pos |
hash _ hash + (aByteArray basicAt: pos).
"Begin hashMultiply"
low _ hash bitAnd: 16383.
hash _ (16r260D * low + ((16r260D * (hash bitShift: -14) + (16r0065 * low) bitAnd: 16383) * 16384)) bitAnd: 16r0FFFFFFF.
].
^ hash!
----- Method: ByteArray>>asByteArray (in category 'converting') -----
asByteArray
^ self!
----- Method: ByteArray>>asByteArrayPointer (in category 'private') -----
asByteArrayPointer
"Return a ByteArray describing a pointer to the contents of the receiver."
^self shouldNotImplement!
----- Method: ByteArray>>asSocketAddress (in category 'converting') -----
asSocketAddress
^SocketAddress fromOldByteAddress: self!
----- Method: ByteArray>>asString (in category 'converting') -----
asString
"Convert to a String with Characters for each byte.
Fast code uses primitive that avoids character conversion"
^ (String new: self size) replaceFrom: 1 to: self size with: self!
----- Method: ByteArray>>asWideString (in category 'accessing') -----
asWideString
^ WideString fromByteArray: self.
!
----- Method: ByteArray>>atAllPut: (in category 'accessing') -----
atAllPut: value
"Fill the receiver with the given value"
<primitive: 145>
super atAllPut: value!
----- Method: ByteArray>>byteAt: (in category 'accessing') -----
byteAt: index
<primitive: 60>
^self at: index!
----- Method: ByteArray>>byteAt:put: (in category 'accessing') -----
byteAt: index put: value
<primitive: 61>
^self at: index put: value!
----- Method: ByteArray>>byteSize (in category 'accessing') -----
byteSize
^self size!
----- Method: ByteArray>>bytesPerElement (in category 'accessing') -----
bytesPerElement
"Number of bytes in each item. This multiplied by (self size)*8 gives the number of bits stored."
^ 1!
----- Method: ByteArray>>copy (in category 'copying') -----
copy
^ self clone.
!
----- Method: ByteArray>>defaultElement (in category 'private') -----
defaultElement
^0!
----- Method: ByteArray>>hash (in category 'comparing') -----
hash
"#hash is implemented, because #= is implemented"
^self class
hashBytes: self
startingWith: self species hash!
----- Method: ByteArray>>isLiteral (in category 'testing') -----
isLiteral
"so that #(1 #[1 2 3] 5) prints itself"
^ true!
----- Method: ByteArray>>lastIndexOfPKSignature: (in category 'zip archive') -----
lastIndexOfPKSignature: aSignature
"Answer the last index in me where aSignature (4 bytes long) occurs, or 0 if not found"
| a b c d |
a _ aSignature first.
b _ aSignature second.
c _ aSignature third.
d _ aSignature fourth.
(self size - 3) to: 1 by: -1 do: [ :i |
(((self at: i) = a)
and: [ ((self at: i + 1) = b)
and: [ ((self at: i + 2) = c)
and: [ ((self at: i + 3) = d) ]]])
ifTrue: [ ^i ]
].
^0!
----- Method: ByteArray>>longAt:bigEndian: (in category 'platform independent access') -----
longAt: index bigEndian: aBool
"Return a 32bit integer quantity starting from the given byte index"
| b0 b1 b2 w h |
aBool ifTrue:[
b0 _ self at: index.
b1 _ self at: index+1.
b2 _ self at: index+2.
w _ self at: index+3.
] ifFalse:[
w _ self at: index.
b2 _ self at: index+1.
b1 _ self at: index+2.
b0 _ self at: index+3.
].
"Minimize LargeInteger arithmetic"
h _ ((b0 bitAnd: 16r7F) - (b0 bitAnd: 16r80) bitShift: 8) + b1.
b2 = 0 ifFalse:[w _ (b2 bitShift: 8) + w].
h = 0 ifFalse:[w _ (h bitShift: 16) + w].
^w!
----- Method: ByteArray>>longAt:put:bigEndian: (in category 'platform independent access') -----
longAt: index put: value bigEndian: aBool
"Return a 32bit integer quantity starting from the given byte index"
| b0 b1 b2 b3 |
b0 _ value bitShift: -24.
b0 _ (b0 bitAnd: 16r7F) - (b0 bitAnd: 16r80).
b0 < 0 ifTrue:[b0 := 256 + b0].
b1 _ (value bitShift: -16) bitAnd: 255.
b2 _ (value bitShift: -8) bitAnd: 255.
b3 _ value bitAnd: 255.
aBool ifTrue:[
self at: index put: b0.
self at: index+1 put: b1.
self at: index+2 put: b2.
self at: index+3 put: b3.
] ifFalse:[
self at: index put: b3.
self at: index+1 put: b2.
self at: index+2 put: b1.
self at: index+3 put: b0.
].
^value!
----- Method: ByteArray>>printOn: (in category 'printing') -----
printOn: aStream
aStream nextPutAll: '#['.
self
do: [ :each | each printOn: aStream ]
separatedBy: [ aStream nextPut: $ ].
aStream nextPut: $]!
----- Method: ByteArray>>replaceFrom:to:with:startingAt: (in category 'private') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
"Primitive. This destructively replaces elements from start to stop in the receiver starting at index, repStart, in the collection, replacement. Answer the receiver. Range checks are performed in the primitive only. Optional. See Object documentation whatIsAPrimitive."
<primitive: 105>
super replaceFrom: start to: stop with: replacement startingAt: repStart!
----- Method: ByteArray>>shallowCopy (in category 'copying') -----
shallowCopy
^ self clone.
!
----- Method: ByteArray>>shortAt:bigEndian: (in category 'platform independent access') -----
shortAt: index bigEndian: aBool
"Return a 16 bit integer quantity starting from the given byte index"
| uShort |
uShort _ self unsignedShortAt: index bigEndian: aBool.
^(uShort bitAnd: 16r7FFF) - (uShort bitAnd: 16r8000)!
----- Method: ByteArray>>shortAt:put:bigEndian: (in category 'platform independent access') -----
shortAt: index put: value bigEndian: aBool
"Store a 16 bit integer quantity starting from the given byte index"
self unsignedShortAt: index put: (value bitAnd: 16r7FFF) - (value bitAnd: -16r8000) bigEndian: aBool.
^value!
----- Method: ByteArray>>storeOn: (in category 'printing') -----
storeOn: aStream
aStream nextPutAll: '#['.
self
do: [ :each | each storeOn: aStream ]
separatedBy: [ aStream nextPut: $ ].
aStream nextPut: $]!
----- Method: ByteArray>>unsignedLongAt:bigEndian: (in category 'platform independent access') -----
unsignedLongAt: index bigEndian: aBool
"Return a 32bit unsigned integer quantity starting from the given byte index"
| b0 b1 b2 w |
aBool ifTrue:[
b0 _ self at: index.
b1 _ self at: index+1.
b2 _ self at: index+2.
w _ self at: index+3.
] ifFalse:[
w _ self at: index.
b2 _ self at: index+1.
b1 _ self at: index+2.
b0 _ self at: index+3.
].
"Minimize LargeInteger arithmetic"
b2 = 0 ifFalse:[w _ (b2 bitShift: 8) + w].
b1 = 0 ifFalse:[w _ (b1 bitShift: 16) + w].
b0 = 0 ifFalse:[w _ (b0 bitShift: 24) + w].
^w!
----- Method: ByteArray>>unsignedLongAt:put:bigEndian: (in category 'platform independent access') -----
unsignedLongAt: index put: value bigEndian: aBool
"Store a 32bit unsigned integer quantity starting from the given byte index"
| b0 b1 b2 b3 |
b0 _ value bitShift: -24.
b1 _ (value bitShift: -16) bitAnd: 255.
b2 _ (value bitShift: -8) bitAnd: 255.
b3 _ value bitAnd: 255.
aBool ifTrue:[
self at: index put: b0.
self at: index+1 put: b1.
self at: index+2 put: b2.
self at: index+3 put: b3.
] ifFalse:[
self at: index put: b3.
self at: index+1 put: b2.
self at: index+2 put: b1.
self at: index+3 put: b0.
].
^value!
----- Method: ByteArray>>unsignedShortAt:bigEndian: (in category 'platform independent access') -----
unsignedShortAt: index bigEndian: aBool
"Return a 16 bit unsigned integer quantity starting from the given byte index"
^aBool
ifTrue:[((self at: index) bitShift: 8) + (self at: index+1)]
ifFalse:[((self at: index+1) bitShift: 8) + (self at: index)].!
----- Method: ByteArray>>unsignedShortAt:put:bigEndian: (in category 'platform independent access') -----
unsignedShortAt: index put: value bigEndian: aBool
"Store a 16 bit unsigned integer quantity starting from the given byte index"
aBool ifTrue:[
self at: index put: (value bitShift: -8).
self at: index+1 put: (value bitAnd: 255).
] ifFalse:[
self at: index+1 put: (value bitShift: -8).
self at: index put: (value bitAnd: 255).
].
^value!
ArrayedCollection variableWordSubclass: #ColorArray
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Arrayed'!
----- Method: ColorArray>>asColorArray (in category 'converting') -----
asColorArray
^self!
----- Method: ColorArray>>at: (in category 'accessing') -----
at: index
^(super at: index) asColorOfDepth: 32!
----- Method: ColorArray>>at:put: (in category 'accessing') -----
at: index put: aColor
^super at: index put: (aColor pixelWordForDepth: 32).!
----- Method: ColorArray>>bytesPerElement (in category 'converting') -----
bytesPerElement
^4!
ArrayedCollection variableWordSubclass: #FloatArray
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Arrayed'!
!FloatArray commentStamp: '<historical>' prior: 0!
FloatArrays store 32bit IEEE floating point numbers.!
----- Method: FloatArray class>>ccg:emitLoadFor:from:on: (in category 'plugin generation') -----
ccg: cg emitLoadFor: aString from: anInteger on: aStream
cg emitLoad: aString asFloatPtrFrom: anInteger on: aStream!
----- Method: FloatArray class>>ccg:prolog:expr:index: (in category 'plugin generation') -----
ccg: cg prolog: aBlock expr: aString index: anInteger
^cg ccgLoad: aBlock expr: aString asWBFloatPtrFrom: anInteger!
----- Method: FloatArray class>>ccgDeclareCForVar: (in category 'plugin generation') -----
ccgDeclareCForVar: aSymbolOrString
^'float *', aSymbolOrString!
----- Method: FloatArray>>* (in category 'arithmetic') -----
* anObject
^self clone *= anObject!
----- Method: FloatArray>>*= (in category 'arithmetic') -----
*= anObject
^anObject isNumber
ifTrue:[self primMulScalar: anObject asFloat]
ifFalse:[self primMulArray: anObject]!
----- Method: FloatArray>>+ (in category 'arithmetic') -----
+ anObject
^self clone += anObject!
----- Method: FloatArray>>+= (in category 'arithmetic') -----
+= anObject
^anObject isNumber
ifTrue:[self primAddScalar: anObject asFloat]
ifFalse:[self primAddArray: anObject]!
----- Method: FloatArray>>- (in category 'arithmetic') -----
- anObject
^self clone -= anObject!
----- Method: FloatArray>>-= (in category 'arithmetic') -----
-= anObject
^anObject isNumber
ifTrue:[self primSubScalar: anObject asFloat]
ifFalse:[self primSubArray: anObject]!
----- Method: FloatArray>>/ (in category 'arithmetic') -----
/ anObject
^self clone /= anObject!
----- Method: FloatArray>>/= (in category 'arithmetic') -----
/= anObject
^anObject isNumber
ifTrue:[self primDivScalar: anObject asFloat]
ifFalse:[self primDivArray: anObject]!
----- Method: FloatArray>>\\= (in category 'arithmetic') -----
\\= other
other isNumber ifTrue: [
1 to: self size do: [:i |
self at: i put: (self at: i) \\ other
].
^ self.
].
1 to: (self size min: other size) do: [:i |
self at: i put: (self at: i) \\ (other at: i).
].
!
----- Method: FloatArray>>adaptToNumber:andSend: (in category 'arithmetic') -----
adaptToNumber: rcvr andSend: selector
"If I am involved in arithmetic with a Number. If possible,
convert it to a float and perform the (more efficient) primitive operation."
selector == #+ ifTrue:[^self + rcvr].
selector == #* ifTrue:[^self * rcvr].
selector == #- ifTrue:[^self negated += rcvr].
selector == #/ ifTrue:[^self * (1.0 / rcvr)].
^super adaptToNumber: rcvr andSend: selector!
----- Method: FloatArray>>asFloatArray (in category 'converting') -----
asFloatArray
^self!
----- Method: FloatArray>>at: (in category 'accessing') -----
at: index
<primitive: 'primitiveAt' module: 'FloatArrayPlugin'>
^Float fromIEEE32Bit: (self basicAt: index)!
----- Method: FloatArray>>at:put: (in category 'accessing') -----
at: index put: value
<primitive: 'primitiveAtPut' module: 'FloatArrayPlugin'>
value isFloat
ifTrue:[self basicAt: index put: value asIEEE32BitWord]
ifFalse:[self at: index put: value asFloat].
^value!
----- Method: FloatArray>>defaultElement (in category 'accessing') -----
defaultElement
"Return the default element of the receiver"
^0.0!
----- Method: FloatArray>>dot: (in category 'arithmetic') -----
dot: aFloatVector
"Primitive. Return the dot product of the receiver and the argument.
Fail if the argument is not of the same size as the receiver."
| result |
"<primitive:'primitiveFloatArrayDotProduct'>"
self size = aFloatVector size ifFalse:[^self error:'Must be equal size'].
result _ 0.0.
1 to: self size do:[:i|
result _ result + ((self at: i) * (aFloatVector at: i)).
].
^result!
----- Method: FloatArray>>hash (in category 'comparing') -----
hash
| result |
<primitive:'primitiveHashArray' module: 'FloatArrayPlugin'>
result _ 0.
1 to: self size do:[:i| result _ result + (self basicAt: i) ].
^result bitAnd: 16r1FFFFFFF!
----- Method: FloatArray>>length (in category 'accessing') -----
length
"Return the length of the receiver"
^self squaredLength sqrt!
----- Method: FloatArray>>negated (in category 'arithmetic') -----
negated
^self clone *= -1!
----- Method: FloatArray>>primAddArray: (in category 'primitives-plugin') -----
primAddArray: floatArray
<primitive: 'primitiveAddFloatArray' module: 'FloatArrayPlugin'>
1 to: self size do:[:i| self at: i put: (self at: i) + (floatArray at: i)].!
----- Method: FloatArray>>primAddScalar: (in category 'primitives-plugin') -----
primAddScalar: scalarValue
<primitive: 'primitiveAddScalar' module: 'FloatArrayPlugin'>
1 to: self size do:[:i| self at: i put: (self at: i) + scalarValue].!
----- Method: FloatArray>>primDivArray: (in category 'primitives-plugin') -----
primDivArray: floatArray
<primitive: 'primitiveDivFloatArray' module: 'FloatArrayPlugin'>
1 to: self size do:[:i| self at: i put: (self at: i) / (floatArray at: i)].!
----- Method: FloatArray>>primDivScalar: (in category 'primitives-plugin') -----
primDivScalar: scalarValue
<primitive: 'primitiveDivScalar' module: 'FloatArrayPlugin'>
1 to: self size do:[:i| self at: i put: (self at: i) / scalarValue].!
----- Method: FloatArray>>primMulArray: (in category 'primitives-plugin') -----
primMulArray: floatArray
<primitive: 'primitiveMulFloatArray' module: 'FloatArrayPlugin'>
1 to: self size do:[:i| self at: i put: (self at: i) * (floatArray at: i)].!
----- Method: FloatArray>>primMulScalar: (in category 'primitives-plugin') -----
primMulScalar: scalarValue
<primitive: 'primitiveMulScalar' module: 'FloatArrayPlugin'>
1 to: self size do:[:i| self at: i put: (self at: i) * scalarValue].!
----- Method: FloatArray>>primSubArray: (in category 'primitives-plugin') -----
primSubArray: floatArray
<primitive: 'primitiveSubFloatArray' module: 'FloatArrayPlugin'>
1 to: self size do:[:i| self at: i put: (self at: i) - (floatArray at: i)].!
----- Method: FloatArray>>primSubScalar: (in category 'primitives-plugin') -----
primSubScalar: scalarValue
<primitive: 'primitiveSubScalar' module: 'FloatArrayPlugin'>
1 to: self size do:[:i| self at: i put: (self at: i) - scalarValue].!
----- Method: FloatArray>>replaceFrom:to:with:startingAt: (in category 'private') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
"Primitive. This destructively replaces elements from start to stop in the receiver starting at index, repStart, in the collection, replacement. Answer the receiver. Range checks are performed in the primitive only. Optional. See Object documentation whatIsAPrimitive."
<primitive: 105>
super replaceFrom: start to: stop with: replacement startingAt: repStart!
----- Method: FloatArray>>squaredLength (in category 'accessing') -----
squaredLength
"Return the squared length of the receiver"
^self dot: self!
----- Method: FloatArray>>sum (in category 'primitives-plugin') -----
sum
<primitive: 'primitiveSum' module: 'FloatArrayPlugin'>
^ super sum!
ArrayedCollection variableWordSubclass: #IntegerArray
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Arrayed'!
!IntegerArray commentStamp: '<historical>' prior: 0!
IntegerArrays store 32bit signed Integer values.
Negative values are stored as 2's complement.!
----- Method: IntegerArray class>>ccg:emitLoadFor:from:on: (in category 'plugin generation') -----
ccg: cg emitLoadFor: aString from: anInteger on: aStream
cg emitLoad: aString asIntPtrFrom: anInteger on: aStream!
----- Method: IntegerArray class>>ccg:prolog:expr:index: (in category 'plugin generation') -----
ccg: cg prolog: aBlock expr: aString index: anInteger
^cg
ccgLoad: aBlock
expr: aString
asIntPtrFrom: anInteger
andThen: (cg ccgValBlock: 'isWords')!
----- Method: IntegerArray class>>ccgDeclareCForVar: (in category 'plugin generation') -----
ccgDeclareCForVar: aSymbolOrString
^'int *', aSymbolOrString!
----- Method: IntegerArray>>asIntegerArray (in category 'converting') -----
asIntegerArray
^self!
----- Method: IntegerArray>>at: (in category 'accessing') -----
at: index
| word |
<primitive: 165>
word _ self basicAt: index.
word < 16r3FFFFFFF ifTrue:[^word]. "Avoid LargeInteger computations"
^word >= 16r80000000 "Negative?!!"
ifTrue:["word - 16r100000000"
(word bitInvert32 + 1) negated]
ifFalse:[word]!
----- Method: IntegerArray>>at:put: (in category 'accessing') -----
at: index put: anInteger
| word |
<primitive: 166>
anInteger < 0
ifTrue:["word _ 16r100000000 + anInteger"
word _ (anInteger + 1) negated bitInvert32]
ifFalse:[word _ anInteger].
self basicAt: index put: word.
^anInteger!
----- Method: IntegerArray>>atAllPut: (in category 'accessing') -----
atAllPut: anInteger
| word |
anInteger < 0
ifTrue:["word _ 16r100000000 + anInteger"
word _ (anInteger + 1) negated bitInvert32]
ifFalse:[word _ anInteger].
self primFill: word.!
----- Method: IntegerArray>>defaultElement (in category 'accessing') -----
defaultElement
"Return the default element of the receiver"
^0!
----- Method: IntegerArray>>primFill: (in category 'private') -----
primFill: aPositiveInteger
"Fill the receiver, an indexable bytes or words object, with the given positive integer. The range of possible fill values is [0..255] for byte arrays and [0..(2^32 - 1)] for word arrays."
<primitive: 145>
self errorImproperStore.!
ArrayedCollection subclass: #RunArray
instanceVariableNames: 'runs values lastIndex lastRun lastOffset pangoAttrCache'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Arrayed'!
!RunArray commentStamp: '<historical>' prior: 0!
My instances provide space-efficient storage of data which tends to be constant over long runs of the possible indices. Essentially repeated values are stored singly and then associated with a "run" length that denotes the number of consecutive occurrences of the value.
My two important variables are
runs An array of how many elements are in each run
values An array of what the value is over those elements
The variables lastIndex, lastRun and lastOffset cache the last access
so that streaming through RunArrays is not an N-squared process.
Many complexities of access can be bypassed by using the method
RunArray withStartStopAndValueDo:!
----- Method: RunArray class>>new (in category 'instance creation') -----
new
^self runs: Array new values: Array new!
----- Method: RunArray class>>new:withAll: (in category 'instance creation') -----
new: size withAll: value
"Answer a new instance of me, whose every element is equal to the
argument, value."
size = 0 ifTrue: [^self new].
^self runs: (Array with: size) values: (Array with: value)!
----- Method: RunArray class>>newFrom: (in category 'instance creation') -----
newFrom: aCollection
"Answer an instance of me containing the same elements as aCollection."
| newCollection |
newCollection _ self new.
aCollection do: [:x | newCollection addLast: x].
^newCollection
" RunArray newFrom: {1. 2. 2. 3}
{1. $a. $a. 3} as: RunArray
({1. $a. $a. 3} as: RunArray) values
"!
----- Method: RunArray class>>readFrom: (in category 'instance creation') -----
readFrom: aStream
"Answer an instance of me as described on the stream, aStream."
| size runs values |
size _ aStream nextWord.
runs _ Array new: size.
values _ Array new: size.
1 to: size do:
[:x |
runs at: x put: aStream nextWord.
values at: x put: aStream nextWord].
^ self runs: runs values: values!
----- Method: RunArray class>>runs:values: (in category 'instance creation') -----
runs: newRuns values: newValues
"Answer an instance of me with runs and values specified by the
arguments."
| instance |
instance _ self basicNew.
instance setRuns: newRuns setValues: newValues.
^instance!
----- Method: RunArray class>>scanFrom: (in category 'instance creation') -----
scanFrom: strm
"Read the style section of a fileOut or sources file. nextChunk has already been done. We need to return a RunArray of TextAttributes of various kinds. These are written by the implementors of writeScanOn:"
| rr vv aa this |
(strm peekFor: $( ) ifFalse: [^ nil].
rr _ OrderedCollection new.
[strm skipSeparators.
strm peekFor: $)] whileFalse:
[rr add: (Number readFrom: strm)].
vv _ OrderedCollection new. "Value array"
aa _ OrderedCollection new. "Attributes list"
[(this _ strm next) == nil] whileFalse: [
this == $, ifTrue: [vv add: aa asArray. aa _ OrderedCollection new].
this == $a ifTrue: [aa add:
(TextAlignment new alignment: (Integer readFrom: strm))].
this == $f ifTrue: [aa add:
(TextFontChange new fontNumber: (Integer readFrom: strm))].
this == $F ifTrue: [aa add: (TextFontReference toFont:
(StrikeFont familyName: (strm upTo: $#) size: (Integer readFrom: strm)))].
this == $b ifTrue: [aa add: (TextEmphasis bold)].
this == $i ifTrue: [aa add: (TextEmphasis italic)].
this == $u ifTrue: [aa add: (TextEmphasis underlined)].
this == $= ifTrue: [aa add: (TextEmphasis struckOut)].
this == $n ifTrue: [aa add: (TextEmphasis normal)].
this == $- ifTrue: [aa add: (TextKern kern: -1)].
this == $+ ifTrue: [aa add: (TextKern kern: 1)].
this == $c ifTrue: [aa add: (TextColor scanFrom: strm)]. "color"
this == $L ifTrue: [aa add: (TextLink scanFrom: strm)]. "L not look like 1"
this == $R ifTrue: [aa add: (TextURL scanFrom: strm)].
"R capitalized so it can follow a number"
this == $q ifTrue: [aa add: (TextSqkPageLink scanFrom: strm)].
this == $p ifTrue: [aa add: (TextSqkProjectLink scanFrom: strm)].
this == $P ifTrue: [aa add: (TextPrintIt scanFrom: strm)].
this == $d ifTrue: [aa add: (TextDoIt scanFrom: strm)].
"space, cr do nothing"
].
aa size > 0 ifTrue: [vv add: aa asArray].
^ self runs: rr asArray values: vv asArray
"
RunArray scanFrom: (ReadStream on: '(14 50 312)f1,f1b,f1LInteger +;i')
"!
----- Method: RunArray>>, (in category 'copying') -----
, aRunArray
"Answer a new RunArray that is a concatenation of the receiver and
aRunArray."
| new newRuns |
(aRunArray isMemberOf: RunArray)
ifFalse:
[new _ self copy.
"attempt to be sociable"
aRunArray do: [:each | new addLast: each].
^new].
runs size = 0 ifTrue: [^aRunArray copy].
aRunArray runs size = 0 ifTrue: [^self copy].
(values at: values size) ~= (aRunArray values at: 1)
ifTrue: [^RunArray
runs: runs , aRunArray runs
values: values , aRunArray values].
newRuns _ runs
copyReplaceFrom: runs size
to: runs size
with: aRunArray runs.
newRuns at: runs size put: (runs at: runs size) + (aRunArray runs at: 1).
^RunArray
runs: newRuns
values:
(values
copyReplaceFrom: values size
to: values size
with: aRunArray values)!
----- Method: RunArray>>= (in category 'accessing') -----
= otherArray
"Test if all my elements are equal to those of otherArray"
(otherArray isMemberOf: RunArray) ifFalse: [^ self hasEqualElements: otherArray].
"Faster test between two RunArrays"
^ (runs hasEqualElements: otherArray runs)
and: [values hasEqualElements: otherArray values]!
----- Method: RunArray>>addFirst: (in category 'adding') -----
addFirst: value
"Add value as the first element of the receiver."
self clearCache. "flush access cache"
(runs size=0 or: [values first ~= value])
ifTrue:
[runs := {1}, runs.
values := {value}, values]
ifFalse:
[runs at: 1 put: runs first+1]!
----- Method: RunArray>>addLast: (in category 'adding') -----
addLast: value
"Add value as the last element of the receiver."
self clearCache. "flush access cache"
(runs size=0 or: [values last ~= value])
ifTrue:
[runs := runs copyWith: 1.
values := values copyWith: value]
ifFalse:
[runs at: runs size put: runs last+1]!
----- Method: RunArray>>addLast:times: (in category 'adding') -----
addLast: value times: times
"Add value as the last element of the receiver, the given number of times"
times = 0 ifTrue: [ ^self ].
self clearCache. "flush access cache"
(runs size=0 or: [values last ~= value])
ifTrue:
[runs := runs copyWith: times.
values := values copyWith: value]
ifFalse:
[runs at: runs size put: runs last+times]!
----- Method: RunArray>>at: (in category 'accessing') -----
at: index
self at: index setRunOffsetAndValue: [:run :offset :value | ^value]!
----- Method: RunArray>>at:setRunOffsetAndValue: (in category 'private') -----
at: index setRunOffsetAndValue: aBlock
"Supply all run information to aBlock."
"Tolerates index=0 and index=size+1 for copyReplace: "
| run limit offset |
limit _ runs size.
(lastIndex == nil or: [index < lastIndex])
ifTrue: "cache not loaded, or beyond index - start over"
[run _ 1.
offset _ index-1]
ifFalse: "cache loaded and before index - start at cache"
[run _ lastRun.
offset _ lastOffset + (index-lastIndex)].
[run <= limit and: [offset >= (runs at: run)]]
whileTrue:
[offset _ offset - (runs at: run).
run _ run + 1].
lastIndex _ index. "Load cache for next access"
lastRun _ run.
lastOffset _ offset.
run > limit
ifTrue:
["adjustment for size+1"
run _ run - 1.
offset _ offset + (runs at: run)].
^aBlock
value: run "an index into runs and values"
value: offset "zero-based offset from beginning of this run"
value: (values at: run) "value for this run"!
----- Method: RunArray>>coalesce (in category 'adding') -----
coalesce
"Try to combine adjacent runs"
| ind |
ind _ 2.
[ind > values size] whileFalse: [
(values at: ind-1) = (values at: ind)
ifFalse: [ind _ ind + 1]
ifTrue: ["two are the same, combine them"
values _ values copyReplaceFrom: ind to: ind with: #().
runs at: ind-1 put: (runs at: ind-1) + (runs at: ind).
runs _ runs copyReplaceFrom: ind to: ind with: #().
"self error: 'needed to combine runs' "]].
!
----- Method: RunArray>>copyFrom:to: (in category 'copying') -----
copyFrom: start to: stop
| newRuns run1 run2 offset1 offset2 |
stop < start ifTrue: [^RunArray new].
self at: start setRunOffsetAndValue: [:r :o :value1 | run1 _ r. offset1
_ o. value1].
self at: stop setRunOffsetAndValue: [:r :o :value2 | run2 _ r. offset2
_ o. value2].
run1 = run2
ifTrue:
[newRuns _ Array with: offset2 - offset1 + 1]
ifFalse:
[newRuns _ runs copyFrom: run1 to: run2.
newRuns at: 1 put: (newRuns at: 1) - offset1.
newRuns at: newRuns size put: offset2 + 1].
^RunArray runs: newRuns values: (values copyFrom: run1 to: run2)!
----- Method: RunArray>>copyReplaceFrom:to:with: (in category 'copying') -----
copyReplaceFrom: start to: stop with: replacement
^(self copyFrom: 1 to: start - 1)
, replacement
, (self copyFrom: stop + 1 to: self size)!
----- Method: RunArray>>first (in category 'accessing') -----
first
^values at: 1!
----- Method: RunArray>>last (in category 'accessing') -----
last
^values at: values size!
----- Method: RunArray>>mapValues: (in category 'private') -----
mapValues: mapBlock
"NOTE: only meaningful to an entire set of runs"
values _ values collect: [:val | mapBlock value: val]!
----- Method: RunArray>>printOn: (in category 'printing') -----
printOn: aStream
self printNameOn: aStream.
aStream
nextPutAll: ' runs: ';
print: runs;
nextPutAll: ' values: ';
print: values!
----- Method: RunArray>>rangeOf:startingAt: (in category 'adding') -----
rangeOf: attr startingAt: startPos
"Answer an interval that gives the range of attr at index position startPos. An empty interval with start value startPos is returned when the attribute attr is not present at position startPos. self size > 0 is assumed, it is the responsibility of the caller to test for emptiness of self.
Note that an attribute may span several adjancent runs. "
self at: startPos
setRunOffsetAndValue:
[:run :offset :value |
^(value includes: attr)
ifFalse: [startPos to: startPos - 1]
ifTrue:
[ | firstRelevantPosition lastRelevantPosition idxOfCandidateRun |
lastRelevantPosition := startPos - offset + (runs at: run) - 1.
firstRelevantPosition := startPos - offset.
idxOfCandidateRun := run + 1.
[idxOfCandidateRun <= runs size
and: [(values at: idxOfCandidateRun) includes: attr]]
whileTrue:
[lastRelevantPosition := lastRelevantPosition + (runs at: idxOfCandidateRun).
idxOfCandidateRun := idxOfCandidateRun + 1].
idxOfCandidateRun := run - 1.
[idxOfCandidateRun >= 1
and: [(values at: idxOfCandidateRun) includes: attr]]
whileTrue:
[firstRelevantPosition := firstRelevantPosition - (runs at: idxOfCandidateRun).
idxOfCandidateRun := idxOfCandidateRun - 1].
firstRelevantPosition to: lastRelevantPosition]
]!
----- Method: RunArray>>repeatLast:ifEmpty: (in category 'adding') -----
repeatLast: times ifEmpty: defaultBlock
"add the last value back again, the given number of times. If we are empty, add (defaultBlock value)"
times = 0 ifTrue: [^self ].
self clearCache. "flush access cache"
(runs size=0)
ifTrue:
[runs := runs copyWith: times.
values := values copyWith: defaultBlock value]
ifFalse:
[runs at: runs size put: runs last+times] !
----- Method: RunArray>>repeatLastIfEmpty: (in category 'adding') -----
repeatLastIfEmpty: defaultBlock
"add the last value back again. If we are empty, add (defaultBlock value)"
self clearCache. "flush access cache"
(runs size=0)
ifTrue:[
runs := runs copyWith: 1.
values := values copyWith: defaultBlock value]
ifFalse:
[runs at: runs size put: runs last+1]!
----- Method: RunArray>>reversed (in category 'converting') -----
reversed
^self class runs: runs reversed values: values reversed!
----- Method: RunArray>>runLengthAt: (in category 'accessing') -----
runLengthAt: index
"Answer the length remaining in run beginning at index."
self at: index
setRunOffsetAndValue: [:run :offset :value | ^(runs at: run) - offset]!
----- Method: RunArray>>runs (in category 'private') -----
runs
^runs!
----- Method: RunArray>>runsAndValuesDo: (in category 'enumerating') -----
runsAndValuesDo: aBlock
"Evaluate aBlock with run lengths and values from the receiver"
^runs with: values do: aBlock.!
----- Method: RunArray>>runsFrom:to:do: (in category 'enumerating') -----
runsFrom: start to: stop do: aBlock
"Evaluate aBlock with all existing runs in the range from start to stop"
| run value index |
start > stop ifTrue:[^self].
self at: start setRunOffsetAndValue:[:firstRun :offset :firstValue|
run _ firstRun.
value _ firstValue.
index _ start + (runs at: run) - offset.
[aBlock value: value.
index <= stop] whileTrue:[
run _ run + 1.
value _ values at: run.
index _ index + (runs at: run)]].
!
----- Method: RunArray>>setRuns:setValues: (in category 'private') -----
setRuns: newRuns setValues: newValues
self clearCache. "flush access cache"
runs := newRuns asArray.
values := newValues asArray.!
----- Method: RunArray>>size (in category 'accessing') -----
size
| size |
size _ 0.
1 to: runs size do: [:i | size _ size + (runs at: i)].
^size!
----- Method: RunArray>>storeOn: (in category 'printing') -----
storeOn: aStream
aStream nextPut: $(.
aStream nextPutAll: self class name.
aStream nextPutAll: ' runs: '.
runs storeOn: aStream.
aStream nextPutAll: ' values: '.
values storeOn: aStream.
aStream nextPut: $)!
----- Method: RunArray>>values (in category 'private') -----
values
"Answer the values in the receiver."
^values!
----- Method: RunArray>>withStartStopAndValueDo: (in category 'accessing') -----
withStartStopAndValueDo: aBlock
| start stop |
start _ 1.
runs with: values do:
[:len : val | stop _ start + len - 1.
aBlock value: start value: stop value: val.
start _ stop + 1]
!
----- Method: RunArray>>writeOn: (in category 'printing') -----
writeOn: aStream
aStream nextWordPut: runs size.
1 to: runs size do:
[:x |
aStream nextWordPut: (runs at: x).
aStream nextWordPut: (values at: x)]!
----- Method: RunArray>>writeScanOn: (in category 'printing') -----
writeScanOn: strm
"Write out the format used for text runs in source files. (14 50 312)f1,f1b,f1LInteger +;i"
strm nextPut: $(.
runs do: [:rr | rr printOn: strm. strm space].
strm skip: -1; nextPut: $).
values do: [:vv |
vv do: [:att | att writeScanOn: strm].
strm nextPut: $,].
strm skip: -1. "trailing comma"!
ArrayedCollection variableSubclass: #SparseLargeTable
instanceVariableNames: 'base size chunkSize defaultValue'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Arrayed'!
!SparseLargeTable commentStamp: '<historical>' prior: 0!
Derivated from Stephan Pair's LargeArray, but to hold a sparse table, in which most of the entries are the same default value, it uses some tricks.!
----- Method: SparseLargeTable class>>defaultChunkSize (in category 'accessing') -----
defaultChunkSize
^100!
----- Method: SparseLargeTable class>>defaultChunkSizeForFiles (in category 'accessing') -----
defaultChunkSizeForFiles
^8000!
----- Method: SparseLargeTable class>>new: (in category 'instance creation') -----
new: size
^self new: size chunkSize: self defaultChunkSize
!
----- Method: SparseLargeTable class>>new:chunkSize: (in category 'instance creation') -----
new: size chunkSize: chunkSize
^self new: size chunkSize: chunkSize arrayClass: Array
!
----- Method: SparseLargeTable class>>new:chunkSize:arrayClass: (in category 'instance creation') -----
new: size chunkSize: chunkSize arrayClass: aClass
^self new: size chunkSize: chunkSize arrayClass: Array base: 1.
!
----- Method: SparseLargeTable class>>new:chunkSize:arrayClass:base: (in category 'instance creation') -----
new: size chunkSize: chunkSize arrayClass: aClass base: b
^self new: size chunkSize: chunkSize arrayClass: Array base: 1 defaultValue: nil.
!
----- Method: SparseLargeTable class>>new:chunkSize:arrayClass:base:defaultValue: (in category 'instance creation') -----
new: size chunkSize: chunkSize arrayClass: aClass base: b defaultValue: d
| basicSize |
(basicSize := ((size - 1) // chunkSize) + 1) = 0
ifTrue: [basicSize := 1].
^(self basicNew: basicSize)
initChunkSize: chunkSize size: size arrayClass: aClass base: b defaultValue: d;
yourself
!
----- Method: SparseLargeTable>>allDefaultValueSubtableAt: (in category 'private') -----
allDefaultValueSubtableAt: index
| t |
t _ self basicAt: index.
t ifNil: [^ true].
t do: [:e |
e ~= defaultValue ifTrue: [^ false].
].
^ true.
!
----- Method: SparseLargeTable>>analyzeSpaceSaving (in category 'private') -----
analyzeSpaceSaving
| total elems tablesTotal nonNilTables |
total _ size - base + 1.
elems _ 0.
base to: size do: [:i | (self at: i) ~= defaultValue ifTrue: [elems _ elems + 1]].
tablesTotal _ self basicSize.
nonNilTables _ 0.
1 to: self basicSize do: [:i | (self basicAt: i) ifNotNil: [nonNilTables _ nonNilTables + 1]].
^ String streamContents: [:strm |
strm nextPutAll: 'total: '.
strm nextPutAll: total printString.
strm nextPutAll: ' elements: '.
strm nextPutAll: elems printString.
strm nextPutAll: ' tables: '.
strm nextPutAll: tablesTotal printString.
strm nextPutAll: ' non-nil: '.
strm nextPutAll: nonNilTables printString.
].
!
----- Method: SparseLargeTable>>arrayClass (in category 'accessing') -----
arrayClass
^(self basicAt: 1) class
!
----- Method: SparseLargeTable>>at: (in category 'accessing') -----
at: index
self pvtCheckIndex: index.
^self noCheckAt: index.
!
----- Method: SparseLargeTable>>at:put: (in category 'accessing') -----
at: index put: value
self pvtCheckIndex: index.
^self noCheckAt: index put: value
!
----- Method: SparseLargeTable>>base (in category 'accessing') -----
base
^ base.
!
----- Method: SparseLargeTable>>chunkSize (in category 'accessing') -----
chunkSize
^chunkSize
!
----- Method: SparseLargeTable>>copyEmpty (in category 'private') -----
copyEmpty
"Answer a copy of the receiver that contains no elements."
^self speciesNew: 0
!
----- Method: SparseLargeTable>>findLastNonNilSubTable (in category 'private') -----
findLastNonNilSubTable
(self basicAt: self basicSize) ifNotNil: [^ self basicSize].
self basicSize - 1 to: 1 by: -1 do: [:lastIndex |
(self basicAt: lastIndex) ifNotNil: [^ lastIndex].
].
^ 0.
!
----- Method: SparseLargeTable>>initChunkSize:size:arrayClass:base:defaultValue: (in category 'initialization') -----
initChunkSize: aChunkSize size: aSize arrayClass: aClass base: b defaultValue: d
| lastChunkSize |
chunkSize := aChunkSize.
size := aSize.
base _ b.
defaultValue _ d.
1 to: (self basicSize - 1) do: [ :in | self basicAt: in put: (aClass new: chunkSize withAll: defaultValue) ].
lastChunkSize := size \\ chunkSize.
lastChunkSize = 0 ifTrue: [lastChunkSize := chunkSize].
size = 0
ifTrue: [self basicAt: 1 put: (aClass new: 0)]
ifFalse: [self basicAt: self basicSize put: (aClass new: lastChunkSize withAll: defaultValue)].
!
----- Method: SparseLargeTable>>noCheckAt: (in category 'accessing') -----
noCheckAt: index
| chunkIndex t |
chunkIndex := index - base // chunkSize + 1.
(chunkIndex > self basicSize or: [chunkIndex < 1]) ifTrue: [^ defaultValue].
t _ self basicAt: chunkIndex.
t ifNil: [^ defaultValue].
^ t at: (index - base + 1 - (chunkIndex - 1 * chunkSize))
!
----- Method: SparseLargeTable>>noCheckAt:put: (in category 'accessing') -----
noCheckAt: index put: value
| chunkIndex t |
chunkIndex := index - base // chunkSize + 1.
chunkIndex > self basicSize ifTrue: [^ value].
t _ self basicAt: chunkIndex.
t ifNil: [^ value].
^ t at: (index - base + 1 - (chunkIndex - 1 * chunkSize)) put: value
!
----- Method: SparseLargeTable>>printElementsOn: (in category 'printing') -----
printElementsOn: aStream
| element |
aStream nextPut: $(.
base to: size do: [:index | element _ self at: index. aStream print: element; space].
self isEmpty ifFalse: [aStream skip: -1].
aStream nextPut: $)
!
----- Method: SparseLargeTable>>printOn: (in category 'printing') -----
printOn: aStream
(#(String) includes: self arrayClass name)
ifTrue: [^self storeOn: aStream].
^super printOn: aStream
!
----- Method: SparseLargeTable>>privateSize: (in category 'private') -----
privateSize: s
size _ s.
!
----- Method: SparseLargeTable>>pvtCheckIndex: (in category 'private') -----
pvtCheckIndex: index
index isInteger ifFalse: [self errorNonIntegerIndex].
index < 1 ifTrue: [self errorSubscriptBounds: index].
index > size ifTrue: [self errorSubscriptBounds: index].
!
----- Method: SparseLargeTable>>similarInstance (in category 'private') -----
similarInstance
^self class
new: self size
chunkSize: self chunkSize
arrayClass: self arrayClass
!
----- Method: SparseLargeTable>>similarInstance: (in category 'private') -----
similarInstance: newSize
^self class
new: newSize
chunkSize: self chunkSize
arrayClass: self arrayClass
!
----- Method: SparseLargeTable>>similarSpeciesInstance (in category 'private') -----
similarSpeciesInstance
^self similarInstance
!
----- Method: SparseLargeTable>>similarSpeciesInstance: (in category 'private') -----
similarSpeciesInstance: newSize
^self similarInstance: newSize
!
----- Method: SparseLargeTable>>size (in category 'accessing') -----
size
^size
!
----- Method: SparseLargeTable>>speciesNew (in category 'private') -----
speciesNew
^self species
new: self size
chunkSize: self chunkSize
arrayClass: self arrayClass
!
----- Method: SparseLargeTable>>speciesNew: (in category 'private') -----
speciesNew: newSize
^self species
new: newSize
chunkSize: self chunkSize
arrayClass: self arrayClass
!
----- Method: SparseLargeTable>>storeOn: (in category 'printing') -----
storeOn: aStream
| x |
(#(String) includes: self arrayClass name) ifTrue:
[aStream nextPut: $'.
1 to: self size do:
[:i |
aStream nextPut: (x _ self at: i).
x == $' ifTrue: [aStream nextPut: x]].
aStream nextPutAll: ''' asLargeArrayChunkSize: '.
aStream nextPutAll: self chunkSize asString.
^self].
^super storeOn: aStream
!
----- Method: SparseLargeTable>>zapDefaultOnlyEntries (in category 'accessing') -----
zapDefaultOnlyEntries
| lastIndex newInst |
1 to: self basicSize do: [:i |
(self allDefaultValueSubtableAt: i) ifTrue: [self basicAt: i put: nil].
].
lastIndex _ self findLastNonNilSubTable.
lastIndex = 0 ifTrue: [^ self].
newInst _ self class new: lastIndex*chunkSize chunkSize: chunkSize arrayClass: (self basicAt: lastIndex) class base: base defaultValue: defaultValue.
newInst privateSize: self size.
base to: newInst size do: [:i | newInst at: i put: (self at: i)].
1 to: newInst basicSize do: [:i |
(newInst allDefaultValueSubtableAt: i) ifTrue: [newInst basicAt: i put: nil].
].
" this is not allowed in production: self becomeForward: newInst. "
^ newInst.
!
ArrayedCollection subclass: #String
instanceVariableNames: ''
classVariableNames: 'AsciiOrder CaseInsensitiveOrder CaseSensitiveOrder CSLineEnders CSNonSeparators CSSeparators HtmlEntities LowercasingTable Tokenish UppercasingTable'
poolDictionaries: ''
category: 'Collections-Strings'!
!String commentStamp: '<historical>' prior: 0!
A String is an indexed collection of Characters. Class String provides the abstract super class for ByteString (that represents an array of 8-bit Characters) and WideString (that represents an array of 32-bit characters). In the similar manner of LargeInteger and SmallInteger, those subclasses are chosen accordingly for a string; namely as long as the system can figure out so, the String is used to represent the given string.
Strings support a vast array of useful methods, which can best be learned by browsing and trying out examples as you find them in the code.
Here are a few useful methods to look at...
String match:
String contractTo:
String also inherits many useful methods from its hierarchy, such as
SequenceableCollection ,
SequenceableCollection copyReplaceAll:with:
!
String variableByteSubclass: #ByteString
instanceVariableNames: ''
classVariableNames: 'Latin1ToUtf8Encodings Latin1ToUtf8Map'
poolDictionaries: ''
category: 'Collections-Strings'!
!ByteString commentStamp: '<historical>' prior: 0!
This class represents the array of 8 bit wide characters.
!
----- Method: ByteString class>>compare:with:collated: (in category 'primitives') -----
compare: string1 with: string2 collated: order
"Return 1, 2 or 3, if string1 is <, =, or > string2, with the collating order of characters given by the order array."
| len1 len2 c1 c2 |
<primitive: 'primitiveCompareString' module: 'MiscPrimitivePlugin'>
self var: #string1 declareC: 'unsigned char *string1'.
self var: #string2 declareC: 'unsigned char *string2'.
self var: #order declareC: 'unsigned char *order'.
len1 _ string1 size.
len2 _ string2 size.
1 to: (len1 min: len2) do:
[:i |
c1 _ order at: (string1 basicAt: i) + 1.
c2 _ order at: (string2 basicAt: i) + 1.
c1 = c2 ifFalse:
[c1 < c2 ifTrue: [^ 1] ifFalse: [^ 3]]].
len1 = len2 ifTrue: [^ 2].
len1 < len2 ifTrue: [^ 1] ifFalse: [^ 3].
!
----- Method: ByteString class>>findFirstInString:inSet:startingAt: (in category 'primitives') -----
findFirstInString: aString inSet: inclusionMap startingAt: start
| i stringSize |
<primitive: 'primitiveFindFirstInString' module: 'MiscPrimitivePlugin'>
self var: #aString declareC: 'unsigned char *aString'.
self var: #inclusionMap declareC: 'char *inclusionMap'.
inclusionMap size ~= 256 ifTrue: [ ^0 ].
i _ start.
stringSize _ aString size.
[ i <= stringSize and: [ (inclusionMap at: (aString at: i) asciiValue+1) = 0 ] ] whileTrue: [
i _ i + 1 ].
i > stringSize ifTrue: [ ^0 ].
^i!
----- Method: ByteString class>>indexOfAscii:inString:startingAt: (in category 'primitives') -----
indexOfAscii: anInteger inString: aString startingAt: start
| stringSize |
<primitive: 'primitiveIndexOfAsciiInString' module: 'MiscPrimitivePlugin'>
self var: #aCharacter declareC: 'int anInteger'.
self var: #aString declareC: 'unsigned char *aString'.
stringSize _ aString size.
start to: stringSize do: [:pos |
(aString at: pos) asciiValue = anInteger ifTrue: [^ pos]].
^ 0
!
----- Method: ByteString class>>initialize (in category 'initialization') -----
initialize
"ByteString initialize"
| latin1 utf8 |
Latin1ToUtf8Map := ByteArray new: 256.
Latin1ToUtf8Encodings := Array new: 256.
0 to: 255 do:[:i|
latin1 := String with: (Character value: i).
utf8 := latin1 convertToWithConverter: UTF8TextConverter new.
latin1 = utf8 ifTrue:[
Latin1ToUtf8Map at: i+1 put: 0. "no translation needed"
] ifFalse:[
Latin1ToUtf8Map at: i+1 put: 1. "no translation needed"
Latin1ToUtf8Encodings at: i+1 put: utf8.
].
].!
----- Method: ByteString class>>stringHash:initialHash: (in category 'primitives') -----
stringHash: aString initialHash: speciesHash
| stringSize hash low |
<primitive: 'primitiveStringHash' module: 'MiscPrimitivePlugin'>
self var: #aHash declareC: 'int speciesHash'.
self var: #aString declareC: 'unsigned char *aString'.
stringSize _ aString size.
hash _ speciesHash bitAnd: 16rFFFFFFF.
1 to: stringSize do: [:pos |
hash _ hash + (aString at: pos) asciiValue.
"Begin hashMultiply"
low _ hash bitAnd: 16383.
hash _ (16r260D * low + ((16r260D * (hash bitShift: -14) + (16r0065 * low) bitAnd: 16383) * 16384)) bitAnd: 16r0FFFFFFF.
].
^ hash!
----- Method: ByteString class>>translate:from:to:table: (in category 'primitives') -----
translate: aString from: start to: stop table: table
"translate the characters in the string by the given table, in place"
<primitive: 'primitiveTranslateStringWithTable' module: 'MiscPrimitivePlugin'>
self var: #table declareC: 'unsigned char *table'.
self var: #aString declareC: 'unsigned char *aString'.
start to: stop do: [ :i |
aString at: i put: (table at: (aString at: i) asciiValue+1) ]!
----- Method: ByteString>>asByteArray (in category 'converting') -----
asByteArray
| ba sz |
sz := self byteSize.
ba := ByteArray new: sz.
ba replaceFrom: 1 to: sz with: self startingAt: 1.
^ba!
----- Method: ByteString>>asOctetString (in category 'converting') -----
asOctetString
^ self.
!
----- Method: ByteString>>at: (in category 'accessing') -----
at: index
"Primitive. Answer the Character stored in the field of the receiver
indexed by the argument. Fail if the index argument is not an Integer or
is out of bounds. Essential. See Object documentation whatIsAPrimitive."
<primitive: 63>
^ Character value: (super at: index)!
----- Method: ByteString>>at:put: (in category 'accessing') -----
at: index put: aCharacter
"Primitive. Store the Character in the field of the receiver indicated by
the index. Fail if the index is not an Integer or is out of bounds, or if
the argument is not a Character. Essential. See Object documentation
whatIsAPrimitive."
<primitive: 64>
aCharacter isCharacter
ifFalse:[^self errorImproperStore].
aCharacter isOctetCharacter ifFalse:[
"Convert to WideString"
self becomeForward: (WideString from: self).
^self at: index put: aCharacter.
].
index isInteger
ifTrue: [self errorSubscriptBounds: index]
ifFalse: [self errorNonIntegerIndex]!
----- Method: ByteString>>byteAt: (in category 'accessing') -----
byteAt: index
<primitive: 60>
^(self at: index) asciiValue!
----- Method: ByteString>>byteAt:put: (in category 'accessing') -----
byteAt: index put: value
<primitive: 61>
self at: index put: value asCharacter.
^value!
----- Method: ByteString>>byteSize (in category 'accessing') -----
byteSize
^self size!
----- Method: ByteString>>convertFromCompoundText (in category 'converting') -----
convertFromCompoundText
| readStream writeStream converter |
readStream _ self readStream.
writeStream _ String new writeStream.
converter _ CompoundTextConverter new.
converter ifNil: [^ self].
[readStream atEnd] whileFalse: [
writeStream nextPut: (converter nextFromStream: readStream)].
^ writeStream contents
!
----- Method: ByteString>>convertFromSystemString (in category 'converting') -----
convertFromSystemString
| readStream writeStream converter |
readStream _ self readStream.
writeStream _ String new writeStream.
converter _ LanguageEnvironment defaultSystemConverter.
converter ifNil: [^ self].
[readStream atEnd] whileFalse: [
writeStream nextPut: (converter nextFromStream: readStream)].
^ writeStream contents
!
----- Method: ByteString>>findSubstring:in:startingAt:matchTable: (in category 'comparing') -----
findSubstring: key in: body startingAt: start matchTable: matchTable
"self assert: ('i' beginsWith: 20999017 asCharacter asString) not"
"self assert: (((String newFrom: {20999017 asCharacter. 20983887
asCharacter}) beginsWith: 20999017 asCharacter asString))"
^ key isByteString
ifTrue: [self
primitiveFindSubstring: key
in: body
startingAt: start
matchTable: matchTable]
ifFalse: [super
findSubstring: key
in: body
startingAt: start
matchTable: matchTable]!
----- Method: ByteString>>isByteString (in category 'testing') -----
isByteString
"Answer whether the receiver is a ByteString"
^true!
----- Method: ByteString>>isOctetString (in category 'testing') -----
isOctetString
"Answer whether the receiver can be represented as a byte string.
This is different from asking whether the receiver *is* a ByteString
(i.e., #isByteString)"
^ true.
!
----- Method: ByteString>>primitiveFindSubstring:in:startingAt:matchTable: (in category 'comparing') -----
primitiveFindSubstring: key in: body startingAt: start matchTable: matchTable
"Answer the index in the string body at which the substring key first occurs, at or beyond start. The match is determined using matchTable, which can be used to effect, eg, case-insensitive matches. If no match is found, zero will be returned.
The algorithm below is not optimum -- it is intended to be translated to C which will go so fast that it wont matter."
| index |
<primitive: 'primitiveFindSubstring' module: 'MiscPrimitivePlugin'>
self var: #key declareC: 'unsigned char *key'.
self var: #body declareC: 'unsigned char *body'.
self var: #matchTable declareC: 'unsigned char *matchTable'.
key size = 0 ifTrue: [^ 0].
start to: body size - key size + 1 do:
[:startIndex |
index _ 1.
[(matchTable at: (body at: startIndex+index-1) asciiValue + 1)
= (matchTable at: (key at: index) asciiValue + 1)]
whileTrue:
[index = key size ifTrue: [^ startIndex].
index _ index+1]].
^ 0
"
' ' findSubstring: 'abc' in: 'abcdefabcd' startingAt: 1 matchTable: CaseSensitiveOrder 1
' ' findSubstring: 'abc' in: 'abcdefabcd' startingAt: 2 matchTable: CaseSensitiveOrder 7
' ' findSubstring: 'abc' in: 'abcdefabcd' startingAt: 8 matchTable: CaseSensitiveOrder 0
' ' findSubstring: 'abc' in: 'abcdefABcd' startingAt: 2 matchTable: CaseSensitiveOrder 0
' ' findSubstring: 'abc' in: 'abcdefABcd' startingAt: 2 matchTable: CaseInsensitiveOrder 7
"!
----- Method: ByteString>>replaceFrom:to:with:startingAt: (in category 'accessing') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
"Primitive. This destructively replaces elements from start to stop in the receiver starting at index, repStart, in the collection, replacement. Answer the receiver. Range checks are performed in the primitive only. Optional. See Object documentation whatIsAPrimitive."
<primitive: 105>
replacement class == WideString ifTrue: [
self becomeForward: (WideString from: self).
].
super replaceFrom: start to: stop with: replacement startingAt: repStart.
!
----- Method: ByteString>>squeakToUtf8 (in category 'converting') -----
squeakToUtf8
"Convert the given string from UTF-8 using the fast path if converting to Latin-1"
| outStream lastIndex nextIndex |
Latin1ToUtf8Map ifNil:[^super squeakToUtf8]. "installation guard"
lastIndex := 1.
nextIndex := ByteString findFirstInString: self inSet: Latin1ToUtf8Map startingAt: lastIndex.
nextIndex = 0 ifTrue:[^self].
outStream := (String new: self size * 2) writeStream.
[outStream next: nextIndex-lastIndex putAll: self startingAt: lastIndex.
outStream nextPutAll: (Latin1ToUtf8Encodings at: (self byteAt: nextIndex)+1).
lastIndex := nextIndex + 1.
nextIndex := ByteString findFirstInString: self inSet: Latin1ToUtf8Map startingAt: lastIndex.
nextIndex = 0] whileFalse.
outStream next: self size-lastIndex+1 putAll: self startingAt: lastIndex.
^outStream contents
!
----- Method: ByteString>>utf8ToSqueak (in category 'converting') -----
utf8ToSqueak
"Convert the given string from UTF-8 using the fast path if converting to Latin-1"
| outStream lastIndex nextIndex byte1 byte2 byte3 byte4 unicode |
Latin1ToUtf8Map ifNil:[^super utf8ToSqueak]. "installation guard"
lastIndex := 1.
nextIndex := ByteString findFirstInString: self inSet: Latin1ToUtf8Map startingAt: lastIndex.
nextIndex = 0 ifTrue:[^self].
outStream := (String new: self size) writeStream.
[outStream next: nextIndex-lastIndex putAll: self startingAt: lastIndex.
byte1 := self byteAt: nextIndex.
(byte1 bitAnd: 16rE0) = 192 ifTrue: [ "two bytes"
byte2 := self byteAt: (nextIndex := nextIndex+1).
(byte2 bitAnd: 16rC0) = 16r80 ifFalse:[self error: 'Invalid UTF-8 input'].
unicode := ((byte1 bitAnd: 31) bitShift: 6) + (byte2 bitAnd: 63)].
(byte1 bitAnd: 16rF0) = 224 ifTrue: [ "three bytes"
byte2 := self byteAt: (nextIndex := nextIndex+1).
(byte2 bitAnd: 16rC0) = 16r80 ifFalse:[self error: 'Invalid UTF-8 input'].
byte3 := self byteAt: (nextIndex := nextIndex+1).
(byte3 bitAnd: 16rC0) = 16r80 ifFalse:[self error: 'Invalid UTF-8 input'].
unicode := ((byte1 bitAnd: 15) bitShift: 12) + ((byte2 bitAnd: 63) bitShift: 6)
+ (byte3 bitAnd: 63)].
(byte1 bitAnd: 16rF8) = 240 ifTrue: [ "four bytes"
byte2 := self byteAt: (nextIndex := nextIndex+1).
(byte2 bitAnd: 16rC0) = 16r80 ifFalse:[self error: 'Invalid UTF-8 input'].
byte3 := self byteAt: (nextIndex := nextIndex+1).
(byte3 bitAnd: 16rC0) = 16r80 ifFalse:[self error: 'Invalid UTF-8 input'].
byte4 := self byteAt: (nextIndex := nextIndex+1).
(byte4 bitAnd: 16rC0) = 16r80 ifFalse:[self error: 'Invalid UTF-8 input'].
unicode := ((byte1 bitAnd: 16r7) bitShift: 18) +
((byte2 bitAnd: 63) bitShift: 12) +
((byte3 bitAnd: 63) bitShift: 6) +
(byte4 bitAnd: 63)].
unicode ifNil:[self error: 'Invalid UTF-8 input'].
outStream nextPut: (Character value: unicode).
lastIndex := nextIndex + 1.
nextIndex := ByteString findFirstInString: self inSet: Latin1ToUtf8Map startingAt: lastIndex.
nextIndex = 0] whileFalse.
outStream next: self size-lastIndex+1 putAll: self startingAt: lastIndex.
^outStream contents
!
----- Method: String class>>ccg:prolog:expr:index: (in category 'plugin generation') -----
ccg: cg prolog: aBlock expr: aString index: anInteger
^cg
ccgLoad: aBlock
expr: aString
asCharPtrFrom: anInteger
andThen: (cg ccgValBlock: 'isBytes')
!
----- Method: String class>>ccgDeclareCForVar: (in category 'plugin generation') -----
ccgDeclareCForVar: aSymbolOrString
^'char *', aSymbolOrString
!
----- Method: String class>>compare:with:collated: (in category 'primitives') -----
compare: string1 with: string2 collated: order
"Return 1, 2 or 3, if string1 is <, =, or > string2, with the collating order of characters given by the order array."
| len1 len2 c1 c2 |
order == nil ifTrue: [
len1 _ string1 size.
len2 _ string2 size.
1 to: (len1 min: len2) do:[:i |
c1 _ (string1 at: i) asInteger.
c2 _ (string2 at: i) asInteger.
c1 = c2 ifFalse: [c1 < c2 ifTrue: [^ 1] ifFalse: [^ 3]].
].
len1 = len2 ifTrue: [^ 2].
len1 < len2 ifTrue: [^ 1] ifFalse: [^ 3].
].
len1 _ string1 size.
len2 _ string2 size.
1 to: (len1 min: len2) do:[:i |
c1 _ (string1 at: i) asInteger.
c2 _ (string2 at: i) asInteger.
c1 < 256 ifTrue: [c1 _ order at: c1 + 1].
c2 < 256 ifTrue: [c2 _ order at: c2 + 1].
c1 = c2 ifFalse:[c1 < c2 ifTrue: [^ 1] ifFalse: [^ 3]].
].
len1 = len2 ifTrue: [^ 2].
len1 < len2 ifTrue: [^ 1] ifFalse: [^ 3].
!
----- Method: String class>>cr (in category 'instance creation') -----
cr
"Answer a string containing a single carriage return character."
^ self with: Character cr
!
----- Method: String class>>crlf (in category 'instance creation') -----
crlf
"Answer a string containing a carriage return and a linefeed."
^ self with: Character cr with: Character lf
!
----- Method: String class>>crlfcrlf (in category 'instance creation') -----
crlfcrlf
^self crlf , self crlf.
!
----- Method: String class>>example (in category 'examples') -----
example
"To see the string displayed at the cursor point, execute this expression
and select a point by pressing a mouse button."
'this is some text' displayOn: Display at: Sensor waitButton!
----- Method: String class>>findFirstInString:inSet:startingAt: (in category 'primitives') -----
findFirstInString: aString inSet: inclusionMap startingAt: start
"Trivial, non-primitive version"
| i stringSize ascii more |
inclusionMap size ~= 256 ifTrue: [^ 0].
stringSize _ aString size.
more _ true.
i _ start - 1.
[more and: [i + 1 <= stringSize]] whileTrue: [
i _ i + 1.
ascii _ (aString at: i) asciiValue.
more _ ascii < 256 ifTrue: [(inclusionMap at: ascii + 1) = 0] ifFalse: [true].
].
i + 1 > stringSize ifTrue: [^ 0].
^ i!
----- Method: String class>>fromByteArray: (in category 'instance creation') -----
fromByteArray: aByteArray
^ aByteArray asString
!
----- Method: String class>>fromPacked: (in category 'instance creation') -----
fromPacked: aLong
"Convert from a longinteger to a String of length 4."
| s |
s _ self new: 4.
s at: 1 put: (aLong digitAt: 4) asCharacter.
s at: 2 put: (aLong digitAt: 3) asCharacter.
s at: 3 put: (aLong digitAt: 2) asCharacter.
s at: 4 put: (aLong digitAt: 1) asCharacter.
^s
"String fromPacked: 'TEXT' asPacked"
!
----- Method: String class>>fromString: (in category 'instance creation') -----
fromString: aString
"Answer an instance of me that is a copy of the argument, aString."
^ aString copyFrom: 1 to: aString size!
----- Method: String class>>indexOfAscii:inString:startingAt: (in category 'primitives') -----
indexOfAscii: anInteger inString: aString startingAt: start
"Trivial, non-primitive version"
| stringSize |
stringSize _ aString size.
start to: stringSize do: [:pos |
(aString at: pos) asInteger = anInteger ifTrue: [^ pos]].
^ 0
!
----- Method: String class>>initialize (in category 'initialization') -----
initialize "self initialize"
| order |
AsciiOrder _ (0 to: 255) as: ByteArray.
CaseInsensitiveOrder _ AsciiOrder copy.
($a to: $z) do:
[:c | CaseInsensitiveOrder at: c asciiValue + 1
put: (CaseInsensitiveOrder at: c asUppercase asciiValue +1)].
"Case-sensitive compare sorts space, digits, letters, all the rest..."
CaseSensitiveOrder _ ByteArray new: 256 withAll: 255.
order _ -1.
' 0123456789' do: "0..10"
[:c | CaseSensitiveOrder at: c asciiValue + 1 put: (order _ order+1)].
($a to: $z) do: "11-64"
[:c | CaseSensitiveOrder at: c asUppercase asciiValue + 1 put: (order _ order+1).
CaseSensitiveOrder at: c asciiValue + 1 put: (order _ order+1)].
1 to: CaseSensitiveOrder size do:
[:i | (CaseSensitiveOrder at: i) = 255 ifTrue:
[CaseSensitiveOrder at: i put: (order _ order+1)]].
order = 255 ifFalse: [self error: 'order problem'].
"a table for translating to lower case"
LowercasingTable _ String withAll: (Character allByteCharacters collect: [:c | c asLowercase]).
"a table for translating to upper case"
UppercasingTable _ String withAll: (Character allByteCharacters collect: [:c | c asUppercase]).
"a table for testing tokenish (for fast numArgs)"
Tokenish _ String withAll: (Character allByteCharacters collect:
[:c | c tokenish ifTrue: [c] ifFalse: [$~]]).
"CR and LF--characters that terminate a line"
CSLineEnders _ CharacterSet empty.
CSLineEnders add: Character cr.
CSLineEnders add: Character lf.
"separators and non-separators"
CSSeparators _ CharacterSet separators.
CSNonSeparators _ CSSeparators complement.!
----- Method: String class>>initializeHtmlEntities (in category 'initialization') -----
initializeHtmlEntities
"self initializeHtmlEntities"
HtmlEntities _ (Dictionary new: 128)
at: 'amp' put: $&;
at: 'lt' put: $<;
at: 'gt' put: $>;
at: 'quot' put: $";
at: 'euro' put: Character euro;
yourself.
#('nbsp' 'iexcl' 'cent' 'pound' 'curren' 'yen' 'brvbar' 'sect' 'uml' 'copy' 'ordf' 'laquo' 'not' 'shy' 'reg' 'hibar' 'deg' 'plusmn' 'sup2' 'sup3' 'acute' 'micro' 'para' 'middot' 'cedil' 'sup1' 'ordm' 'raquo' 'frac14' 'frac12' 'frac34' 'iquest' 'Agrave' 'Aacute' 'Acirc' 'Atilde' 'Auml' 'Aring' 'AElig' 'Ccedil' 'Egrave' 'Eacute' 'Ecirc' 'Euml' 'Igrave' 'Iacute' 'Icirc' 'Iuml' 'ETH' 'Ntilde' 'Ograve' 'Oacute' 'Ocirc' 'Otilde' 'Ouml' 'times' 'Oslash' 'Ugrave' 'Uacute' 'Ucirc' 'Uuml' 'Yacute' 'THORN' 'szlig' 'agrave' 'aacute' 'acirc' 'atilde' 'auml' 'aring' 'aelig' 'ccedil' 'egrave' 'eacute' 'ecirc' 'euml' 'igrave' 'iacute' 'icirc' 'iuml' 'eth' 'ntilde' 'ograve' 'oacute' 'ocirc' 'otilde' 'ouml' 'divide' 'oslash' 'ugrave' 'uacute' 'ucirc' 'uuml' 'yacute' 'thorn' 'yuml' ) withIndexDo: [:each :index | HtmlEntities at: each put: (index + 159) asCharacter]!
----- Method: String class>>lf (in category 'instance creation') -----
lf
"Answer a string containing a single carriage return character."
^ self with: Character lf!
----- Method: String class>>new: (in category 'instance creation') -----
new: sizeRequested
"Answer an instance of this class with the number of indexable
variables specified by the argument, sizeRequested."
self == String
ifTrue:[^ByteString new: sizeRequested]
ifFalse:[^self basicNew: sizeRequested].!
----- Method: String class>>readFrom: (in category 'instance creation') -----
readFrom: inStream
"Answer an instance of me that is determined by reading the stream,
inStream. Embedded double quotes become the quote Character."
| outStream char done |
outStream _ WriteStream on: (self new: 16).
"go to first quote"
inStream skipTo: $'.
done _ false.
[done or: [inStream atEnd]]
whileFalse:
[char _ inStream next.
char = $'
ifTrue:
[char _ inStream next.
char = $'
ifTrue: [outStream nextPut: char]
ifFalse: [done _ true]]
ifFalse: [outStream nextPut: char]].
^outStream contents!
----- Method: String class>>stringHash:initialHash: (in category 'primitives') -----
stringHash: aString initialHash: speciesHash
| stringSize hash low |
stringSize _ aString size.
hash _ speciesHash bitAnd: 16rFFFFFFF.
1 to: stringSize do: [:pos |
hash _ hash + (aString at: pos) asInteger.
"Begin hashMultiply"
low _ hash bitAnd: 16383.
hash _ (16r260D * low + ((16r260D * (hash bitShift: -14) + (16r0065 * low) bitAnd: 16383) * 16384)) bitAnd: 16r0FFFFFFF.
].
^ hash.
!
----- Method: String class>>tab (in category 'instance creation') -----
tab
"Answer a string containing a single tab character."
^ self with: Character tab
!
----- Method: String class>>translate:from:to:table: (in category 'primitives') -----
translate: aString from: start to: stop table: table
"Trivial, non-primitive version"
| char |
start to: stop do: [:i |
char _ (aString at: i) asInteger.
char < 256 ifTrue: [aString at: i put: (table at: char+1)].
].
!
----- Method: String class>>value: (in category 'instance creation') -----
value: anInteger
^ self with: (Character value: anInteger).
!
----- Method: String class>>with: (in category 'instance creation') -----
with: aCharacter
| newCollection |
aCharacter asInteger < 256
ifTrue:[newCollection _ ByteString new: 1]
ifFalse:[newCollection _ WideString new: 1].
newCollection at: 1 put: aCharacter.
^newCollection!
----- Method: String>>* (in category 'arithmetic') -----
* arg
^ arg adaptToString: self andSend: #*!
----- Method: String>>+ (in category 'arithmetic') -----
+ arg
^ arg adaptToString: self andSend: #+!
----- Method: String>>- (in category 'arithmetic') -----
- arg
^ arg adaptToString: self andSend: #-!
----- Method: String>>/ (in category 'arithmetic') -----
/ arg
^ arg adaptToString: self andSend: #/!
----- Method: String>>// (in category 'arithmetic') -----
// arg
^ arg adaptToString: self andSend: #//!
----- Method: String>>< (in category 'comparing') -----
< aString
"Answer whether the receiver sorts before aString.
The collation order is simple ascii (with case differences)."
^(self compare: aString caseSensitive: true) = 1!
----- Method: String>><= (in category 'comparing') -----
<= aString
"Answer whether the receiver sorts before or equal to aString.
The collation order is simple ascii (with case differences)."
^(self compare: aString caseSensitive: true) <= 2!
----- Method: String>>= (in category 'comparing') -----
= aString
"Answer whether the receiver sorts equally as aString.
The collation order is simple ascii (with case differences)."
aString isString ifFalse:[^false].
^(self compare: aString caseSensitive: true) = 2!
----- Method: String>>> (in category 'comparing') -----
> aString
"Answer whether the receiver sorts after aString.
The collation order is simple ascii (with case differences)."
^(self compare: aString caseSensitive: true) = 3!
----- Method: String>>>= (in category 'comparing') -----
>= aString
"Answer whether the receiver sorts after or equal to aString.
The collation order is simple ascii (with case differences)."
^(self compare: aString caseSensitive: true) >= 2!
----- Method: String>>\\ (in category 'arithmetic') -----
\\ arg
^ arg adaptToString: self andSend: #\\!
----- Method: String>>adaptToCollection:andSend: (in category 'converting') -----
adaptToCollection: rcvr andSend: selector
"If I am involved in arithmetic with a collection, convert me to a number."
^ rcvr perform: selector with: self asNumber!
----- Method: String>>adaptToNumber:andSend: (in category 'converting') -----
adaptToNumber: rcvr andSend: selector
"If I am involved in arithmetic with a number, convert me to a number."
^ rcvr perform: selector with: self asNumber!
----- Method: String>>adaptToPoint:andSend: (in category 'converting') -----
adaptToPoint: rcvr andSend: selector
"If I am involved in arithmetic with a point, convert me to a number."
^ rcvr perform: selector with: self asNumber!
----- Method: String>>adaptToString:andSend: (in category 'converting') -----
adaptToString: rcvr andSend: selector
"If I am involved in arithmetic with a string, convert us both to
numbers, and return the printString of the result."
^ (rcvr asNumber perform: selector with: self asNumber) printString!
----- Method: String>>alike: (in category 'comparing') -----
alike: aString
"Answer some indication of how alike the receiver is to the argument, 0 is no match, twice aString size is best score. Case is ignored."
| i j k minSize bonus |
minSize _ (j _ self size) min: (k _ aString size).
bonus _ (j - k) abs < 2 ifTrue: [ 1 ] ifFalse: [ 0 ].
i _ 1.
[(i <= minSize) and: [((super at: i) bitAnd: 16rDF) = ((aString at: i) asciiValue bitAnd: 16rDF)]]
whileTrue: [ i _ i + 1 ].
[(j > 0) and: [(k > 0) and:
[((super at: j) bitAnd: 16rDF) = ((aString at: k) asciiValue bitAnd: 16rDF)]]]
whileTrue: [ j _ j - 1. k _ k - 1. ].
^ i - 1 + self size - j + bonus. !
----- Method: String>>asByteArray (in category 'converting') -----
asByteArray
"Convert to a ByteArray with the ascii values of the string."
| b |
b _ ByteArray new: self byteSize.
1 to: self size * 4 do: [:i |
b at: i put: (self byteAt: i).
].
^ b.
!
----- Method: String>>asByteString (in category 'converting') -----
asByteString
"Convert the receiver into a ByteString"
^self asOctetString!
----- Method: String>>asCharacter (in category 'converting') -----
asCharacter
"Answer the receiver's first character, or '*' if none. Idiosyncratic, provisional."
^ self size > 0 ifTrue: [self first] ifFalse:[$·]!
----- Method: String>>asDate (in category 'converting') -----
asDate
"Many allowed forms, see Date>>#readFrom:"
^ Date fromString: self!
----- Method: String>>asDateAndTime (in category 'converting') -----
asDateAndTime
"Convert from UTC format" ^ DateAndTime fromString: self!
----- Method: String>>asDefaultDecodedString (in category 'converting') -----
asDefaultDecodedString
^ self
!
----- Method: String>>asDisplayText (in category 'converting') -----
asDisplayText
"Answer a DisplayText whose text string is the receiver."
^DisplayText text: self asText!
----- Method: String>>asDuration (in category 'converting') -----
asDuration
"convert from [nnnd]hh:mm:ss[.nanos] format. [] implies optional elements"
^ Duration fromString: self
!
----- Method: String>>asExplorerString (in category 'user interface') -----
asExplorerString
^ self asString!
----- Method: String>>asFileName (in category 'converting') -----
asFileName
"Answer a String made up from the receiver that is an acceptable file
name."
| string checkedString |
string _ FileDirectory checkName: self fixErrors: true.
checkedString _ (FilePath pathName: string) asVmPathName.
^ (FilePath pathName: checkedString isEncoded: true) asSqueakPathName.
!
----- Method: String>>asFourCode (in category 'converting') -----
asFourCode
| result |
self size = 4 ifFalse: [^self error: 'must be exactly four characters'].
result _ self inject: 0 into: [:val :each | 256 * val + each asciiValue].
(result bitAnd: 16r80000000) = 0
ifFalse: [self error: 'cannot resolve fourcode'].
(result bitAnd: 16r40000000) = 0 ifFalse: [^result - 16r80000000].
^ result
!
----- Method: String>>asHex (in category 'converting') -----
asHex
| stream |
stream _ WriteStream on: (String new: self size * 4).
self do: [ :ch | stream nextPutAll: ch hex ].
^stream contents!
----- Method: String>>asHtml (in category 'converting') -----
asHtml
"Do the basic character conversion for HTML. Leave all original return
and tabs in place, so can conver back by simply removing bracked
things. 4/4/96 tk"
| temp |
temp _ self copyReplaceAll: '&' with: '&'.
HtmlEntities keysAndValuesDo:
[:entity :char |
char = $& ifFalse:
[temp _ temp copyReplaceAll: char asString with: '&' , entity , ';']].
temp _ temp copyReplaceAll: ' ' with: ' <IMG SRC="tab.gif" ALT=" ">'.
temp _ temp copyReplaceAll: '
' with: '
<BR>'.
^ temp
"
'A<&>B' asHtml
"!
----- Method: String>>asIRCLowercase (in category 'converting') -----
asIRCLowercase
"Answer a String made up from the receiver whose characters are all
lowercase, where 'lowercase' is by IRC's definition"
^self collect: [ :c | c asIRCLowercase ]!
----- Method: String>>asIdentifier: (in category 'converting') -----
asIdentifier: shouldBeCapitalized
"Return a legal identifier, with first character in upper case if shouldBeCapitalized is true, else lower case. This will always return a legal identifier, even for an empty string"
| aString firstChar firstLetterPosition |
aString _ self select: [:el | el isAlphaNumeric].
firstLetterPosition _ aString findFirst: [:ch | ch isLetter].
aString _ firstLetterPosition == 0
ifFalse:
[aString copyFrom: firstLetterPosition to: aString size]
ifTrue:
['a', aString].
firstChar _ shouldBeCapitalized ifTrue: [aString first asUppercase] ifFalse: [aString first asLowercase].
^ firstChar asString, (aString copyFrom: 2 to: aString size)
"
'234Fred987' asIdentifier: false
'235Fred987' asIdentifier: true
'' asIdentifier: true
'()87234' asIdentifier: false
'())z>=PPve889 U >' asIdentifier: false
"!
----- Method: String>>asInteger (in category 'converting') -----
asInteger
^self asSignedInteger
!
----- Method: String>>asIntegerIfAllDigits (in category 'converting') -----
asIntegerIfAllDigits
"If the receiver consists entirely of digits, answer the integer represented, else answer nil. Because of the all-digits requirement, a negative result is impossible."
^ self isAllDigits ifTrue: [self asNumber] ifFalse: [nil]
"
'123.4' asIntegerIfAllDigits
'2345678901234' asIntegerIfAllDigits
'-234' asIntegerIfAllDigits
'02398' asIntegerIfAllDigits
"!
----- Method: String>>asLegalSelector (in category 'converting') -----
asLegalSelector
| toUse |
toUse _ ''.
self do:
[:char | char isAlphaNumeric ifTrue: [toUse _ toUse copyWith: char]].
(self size == 0 or: [self first isLetter not])
ifTrue: [toUse _ 'v', toUse].
^ toUse withFirstCharacterDownshifted
"'234znak 43 ) 2' asLegalSelector"!
----- Method: String>>asLowercase (in category 'converting') -----
asLowercase
"Answer a String made up from the receiver whose characters are all
lowercase."
^ self copy asString translateToLowercase!
----- Method: String>>asLowercaseAlphabetic (in category 'converting') -----
asLowercaseAlphabetic
"Return a copy of the receiver from which all non-alphabetic chars have been removed"
^ self select: [:ch | ch isLetter] thenCollect: [:l | l asLowercase]
"
' ? abc 8/ d ' asLowercaseAlphabetic
"
!
----- Method: String>>asNumber (in category 'converting') -----
asNumber
"Answer the Number created by interpreting the receiver as the string
representation of a number."
^Number readFromString: self!
----- Method: String>>asOctetString (in category 'converting') -----
asOctetString
"Convert the receiver into an octet string"
| string |
string _ String new: self size.
1 to: self size do: [:i | string at: i put: (self at: i)].
^string!
----- Method: String>>asPacked (in category 'converting') -----
asPacked
"Convert to a longinteger that describes the string"
^ self inject: 0 into: [ :pack :next | pack _ pack * 256 + next asInteger ].!
----- Method: String>>asPangoAttributes (in category 'pango') -----
asPangoAttributes
^ Array new: 0.
!
----- Method: String>>asSignedInteger (in category 'converting') -----
asSignedInteger
"Returns the first signed integer it can find or nil."
| start stream |
start := self findFirst: [:char | char isDigit].
start isZero ifTrue: [^nil].
stream := (ReadStream on: self) position: start.
stream back = $- ifTrue: [stream back].
^Integer readFrom: stream!
----- Method: String>>asSmalltalkComment (in category 'converting') -----
asSmalltalkComment
"return this string, munged so that it can be treated as a comment in Smalltalk code. Quote marks are added to the beginning and end of the string, and whenever a solitary quote mark appears within the string, it is doubled"
^String streamContents: [ :str |
| quoteCount first |
str nextPut: $".
quoteCount := 0.
first := true.
self do: [ :char |
char = $"
ifTrue: [
first ifFalse: [
str nextPut: char.
quoteCount := quoteCount + 1 ] ]
ifFalse: [
quoteCount odd ifTrue: [
"add a quote to even the number of quotes in a row"
str nextPut: $" ].
quoteCount := 0.
str nextPut: char ].
first := false ].
quoteCount odd ifTrue: [
"check at the end"
str nextPut: $". ].
str nextPut: $".
].
!
----- Method: String>>asSqueakPathName (in category 'converting') -----
asSqueakPathName
^ self.
!
----- Method: String>>asString (in category 'converting') -----
asString
"Answer this string."
^ self
!
----- Method: String>>asStringOrText (in category 'converting') -----
asStringOrText
"Answer this string."
^ self
!
----- Method: String>>asSymbol (in category 'converting') -----
asSymbol
"Answer the unique Symbol whose characters are the characters of the
string."
^Symbol intern: self!
----- Method: String>>asText (in category 'converting') -----
asText
"Answer a Text whose string is the receiver."
^Text fromString: self!
----- Method: String>>asTime (in category 'converting') -----
asTime
"Many allowed forms, see Time>>readFrom:"
^ Time fromString: self.!
----- Method: String>>asTimeStamp (in category 'converting') -----
asTimeStamp
"Convert from obsolete TimeStamp format"
^ TimeStamp fromString: self!
----- Method: String>>asUnHtml (in category 'converting') -----
asUnHtml
"Strip out all Html stuff (commands in angle brackets <>) and convert
the characters &<> back to their real value. Leave actual cr and tab as
they were in text."
| in out char rest did |
in _ ReadStream on: self.
out _ WriteStream on: (String new: self size).
[in atEnd] whileFalse:
[in peek = $<
ifTrue: [in unCommand] "Absorb <...><...>"
ifFalse: [(char _ in next) = $&
ifTrue: [rest _ in upTo: $;.
did _ out position.
rest = 'lt' ifTrue: [out nextPut: $<].
rest = 'gt' ifTrue: [out nextPut: $>].
rest = 'amp' ifTrue: [out nextPut: $&].
rest = 'deg' ifTrue: [out nextPut: $°].
rest = 'quot' ifTrue: [out nextPut: $"].
did = out position ifTrue: [
self error: 'unknown encoded HTML char'.
"Please add it to this method"]]
ifFalse: [out nextPut: char]].
].
^ out contents!
----- Method: String>>asUnsignedInteger (in category 'converting') -----
asUnsignedInteger
"Returns the first integer it can find or nil."
| start stream |
start := self findFirst: [:char | char isDigit].
start isZero ifTrue: [^nil].
stream := (ReadStream on: self) position: start - 1.
^Integer readFrom: stream!
----- Method: String>>asUppercase (in category 'converting') -----
asUppercase
"Answer a String made up from the receiver whose characters are all
uppercase."
^self copy asString translateToUppercase!
----- Method: String>>asUrl (in category 'converting') -----
asUrl
"convert to a Url"
"'http://www.cc.gatech.edu/' asUrl"
"msw://chaos.resnet.gatech.edu:9000/' asUrl"
^Url absoluteFromText: self!
----- Method: String>>asUrlRelativeTo: (in category 'converting') -----
asUrlRelativeTo: aUrl
^aUrl newFromRelativeText: self!
----- Method: String>>asVmPathName (in category 'converting') -----
asVmPathName
^ (FilePath pathName: self) asVmPathName.
!
----- Method: String>>asWideString (in category 'converting') -----
asWideString
self isWideString
ifTrue:[^self]
ifFalse:[^WideString from: self]!
----- Method: String>>askIfAddStyle:req: (in category 'converting') -----
askIfAddStyle: priorMethod req: requestor
^ self "we are a string with no text style"!
----- Method: String>>beginsWith: (in category 'comparing') -----
beginsWith: prefix
"Answer whether the receiver begins with the given prefix string.
The comparison is case-sensitive."
self size < prefix size ifTrue: [^ false].
^ (self findSubstring: prefix in: self startingAt: 1
matchTable: CaseSensitiveOrder) = 1
!
----- Method: String>>byteAt: (in category 'accessing') -----
byteAt: index
^self subclassResponsibility!
----- Method: String>>byteAt:put: (in category 'accessing') -----
byteAt: index put: value
^self subclassResponsibility!
----- Method: String>>byteEncode: (in category 'filter streaming') -----
byteEncode:aStream
^aStream writeString: self.
!
----- Method: String>>byteSize (in category 'accessing') -----
byteSize
^self subclassResponsibility!
----- Method: String>>capitalized (in category 'converting') -----
capitalized
"Return a copy with the first letter capitalized"
| cap |
self isEmpty ifTrue: [ ^self copy ].
cap _ self copy.
cap at: 1 put: (cap at: 1) asUppercase.
^ cap!
----- Method: String>>caseInsensitiveLessOrEqual: (in category 'comparing') -----
caseInsensitiveLessOrEqual: aString
"Answer whether the receiver sorts before or equal to aString.
The collation order is case insensitive."
^(self compare: aString caseSensitive: false) <= 2!
----- Method: String>>caseSensitiveLessOrEqual: (in category 'comparing') -----
caseSensitiveLessOrEqual: aString
"Answer whether the receiver sorts before or equal to aString.
The collation order is case sensitive."
^(self compare: aString caseSensitive: true) <= 2!
----- Method: String>>charactersExactlyMatching: (in category 'comparing') -----
charactersExactlyMatching: aString
"Do a character-by-character comparison between the receiver and aString. Return the index of the final character that matched exactly."
| count |
count _ self size min: aString size.
1 to: count do: [:i |
(self at: i) = (aString at: i) ifFalse: [
^ i - 1]].
^ count!
----- Method: String>>compare: (in category 'comparing') -----
compare: aString
"Answer a comparison code telling how the receiver sorts relative to aString:
1 - before
2 - equal
3 - after.
The collation sequence is ascii with case differences ignored.
To get the effect of a <= b, but ignoring case, use (a compare: b) <= 2."
^self compare: aString caseSensitive: false!
----- Method: String>>compare:caseSensitive: (in category 'comparing') -----
compare: aString caseSensitive: aBool
"Answer a comparison code telling how the receiver sorts relative to aString:
1 - before
2 - equal
3 - after.
"
| map |
map := aBool ifTrue:[CaseSensitiveOrder] ifFalse:[CaseInsensitiveOrder].
^self compare: self with: aString collated: map!
----- Method: String>>compare:with:collated: (in category 'comparing') -----
compare: string1 with: string2 collated: order
(string1 isByteString and: [string2 isByteString]) ifTrue: [
^ ByteString compare: string1 with: string2 collated: order
].
"Primitive does not fail properly right now"
^ String compare: string1 with: string2 collated: order
"
self assert: 'abc' = 'abc' asWideString.
self assert: 'abc' asWideString = 'abc'.
self assert: ((ByteArray with: 97 with: 0 with: 0 with: 0) asString ~= 'a000' asWideString).
self assert: ('a000' asWideString ~= (ByteArray with: 97 with: 0 with: 0 with: 0) asString).
self assert: ('abc' sameAs: 'aBc' asWideString).
self assert: ('aBc' asWideString sameAs: 'abc').
self assert: ((ByteArray with: 97 with: 0 with: 0 with: 0) asString sameAs: 'Abcd' asWideString) not.
self assert: ('a000' asWideString sameAs: (ByteArray with: 97 with: 0 with: 0 with: 0) asString) not.
"!
----- Method: String>>composeAccents (in category 'converting') -----
composeAccents
| stream |
stream _ UnicodeCompositionStream on: (String new: 16).
self do: [:e | stream nextPut: e].
^ stream contents.
!
----- Method: String>>compressWithTable: (in category 'converting') -----
compressWithTable: tokens
"Return a string with all substrings that occur in tokens replaced
by a character with ascii code = 127 + token index.
This will work best if tokens are sorted by size.
Assumes this string contains no characters > 127, or that they
are intentionally there and will not interfere with this process."
| str null finalSize start result ri c ts |
null _ Character value: 0.
str _ self copyFrom: 1 to: self size. "Working string will get altered"
finalSize _ str size.
tokens doWithIndex:
[:token :tIndex |
start _ 1.
[(start _ str findString: token startingAt: start) > 0]
whileTrue:
[ts _ token size.
((start + ts) <= str size
and: [(str at: start + ts) = $ and: [tIndex*2 <= 128]])
ifTrue: [ts _ token size + 1. "include training blank"
str at: start put: (Character value: tIndex*2 + 127)]
ifFalse: [str at: start put: (Character value: tIndex + 127)].
str at: start put: (Character value: tIndex + 127).
1 to: ts-1 do: [:i | str at: start+i put: null].
finalSize _ finalSize - (ts - 1).
start _ start + ts]].
result _ String new: finalSize.
ri _ 0.
1 to: str size do:
[:i | (c _ str at: i) = null ifFalse: [result at: (ri _ ri+1) put: c]].
^ result!
----- Method: String>>contractTo: (in category 'converting') -----
contractTo: smallSize
"return myself or a copy shortened by ellipsis to smallSize"
| leftSize |
self size <= smallSize
ifTrue: [^ self]. "short enough"
smallSize < 5
ifTrue: [^ self copyFrom: 1 to: smallSize]. "First N characters"
leftSize _ smallSize-2//2.
^ self copyReplaceFrom: leftSize+1 "First N/2 ... last N/2"
to: self size - (smallSize - leftSize - 3)
with: '...'
"
'A clear but rather long-winded summary' contractTo: 18
"!
----- Method: String>>convertFromEncoding: (in category 'converting') -----
convertFromEncoding: encodingName
^self convertFromWithConverter: (TextConverter newForEncoding: encodingName)!
----- Method: String>>convertFromSuperSwikiServerString (in category 'converting') -----
convertFromSuperSwikiServerString
^self convertFromEncoding: 'shift_jis'!
----- Method: String>>convertFromWithConverter: (in category 'converting') -----
convertFromWithConverter: converter
| readStream writeStream c |
readStream _ self readStream.
writeStream _ String new writeStream.
converter ifNil: [^ self].
[readStream atEnd] whileFalse: [
c _ converter nextFromStream: readStream.
c ifNotNil: [writeStream nextPut: c] ifNil: [^ writeStream contents]
].
^ writeStream contents
!
----- Method: String>>convertToEncoding: (in category 'converting') -----
convertToEncoding: encodingName
^self convertToWithConverter: (TextConverter newForEncoding: encodingName).!
----- Method: String>>convertToSuperSwikiServerString (in category 'converting') -----
convertToSuperSwikiServerString
^self convertToEncoding: 'shift_jis'!
----- Method: String>>convertToSystemString (in category 'converting') -----
convertToSystemString
| readStream writeStream converter |
readStream _ self readStream.
writeStream _ String new writeStream.
converter _ LanguageEnvironment defaultSystemConverter.
converter ifNil: [^ self].
[readStream atEnd] whileFalse: [
converter nextPut: readStream next toStream: writeStream
].
converter emitSequenceToResetStateIfNeededOn: writeStream.
^ writeStream contents.
!
----- Method: String>>convertToWithConverter: (in category 'converting') -----
convertToWithConverter: converter
| readStream writeStream |
readStream _ self readStream.
writeStream _ String new writeStream.
converter ifNil: [^ self].
[readStream atEnd] whileFalse: [
converter nextPut: readStream next toStream: writeStream
].
converter emitSequenceToResetStateIfNeededOn: writeStream.
^ writeStream contents.
!
----- Method: String>>copy (in category 'copying') -----
copy
^ self clone.
!
----- Method: String>>copyReplaceTokens:with: (in category 'copying') -----
copyReplaceTokens: oldSubstring with: newSubstring
"Replace all occurrences of oldSubstring that are surrounded
by non-alphanumeric characters"
^ self copyReplaceAll: oldSubstring with: newSubstring asTokens: true
"'File asFile Files File''s File' copyReplaceTokens: 'File' with: 'Snick'"!
----- Method: String>>correctAgainst: (in category 'converting') -----
correctAgainst: wordList
"Correct the receiver: assume it is a misspelled word and return the (maximum of five) nearest words in the wordList. Depends on the scoring scheme of alike:"
| results |
results _ self correctAgainst: wordList continuedFrom: nil.
results _ self correctAgainst: nil continuedFrom: results.
^ results!
----- Method: String>>correctAgainst:continuedFrom: (in category 'converting') -----
correctAgainst: wordList continuedFrom: oldCollection
"Like correctAgainst:. Use when you want to correct against several lists, give nil as the first oldCollection, and nil as the last wordList."
^ wordList isNil
ifTrue: [ self correctAgainstEnumerator: nil
continuedFrom: oldCollection ]
ifFalse: [ self correctAgainstEnumerator: [ :action | wordList do: action without: nil]
continuedFrom: oldCollection ]!
----- Method: String>>correctAgainstDictionary:continuedFrom: (in category 'converting') -----
correctAgainstDictionary: wordDict continuedFrom: oldCollection
"Like correctAgainst:continuedFrom:. Use when you want to correct against a dictionary."
^ wordDict isNil
ifTrue: [ self correctAgainstEnumerator: nil
continuedFrom: oldCollection ]
ifFalse: [ self correctAgainstEnumerator: [ :action | wordDict keysDo: action ]
continuedFrom: oldCollection ]!
----- Method: String>>correctAgainstEnumerator:continuedFrom: (in category 'private') -----
correctAgainstEnumerator: wordBlock continuedFrom: oldCollection
"The guts of correction, instead of a wordList, there is a block that should take another block and enumerate over some list with it."
| choices scoreMin results score maxChoices |
scoreMin _ self size // 2 min: 3.
maxChoices _ 10.
oldCollection isNil
ifTrue: [ choices _ SortedCollection sortBlock: [ :x :y | x value > y value ] ]
ifFalse: [ choices _ oldCollection ].
wordBlock isNil
ifTrue:
[ results _ OrderedCollection new.
1 to: (maxChoices min: choices size) do: [ :i | results add: (choices at: i) key ] ]
ifFalse:
[ wordBlock value: [ :word |
(score _ self alike: word) >= scoreMin ifTrue:
[ choices add: (Association key: word value: score).
(choices size >= maxChoices) ifTrue: [ scoreMin _ (choices at: maxChoices) value] ] ].
results _ choices ].
^ results!
----- Method: String>>crc16 (in category 'comparing') -----
crc16
"Compute a 16 bit cyclic redundancy check."
| crc |
crc := 0.
1 to: self byteSize do: [:i |
crc := (crc bitShift: -8) bitXor: (
#( 16r0000 16rC0C1 16rC181 16r0140 16rC301 16r03C0 16r0280 16rC241
16rC601 16r06C0 16r0780 16rC741 16r0500 16rC5C1 16rC481 16r0440
16rCC01 16r0CC0 16r0D80 16rCD41 16r0F00 16rCFC1 16rCE81 16r0E40
16r0A00 16rCAC1 16rCB81 16r0B40 16rC901 16r09C0 16r0880 16rC841
16rD801 16r18C0 16r1980 16rD941 16r1B00 16rDBC1 16rDA81 16r1A40
16r1E00 16rDEC1 16rDF81 16r1F40 16rDD01 16r1DC0 16r1C80 16rDC41
16r1400 16rD4C1 16rD581 16r1540 16rD701 16r17C0 16r1680 16rD641
16rD201 16r12C0 16r1380 16rD341 16r1100 16rD1C1 16rD081 16r1040
16rF001 16r30C0 16r3180 16rF141 16r3300 16rF3C1 16rF281 16r3240
16r3600 16rF6C1 16rF781 16r3740 16rF501 16r35C0 16r3480 16rF441
16r3C00 16rFCC1 16rFD81 16r3D40 16rFF01 16r3FC0 16r3E80 16rFE41
16rFA01 16r3AC0 16r3B80 16rFB41 16r3900 16rF9C1 16rF881 16r3840
16r2800 16rE8C1 16rE981 16r2940 16rEB01 16r2BC0 16r2A80 16rEA41
16rEE01 16r2EC0 16r2F80 16rEF41 16r2D00 16rEDC1 16rEC81 16r2C40
16rE401 16r24C0 16r2580 16rE541 16r2700 16rE7C1 16rE681 16r2640
16r2200 16rE2C1 16rE381 16r2340 16rE101 16r21C0 16r2080 16rE041
16rA001 16r60C0 16r6180 16rA141 16r6300 16rA3C1 16rA281 16r6240
16r6600 16rA6C1 16rA781 16r6740 16rA501 16r65C0 16r6480 16rA441
16r6C00 16rACC1 16rAD81 16r6D40 16rAF01 16r6FC0 16r6E80 16rAE41
16rAA01 16r6AC0 16r6B80 16rAB41 16r6900 16rA9C1 16rA881 16r6840
16r7800 16rB8C1 16rB981 16r7940 16rBB01 16r7BC0 16r7A80 16rBA41
16rBE01 16r7EC0 16r7F80 16rBF41 16r7D00 16rBDC1 16rBC81 16r7C40
16rB401 16r74C0 16r7580 16rB541 16r7700 16rB7C1 16rB681 16r7640
16r7200 16rB2C1 16rB381 16r7340 16rB101 16r71C0 16r7080 16rB041
16r5000 16r90C1 16r9181 16r5140 16r9301 16r53C0 16r5280 16r9241
16r9601 16r56C0 16r5780 16r9741 16r5500 16r95C1 16r9481 16r5440
16r9C01 16r5CC0 16r5D80 16r9D41 16r5F00 16r9FC1 16r9E81 16r5E40
16r5A00 16r9AC1 16r9B81 16r5B40 16r9901 16r59C0 16r5880 16r9841
16r8801 16r48C0 16r4980 16r8941 16r4B00 16r8BC1 16r8A81 16r4A40
16r4E00 16r8EC1 16r8F81 16r4F40 16r8D01 16r4DC0 16r4C80 16r8C41
16r4400 16r84C1 16r8581 16r4540 16r8701 16r47C0 16r4680 16r8641
16r8201 16r42C0 16r4380 16r8341 16r4100 16r81C1 16r8081 16r4040)
at: ((crc bitXor: (self byteAt: i)) bitAnd: 16rFF) + 1) ].
^crc!
----- Method: String>>decodeMimeHeader (in category 'internet') -----
decodeMimeHeader
"See RFC 2047, MIME Part Three: Message Header Extension for Non-ASCII
Text. Text containing non-ASCII characters is encoded by the sequence
=?character-set?encoding?encoded-text?=
Encoding is Q (quoted printable) or B (Base64), handled by
Base64MimeConverter / RFC2047MimeConverter.
Thanks to Yokokawa-san, it works in m17n package. Try the following:
'=?ISO-2022-JP?B?U1dJS0lQT1AvGyRCPUJDKyVpJXMlQRsoQi8=?= =?ISO-2022-JP?B?GyRCJVElRiUjJSobKEIoUGF0aW8p?=' decodeMimeHeader.
"
| input output temp charset decoder encodedStream encoding pos |
input _ ReadStream on: self.
output _ WriteStream on: String new.
[output
nextPutAll: (input upTo: $=).
"ASCII Text"
input atEnd]
whileFalse: [(temp _ input next) = $?
ifTrue: [charset _ input upTo: $?.
encoding _ (input upTo: $?) asUppercase.
temp _ input upTo: $?.
input next.
"Skip final ="
(charset isNil or: [charset size = 0]) ifTrue: [charset _ 'LATIN-1'].
encodedStream _ MultiByteBinaryOrTextStream on: String new encoding: charset.
decoder _ encoding = 'B'
ifTrue: [Base64MimeConverter new]
ifFalse: [RFC2047MimeConverter new].
decoder
mimeStream: (ReadStream on: temp);
dataStream: encodedStream;
mimeDecode.
output nextPutAll: encodedStream reset contents.
pos _ input position.
input skipSeparators.
"Delete spaces if followed by ="
input peek = $=
ifFalse: [input position: pos]]
ifFalse: [output nextPut: $=;
nextPut: temp]].
^ output contents!
----- Method: String>>decodeQuotedPrintable (in category 'internet') -----
decodeQuotedPrintable
"Assume receiver is in MIME 'quoted-printable' encoding, and decode it."
^QuotedPrintableMimeConverter mimeDecode: self as: self class!
----- Method: String>>deepCopy (in category 'copying') -----
deepCopy
"DeepCopy would otherwise mean make a copy of the character; since
characters are unique, just return a shallowCopy."
^self shallowCopy!
----- Method: String>>displayAt: (in category 'displaying') -----
displayAt: aPoint
"Display the receiver as a DisplayText at aPoint on the display screen."
self displayOn: Display at: aPoint!
----- Method: String>>displayOn: (in category 'displaying') -----
displayOn: aDisplayMedium
"Display the receiver on the given DisplayMedium. 5/16/96 sw"
self displayOn: aDisplayMedium at: 0 @ 0!
----- Method: String>>displayOn:at: (in category 'displaying') -----
displayOn: aDisplayMedium at: aPoint
"Show a representation of the receiver as a DisplayText at location aPoint on aDisplayMedium, using black-colored text."
self displayOn: aDisplayMedium at: aPoint textColor: Color black!
----- Method: String>>displayOn:at:textColor: (in category 'displaying') -----
displayOn: aDisplayMedium at: aPoint textColor: aColor
"Show a representation of the receiver as a DisplayText at location aPoint on aDisplayMedium, rendering the text in the designated color"
(self asDisplayText foregroundColor: (aColor ifNil: [Color black]) backgroundColor: Color white)
displayOn: aDisplayMedium at: aPoint!
----- Method: String>>displayProgressAt:from:to:during: (in category 'displaying') -----
displayProgressAt: aPoint from: minVal to: maxVal during: workBlock
"Display this string as a caption over a progress bar while workBlock is evaluated.
EXAMPLE (Select next 6 lines and Do It)
'Now here''s some Real Progress'
displayProgressAt: Sensor cursorPoint
from: 0 to: 10
during: [:bar |
1 to: 10 do: [:x | bar value: x.
(Delay forMilliseconds: 500) wait]].
HOW IT WORKS (Try this in any other language :-)
Since your code (the last 2 lines in the above example) is in a block,
this method gets control to display its heading before, and clean up
the screen after, its execution.
The key, though, is that the block is supplied with an argument,
named 'bar' in the example, which will update the bar image every
it is sent the message value: x, where x is in the from:to: range.
"
^ProgressInitiationException
display: self
at: aPoint
from: minVal
to: maxVal
during: workBlock!
----- Method: String>>do:toFieldNumber: (in category 'accessing') -----
do: aBlock toFieldNumber: aNumber
"Considering the receiver as a holder of tab-delimited fields, evaluate aBlock on behalf of a field in this string"
| start end index |
start _ 1.
index _ 1.
[start <= self size] whileTrue:
[end _ self indexOf: Character tab startingAt: start ifAbsent: [self size + 1].
end _ end - 1.
aNumber = index ifTrue:
[aBlock value: (self copyFrom: start to: end).
^ self].
index _ index + 1.
start _ end + 2]
"
1 to: 6 do:
[:aNumber |
'fred charlie elmo wimpy friml' do:
[:aField | Transcript cr; show: aField] toFieldNumber: aNumber]
"!
----- Method: String>>encodeDoublingQuoteOn: (in category 'printing') -----
encodeDoublingQuoteOn: aStream
"Print inside string quotes, doubling inbedded quotes."
| x |
aStream print: $'.
1 to: self size do:
[:i |
aStream print: (x _ self at: i).
x = $' ifTrue: [aStream print: x]].
aStream print: $'!
----- Method: String>>encodeForHTTP (in category 'converting') -----
encodeForHTTP
"change dangerous characters to their %XX form, for use in HTTP transactions"
^ self encodeForHTTPWithTextEncoding: 'utf-8' conditionBlock: [:c | c isSafeForHTTP].
!
----- Method: String>>encodeForHTTPWithTextEncoding: (in category 'converting') -----
encodeForHTTPWithTextEncoding: encodingName
^ self encodeForHTTPWithTextEncoding: encodingName conditionBlock: [:c | c isSafeForHTTP].
!
----- Method: String>>encodeForHTTPWithTextEncoding:conditionBlock: (in category 'converting') -----
encodeForHTTPWithTextEncoding: encodingName conditionBlock: conditionBlock
"change dangerous characters to their %XX form, for use in HTTP transactions"
| httpSafeStream encodedStream cont |
httpSafeStream _ WriteStream on: (String new).
encodedStream _ MultiByteBinaryOrTextStream on: (String new: 6).
encodedStream converter: (TextConverter newForEncoding: encodingName).
self do: [:c |
(conditionBlock value: c)
ifTrue: [httpSafeStream nextPut: (Character value: c charCode)]
ifFalse: [
encodedStream text; reset.
encodedStream nextPut: c.
encodedStream position: 0.
encodedStream binary.
cont _ encodedStream contents.
cont do: [:byte |
httpSafeStream nextPut: $%.
httpSafeStream nextPut: (byte // 16) asHexDigit.
httpSafeStream nextPut: (byte \\ 16) asHexDigit.
].
].
].
^ httpSafeStream contents.
!
----- Method: String>>endsWith: (in category 'comparing') -----
endsWith: suffix
"Answer whether the tail end of the receiver is the same as suffix.
The comparison is case-sensitive."
| extra |
(extra _ self size - suffix size) < 0 ifTrue: [^ false].
^ (self findSubstring: suffix in: self startingAt: extra + 1
matchTable: CaseSensitiveOrder) > 0
"
'Elvis' endsWith: 'vis'
"!
----- Method: String>>endsWithAColon (in category 'system primitives') -----
endsWithAColon
"Answer whether the final character of the receiver is a colon"
^ self size > 0 and: [self last == $:]
"
#fred: endsWithAColon
'fred' endsWithAColon
"!
----- Method: String>>endsWithAnyOf: (in category 'comparing') -----
endsWithAnyOf: aCollection
aCollection do:[:suffix|
(self endsWith: suffix) ifTrue:[^true].
].
^false!
----- Method: String>>endsWithDigit (in category 'accessing') -----
endsWithDigit
"Answer whether the receiver's final character represents a digit. 3/11/96 sw"
^ self size > 0 and: [self last isDigit]!
----- Method: String>>evaluateExpression:parameters: (in category 'private') -----
evaluateExpression: aString parameters: aCollection
"private - evaluate the expression aString with
aCollection as the parameters and answer the
evaluation result as an string"
| index |
index := ('0' , aString) asNumber.
index isZero
ifTrue: [^ '[invalid subscript: {1}]' format: {aString}].
index > aCollection size
ifTrue: [^ '[subscript is out of bounds: {1}]' format: {aString}].
^ (aCollection at: index) asString!
----- Method: String>>findAnySubStr:startingAt: (in category 'accessing') -----
findAnySubStr: delimiters startingAt: start
"Answer the index of the character within the receiver, starting at start, that begins a substring matching one of the delimiters. delimiters is an Array of Strings (Characters are permitted also). If the receiver does not contain any of the delimiters, answer size + 1."
| min ind |
min _ self size + 1.
delimiters do: [:delim | "May be a char, a string of length 1, or a substring"
delim isCharacter
ifTrue: [ind _ self indexOfSubCollection: (String with: delim)
startingAt: start ifAbsent: [min]]
ifFalse: [ind _ self indexOfSubCollection: delim
startingAt: start ifAbsent: [min]].
min _ min min: ind].
^ min!
----- Method: String>>findBetweenSubStrs: (in category 'accessing') -----
findBetweenSubStrs: delimiters
"Answer the collection of String tokens that result from parsing self. Tokens are separated by 'delimiters', which can be a collection of Strings, or a collection of Characters. Several delimiters in a row are considered as just one separation."
| tokens keyStart keyStop |
tokens _ OrderedCollection new.
keyStop _ 1.
[keyStop <= self size] whileTrue:
[keyStart _ self skipAnySubStr: delimiters startingAt: keyStop.
keyStop _ self findAnySubStr: delimiters startingAt: keyStart.
keyStart < keyStop
ifTrue: [tokens add: (self copyFrom: keyStart to: (keyStop - 1))]].
^tokens!
----- Method: String>>findCloseParenthesisFor: (in category 'accessing') -----
findCloseParenthesisFor: startIndex
"assume (self at: startIndex) is $(. Find the matching $), allowing parentheses to nest."
" '(1+(2-3))-3.14159' findCloseParenthesisFor: 1 "
" '(1+(2-3))-3.14159' findCloseParenthesisFor: 4 "
| pos nestLevel |
pos := startIndex+1.
nestLevel := 1.
[ pos <= self size ] whileTrue: [
(self at: pos) = $( ifTrue: [ nestLevel := nestLevel + 1 ].
(self at: pos) = $) ifTrue: [ nestLevel := nestLevel - 1 ].
nestLevel = 0 ifTrue: [ ^pos ].
pos := pos + 1.
].
^self size + 1!
----- Method: String>>findDelimiters:startingAt: (in category 'accessing') -----
findDelimiters: delimiters startingAt: start
"Answer the index of the character within the receiver, starting at start, that matches one of the delimiters. If the receiver does not contain any of the delimiters, answer size + 1."
start to: self size do: [:i |
delimiters do: [:delim | delim = (self at: i) ifTrue: [^ i]]].
^ self size + 1!
----- Method: String>>findLastOccuranceOfString:startingAt: (in category 'deprecated-3.10') -----
findLastOccuranceOfString: subString startingAt: start
"Answer the index of the last occurance of subString within the receiver, starting at start. If
the receiver does not contain subString, answer 0."
^ self findLastOccurrenceOfString: subString startingAt: start
!
----- Method: String>>findLastOccurrenceOfString:startingAt: (in category 'accessing') -----
findLastOccurrenceOfString: subString startingAt: start
"Answer the index of the last occurrence of subString within the receiver, starting at start. If
the receiver does not contain subString, answer 0. Case-sensitive match used."
| last now |
last _ self findSubstring: subString in: self startingAt: start matchTable: CaseSensitiveOrder.
last = 0 ifTrue: [^ 0].
[last > 0] whileTrue:
[now _ last.
last _ self findSubstring: subString in: self startingAt: last + 1 matchTable: CaseSensitiveOrder].
^ now
"
'ababa' findLastOccurrenceOfString: 'aba' startingAt: 1
'ababababa' findLastOccurrenceOfString: 'aba' startingAt:3
'aaa' findLastOccurrenceOfString: 'aa' startingAt: 1
"
!
----- Method: String>>findSelector (in category 'converting') -----
findSelector
"Dan's code for hunting down selectors with keyword parts; while this doesn't give a true parse, in most cases it does what we want, in where it doesn't, we're none the worse for it."
| sel possibleParens level n |
sel _ self withBlanksTrimmed.
(sel includes: $:) ifTrue:
[sel _ sel copyReplaceAll: ':' with: ': '. "for the style (aa max:bb) with no space"
possibleParens _ sel findTokens: Character separators.
sel _ self class streamContents:
[:s | level _ 0.
possibleParens do:
[:token |
(level = 0 and: [token endsWith: ':'])
ifTrue: [s nextPutAll: token]
ifFalse: [(n _ token occurrencesOf: $( ) > 0 ifTrue: [level _ level + n].
(n _ token occurrencesOf: $[ ) > 0 ifTrue: [level _ level + n].
(n _ token occurrencesOf: $] ) > 0 ifTrue: [level _ level - n].
(n _ token occurrencesOf: $) ) > 0 ifTrue: [level _ level - n]]]]].
sel isEmpty ifTrue: [^ nil].
sel isOctetString ifTrue: [sel _ sel asOctetString].
Symbol hasInterned: sel ifTrue:
[:aSymbol | ^ aSymbol].
^ nil!
----- Method: String>>findString: (in category 'accessing') -----
findString: subString
"Answer the index of subString within the receiver, starting at start. If
the receiver does not contain subString, answer 0."
^self findString: subString startingAt: 1.!
----- Method: String>>findString:startingAt: (in category 'accessing') -----
findString: subString startingAt: start
"Answer the index of subString within the receiver, starting at start. If
the receiver does not contain subString, answer 0."
^ self findSubstring: subString in: self startingAt: start matchTable: CaseSensitiveOrder!
----- Method: String>>findString:startingAt:caseSensitive: (in category 'accessing') -----
findString: key startingAt: start caseSensitive: caseSensitive
"Answer the index in this String at which the substring key first occurs, at or beyond start. The match can be case-sensitive or not. If no match is found, zero will be returned."
caseSensitive
ifTrue: [^ self findSubstring: key in: self startingAt: start matchTable: CaseSensitiveOrder]
ifFalse: [^ self findSubstring: key in: self startingAt: start matchTable: CaseInsensitiveOrder]!
----- Method: String>>findSubstring:in:startingAt:matchTable: (in category 'system primitives') -----
findSubstring: key in: body startingAt: start matchTable: matchTable
"Answer the index in the string body at which the substring key first occurs, at or beyond start. The match is determined using matchTable, which can be used to effect, eg, case-insensitive matches. If no match is found, zero will be returned."
| index c1 c2 c1Index c2Index |
matchTable == nil ifTrue: [
key size = 0 ifTrue: [^ 0].
start to: body size - key size + 1 do:
[:startIndex |
index _ 1.
[(body at: startIndex+index-1)
= (key at: index)]
whileTrue:
[index = key size ifTrue: [^ startIndex].
index _ index+1]].
^ 0
].
key size = 0 ifTrue: [^ 0].
start to: body size - key size + 1 do:
[:startIndex |
index _ 1.
[c1 _ body at: startIndex+index-1.
c2 _ key at: index.
c1Index _ c1 asciiValue + 1.
c2Index _ c2 asciiValue + 1.
((c1 leadingChar = 0) ifTrue: [
c1Index > matchTable size ifTrue: [c1Index] ifFalse: [matchTable at: c1Index]]
ifFalse: [c1Index])
= ((c2 leadingChar = 0) ifTrue: [
c2Index > matchTable size ifTrue: [c2Index] ifFalse: [matchTable at: c2Index]]
ifFalse: [c2Index])]
whileTrue:
[index = key size ifTrue: [^ startIndex].
index _ index+1]].
^ 0
!
----- Method: String>>findTokens: (in category 'accessing') -----
findTokens: delimiters
"Answer the collection of tokens that result from parsing self. Return strings between the delimiters. Any character in the Collection delimiters marks a border. Several delimiters in a row are considered as just one separation. Also, allow delimiters to be a single character."
| tokens keyStart keyStop separators |
tokens _ OrderedCollection new.
separators _ delimiters isCharacter
ifTrue: [Array with: delimiters]
ifFalse: [delimiters].
keyStop _ 1.
[keyStop <= self size] whileTrue:
[keyStart _ self skipDelimiters: separators startingAt: keyStop.
keyStop _ self findDelimiters: separators startingAt: keyStart.
keyStart < keyStop
ifTrue: [tokens add: (self copyFrom: keyStart to: (keyStop - 1))]].
^tokens!
----- Method: String>>findTokens:includes: (in category 'accessing') -----
findTokens: delimiters includes: subString
"Divide self into pieces using delimiters. Return the piece that includes subString anywhere in it. Is case sensitive (say asLowercase to everything beforehand to make insensitive)."
^ (self findTokens: delimiters)
detect: [:str | (str includesSubString: subString)]
ifNone: [nil]!
----- Method: String>>findTokens:keep: (in category 'accessing') -----
findTokens: delimiters keep: keepers
"Answer the collection of tokens that result from parsing self. The tokens are seperated by delimiters, any of a string of characters. If a delimiter is also in keepers, make a token for it. (Very useful for carriage return. A sole return ends a line, but is also saved as a token so you can see where the line breaks were.)"
| tokens keyStart keyStop |
tokens _ OrderedCollection new.
keyStop _ 1.
[keyStop <= self size] whileTrue:
[keyStart _ self skipDelimiters: delimiters startingAt: keyStop.
keyStop to: keyStart-1 do: [:ii |
(keepers includes: (self at: ii)) ifTrue: [
tokens add: (self copyFrom: ii to: ii)]]. "Make this keeper be a token"
keyStop _ self findDelimiters: delimiters startingAt: keyStart.
keyStart < keyStop
ifTrue: [tokens add: (self copyFrom: keyStart to: (keyStop - 1))]].
^tokens!
----- Method: String>>findWordStart:startingAt: (in category 'accessing') -----
findWordStart: key startingAt: start
| ind |
"HyperCard style searching. Answer the index in self of the substring key, when that key is preceeded by a separator character. Must occur at or beyond start. The match is case-insensitive. If no match is found, zero will be returned."
ind _ start.
[ind _ self findSubstring: key in: self startingAt: ind matchTable: CaseInsensitiveOrder.
ind = 0 ifTrue: [^ 0]. "not found"
ind = 1 ifTrue: [^ 1]. "First char is the start of a word"
(self at: ind-1) isSeparator] whileFalse: [ind _ ind + 1].
^ ind "is a word start"!
----- Method: String>>format: (in category 'formatting') -----
format: aCollection
"format the receiver with aCollection
simplest example:
'foo {1} bar' format: {Date today}.
complete example:
'\{ \} \\ foo {1} bar {2}' format: {12. 'string'}.
"
| result stream |
result := String new writeStream.
stream := self readStream.
[stream atEnd]
whileFalse: [| currentChar |
currentChar := stream next.
currentChar == ${
ifTrue: [| expression |
expression := self getEnclosedExpressionFrom: stream.
result
nextPutAll: (self evaluateExpression: expression parameters: aCollection)]
ifFalse: [
currentChar == $\
ifTrue: [stream atEnd
ifFalse: [result nextPut: stream next]]
ifFalse: [result nextPut: currentChar]]].
^ result contents!
----- Method: String>>fromCamelCase (in category 'converting') -----
fromCamelCase
"convert 'anExampleString' to 'an example string'"
| upper nextWord start |
upper := ($A to: $Z) asCharacterSet.
nextWord := self indexOfAnyOf: upper.
nextWord = 0 ifTrue: [^self].
start := 1.
^String streamContents: [:strm |
[
strm nextPutAll: (self copyFrom: start to: nextWord-1).
strm space; nextPut: (self at: nextWord) asLowercase.
start := nextWord+1.
nextWord := self indexOfAnyOf: upper startingAt: start.
nextWord = 0
] whileFalse.
strm nextPutAll: (self copyFrom: start to: self size).
].!
----- Method: String>>getEnclosedExpressionFrom: (in category 'private') -----
getEnclosedExpressionFrom: aStream
"private - get the expression enclosed between '{' and
'}' and remove all the characters from the stream"
| result currentChar |
result := String new writeStream.
[aStream atEnd
or: [(currentChar := aStream next) == $}]]
whileFalse: [result nextPut: currentChar].
^ result contents withBlanksTrimmed!
----- Method: String>>getInteger32: (in category 'encoding') -----
getInteger32: location
| integer |
<primitive: 'getInteger' module: 'IntegerPokerPlugin'>
"^IntegerPokerPlugin doPrimitive: #getInteger"
"the following is about 7x faster than interpreting the plugin if not compiled"
integer :=
((self at: location) asInteger bitShift: 24) +
((self at: location+1) asInteger bitShift: 16) +
((self at: location+2) asInteger bitShift: 8) +
(self at: location+3) asInteger.
integer > 1073741824 ifTrue: [^1073741824 - integer ].
^integer
!
----- Method: String>>hash (in category 'comparing') -----
hash
"#hash is implemented, because #= is implemented"
"ar 4/10/2005: I had to change this to use ByteString hash as initial
hash in order to avoid having to rehash everything and yet compute
the same hash for ByteString and WideString."
^ self class stringHash: self initialHash: ByteString hash!
----- Method: String>>hashMappedBy: (in category 'comparing') -----
hashMappedBy: map
"My hash is independent of my oop."
^self hash!
----- Method: String>>hashWithInitialHash: (in category 'comparing') -----
hashWithInitialHash: initialHash
^ self class stringHash: self initialHash: initialHash!
----- Method: String>>howManyMatch: (in category 'comparing') -----
howManyMatch: string
"Count the number of characters that match up in self and aString."
| count shorterLength |
count _ 0 .
shorterLength _ ((self size ) min: (string size ) ) .
(1 to: shorterLength do: [:index |
(((self at: index ) = (string at: index ) ) ifTrue: [count _ (count + 1 ) .
] ).
] ).
^ count
!
----- Method: String>>includesSubString: (in category 'accessing') -----
includesSubString: subString
^ (self findString: subString startingAt: 1) > 0!
----- Method: String>>includesSubstring:caseSensitive: (in category 'accessing') -----
includesSubstring: aString caseSensitive: caseSensitive
^ (self findString: aString startingAt: 1 caseSensitive: caseSensitive) > 0!
----- Method: String>>includesUnifiedCharacter (in category 'testing') -----
includesUnifiedCharacter
^false!
----- Method: String>>indentationIfBlank: (in category 'paragraph support') -----
indentationIfBlank: aBlock
"Answer the number of leading tabs in the receiver. If there are
no visible characters, pass the number of tabs to aBlock and return its value."
| reader leadingTabs lastSeparator cr tab ch |
cr _ Character cr.
tab _ Character tab.
reader _ ReadStream on: self.
leadingTabs _ 0.
[reader atEnd not and: [(ch _ reader next) = tab]]
whileTrue: [leadingTabs _ leadingTabs + 1].
lastSeparator _ leadingTabs + 1.
[reader atEnd not and: [ch isSeparator and: [ch ~= cr]]]
whileTrue: [lastSeparator _ lastSeparator + 1. ch _ reader next].
lastSeparator = self size | (ch = cr)
ifTrue: [^aBlock value: leadingTabs].
^ leadingTabs.
!
----- Method: String>>indexOf: (in category 'accessing') -----
indexOf: aCharacter
aCharacter isCharacter ifFalse: [^ 0].
^ self class
indexOfAscii: aCharacter asciiValue
inString: self
startingAt: 1.
!
----- Method: String>>indexOf:startingAt: (in category 'accessing') -----
indexOf: aCharacter startingAt: start
(aCharacter isCharacter) ifFalse: [^ 0].
^ self class indexOfAscii: aCharacter asciiValue inString: self startingAt: start!
----- Method: String>>indexOf:startingAt:ifAbsent: (in category 'accessing') -----
indexOf: aCharacter startingAt: start ifAbsent: aBlock
| ans |
(aCharacter isCharacter) ifFalse: [ ^ aBlock value ].
ans _ self class indexOfAscii: aCharacter asciiValue inString: self startingAt: start.
ans = 0
ifTrue: [ ^ aBlock value ]
ifFalse: [ ^ ans ]!
----- Method: String>>indexOfAnyOf: (in category 'accessing') -----
indexOfAnyOf: aCharacterSet
"returns the index of the first character in the given set. Returns 0 if none are found"
^self indexOfAnyOf: aCharacterSet startingAt: 1!
----- Method: String>>indexOfAnyOf:ifAbsent: (in category 'accessing') -----
indexOfAnyOf: aCharacterSet ifAbsent: aBlock
"returns the index of the first character in the given set. Returns the evaluation of aBlock if none are found"
^self indexOfAnyOf: aCharacterSet startingAt: 1 ifAbsent: aBlock!
----- Method: String>>indexOfAnyOf:startingAt: (in category 'accessing') -----
indexOfAnyOf: aCharacterSet startingAt: start
"returns the index of the first character in the given set, starting from start. Returns 0 if none are found"
^self indexOfAnyOf: aCharacterSet startingAt: start ifAbsent: [ 0 ]!
----- Method: String>>indexOfAnyOf:startingAt:ifAbsent: (in category 'accessing') -----
indexOfAnyOf: aCharacterSet startingAt: start ifAbsent: aBlock
"returns the index of the first character in the given set, starting from start"
| ans |
ans _ self class findFirstInString: self inSet: aCharacterSet byteArrayMap startingAt: start.
ans = 0
ifTrue: [ ^aBlock value ]
ifFalse: [ ^ans ]!
----- Method: String>>indexOfSubCollection: (in category 'accessing') -----
indexOfSubCollection: sub
#Collectn.
"Added 2000/04/08 For ANSI <sequenceReadableCollection> protocol."
^ self
indexOfSubCollection: sub
startingAt: 1
ifAbsent: [0]!
----- Method: String>>indexOfSubCollection:startingAt:ifAbsent: (in category 'accessing') -----
indexOfSubCollection: sub startingAt: start ifAbsent: exceptionBlock
| index |
index _ self findSubstring: sub in: self startingAt: start matchTable: CaseSensitiveOrder.
index = 0 ifTrue: [^ exceptionBlock value].
^ index!
----- Method: String>>initialIntegerOrNil (in category 'converting') -----
initialIntegerOrNil
"Answer the integer represented by the leading digits of the receiver, or nil if the receiver does not begin with a digit"
| firstNonDigit |
(self size == 0 or: [self first isDigit not]) ifTrue: [^ nil].
firstNonDigit _ (self findFirst: [:m | m isDigit not]).
firstNonDigit = 0 ifTrue: [firstNonDigit _ self size + 1].
^ (self copyFrom: 1 to: (firstNonDigit - 1)) asNumber
"
'234Whoopie' initialIntegerOrNil
'wimpy' initialIntegerOrNil
'234' initialIntegerOrNil
'2N' initialIntegerOrNil
'2' initialIntegerOrNil
' 89Ten ' initialIntegerOrNil
'78 92' initialIntegerOrNil
"
!
----- Method: String>>isAllDigits (in category 'testing') -----
isAllDigits
"whether the receiver is composed entirely of digits"
self do: [:c | c isDigit ifFalse: [^ false]].
^ true!
----- Method: String>>isAllSeparators (in category 'testing') -----
isAllSeparators
"whether the receiver is composed entirely of separators"
self do: [ :c | c isSeparator ifFalse: [ ^false ] ].
^true!
----- Method: String>>isAsciiString (in category 'testing') -----
isAsciiString
| c |
c _ self detect: [:each | each asciiValue > 127] ifNone: [nil].
^ c isNil.
!
----- Method: String>>isByteString (in category 'testing') -----
isByteString
"Answer whether the receiver is a ByteString"
^false!
----- Method: String>>isLiteral (in category 'printing') -----
isLiteral
^true!
----- Method: String>>isOctetString (in category 'testing') -----
isOctetString
"Answer whether the receiver can be represented as a byte string.
This is different from asking whether the receiver *is* a ByteString
(i.e., #isByteString)"
1 to: self size do: [:pos |
(self at: pos) asInteger >= 256 ifTrue: [^ false].
].
^ true.
!
----- Method: String>>isReallyString (in category 'testing') -----
isReallyString
^ true!
----- Method: String>>isString (in category 'testing') -----
isString
^ true!
----- Method: String>>isWideString (in category 'testing') -----
isWideString
"Answer whether the receiver is a WideString"
^false!
----- Method: String>>isoToSqueak (in category 'internet') -----
isoToSqueak
^self "no longer needed"!
----- Method: String>>isoToUtf8 (in category 'internet') -----
isoToUtf8
"Convert ISO 8559-1 to UTF-8"
| s v |
s _ WriteStream on: (String new: self size).
self do: [:c |
v _ c asciiValue.
(v > 128)
ifFalse: [s nextPut: c]
ifTrue: [
s nextPut: (192+(v >> 6)) asCharacter.
s nextPut: (128+(v bitAnd: 63)) asCharacter]].
^s contents.
!
----- Method: String>>keywords (in category 'converting') -----
keywords
"Answer an array of the keywords that compose the receiver."
| kwd char keywords |
keywords _ Array streamContents:
[:kwds | kwd _ WriteStream on: (String new: 16).
1 to: self size do:
[:i |
kwd nextPut: (char _ self at: i).
char = $: ifTrue:
[kwds nextPut: kwd contents.
kwd reset]].
kwd isEmpty ifFalse: [kwds nextPut: kwd contents]].
(keywords size >= 1 and: [(keywords at: 1) = ':']) ifTrue:
["Has an initial keyword, as in #:if:then:else:"
keywords _ keywords allButFirst].
(keywords size >= 2 and: [(keywords at: keywords size - 1) = ':']) ifTrue:
["Has a final keyword, as in #nextPut::andCR"
keywords _ keywords copyReplaceFrom: keywords size - 1
to: keywords size with: {':' , keywords last}].
^ keywords!
----- Method: String>>lastIndexOfPKSignature: (in category 'accessing') -----
lastIndexOfPKSignature: aSignature
"Answer the last index in me where aSignature (4 bytes long) occurs, or 0 if not found"
| a b c d |
a _ aSignature first.
b _ aSignature second.
c _ aSignature third.
d _ aSignature fourth.
(self size - 3) to: 1 by: -1 do: [ :i |
(((self at: i) = a)
and: [ ((self at: i + 1) = b)
and: [ ((self at: i + 2) = c)
and: [ ((self at: i + 3) = d) ]]])
ifTrue: [ ^i ]
].
^0!
----- Method: String>>lastSpacePosition (in category 'testing') -----
lastSpacePosition
"Answer the character position of the final space or other separator character in the receiver, and 0 if none"
self size to: 1 by: -1 do:
[:i | ((self at: i) isSeparator) ifTrue: [^ i]].
^ 0
"
'fred the bear' lastSpacePosition
'ziggie' lastSpacePosition
'elvis ' lastSpacePosition
'wimpy ' lastSpacePosition
'' lastSpacePosition
"!
----- Method: String>>leadingCharRunLengthAt: (in category 'accessing') -----
leadingCharRunLengthAt: index
| leadingChar |
leadingChar _ (self at: index) leadingChar.
index to: self size do: [:i |
(self at: i) leadingChar ~= leadingChar ifTrue: [^ i - index].
].
^ self size - index + 1.
!
----- Method: String>>lineCorrespondingToIndex: (in category 'accessing') -----
lineCorrespondingToIndex: anIndex
"Answer a string containing the line at the given character position. 1/15/96 sw: Inefficient first stab at this"
| cr aChar answer |
cr _ Character cr.
answer _ ''.
1 to: self size do:
[:i |
aChar _ self at: i.
aChar = cr
ifTrue:
[i > anIndex
ifTrue:
[^ answer]
ifFalse:
[answer _ '']]
ifFalse:
[answer _ answer copyWith: aChar]].
^ answer!
----- Method: String>>lineCount (in category 'accessing') -----
lineCount
"Answer the number of lines represented by the receiver, where every cr adds one line. 5/10/96 sw"
| cr count |
cr _ Character cr.
count _ 1 min: self size..
1 to: self size do:
[:i | (self at: i) = cr ifTrue: [count _ count + 1]].
^ count
"
'Fred
the
Bear' lineCount
"!
----- Method: String>>lineNumber: (in category 'accessing') -----
lineNumber: anIndex
"Answer a string containing the characters in the given line number. 5/10/96 sw"
| crString pos finalPos |
crString _ String with: Character cr.
pos _ 0.
1 to: anIndex - 1 do:
[:i | pos _ self findString: crString startingAt: pos + 1.
pos = 0 ifTrue: [^ nil]].
finalPos _ self findString: crString startingAt: pos + 1.
finalPos = 0 ifTrue: [finalPos _ self size + 1].
^ self copyFrom: pos + 1 to: finalPos - 1
"
'Fred
the
Bear' lineNumber: 3
"!
----- Method: String>>linesDo: (in category 'accessing') -----
linesDo: aBlock
"execute aBlock with each line in this string. The terminating CR's are not included in what is passed to aBlock"
| start end |
start _ 1.
[ start <= self size ] whileTrue: [
end _ self indexOf: Character cr startingAt: start ifAbsent: [ self size + 1 ].
end _ end - 1.
aBlock value: (self copyFrom: start to: end).
start _ end + 2. ].!
----- Method: String>>macToSqueak (in category 'internet') -----
macToSqueak
"Convert the receiver from MacRoman to Squeak encoding"
^ self collect: [:each | each macToSqueak]!
----- Method: String>>match: (in category 'comparing') -----
match: text
"Answer whether text matches the pattern in this string.
Matching ignores upper/lower case differences.
Where this string contains #, text may contain any character.
Where this string contains *, text may contain any sequence of characters."
^ self startingAt: 1 match: text startingAt: 1
"
'*' match: 'zort' true
'*baz' match: 'mobaz' true
'*baz' match: 'mobazo' false
'*baz*' match: 'mobazo' true
'*baz*' match: 'mozo' false
'foo*' match: 'foozo' true
'foo*' match: 'bozo' false
'foo*baz' match: 'foo23baz' true
'foo*baz' match: 'foobaz' true
'foo*baz' match: 'foo23bazo' false
'foo' match: 'Foo' true
'foo*baz*zort' match: 'foobazort' false
'foo*baz*zort' match: 'foobazzort' false
'*foo#zort' match: 'afoo3zortthenfoo3zort' true
'*foo*zort' match: 'afoodezortorfoo3zort' true
"!
----- Method: String>>numArgs (in category 'system primitives') -----
numArgs
"Answer either the number of arguments that the receiver would take if considered a selector. Answer -1 if it couldn't be a selector. Note that currently this will answer -1 for anything begining with an uppercase letter even though the system will accept such symbols as selectors. It is intended mostly for the assistance of spelling correction."
| firstChar numColons excess start ix |
self size = 0 ifTrue: [^ -1].
firstChar _ self at: 1.
(firstChar isLetter or: [firstChar = $:]) ifTrue:
["Fast reject if any chars are non-alphanumeric"
(self findSubstring: '~' in: self startingAt: 1 matchTable: Tokenish) > 0 ifTrue: [^ -1].
"Fast colon count"
numColons _ 0. start _ 1.
[(ix _ self findSubstring: ':' in: self startingAt: start matchTable: CaseSensitiveOrder) > 0]
whileTrue:
[numColons _ numColons + 1.
start _ ix + 1].
numColons = 0 ifTrue: [^ 0].
firstChar = $:
ifTrue: [excess _ 2 "Has an initial keyword, as #:if:then:else:"]
ifFalse: [excess _ 0].
self last = $:
ifTrue: [^ numColons - excess]
ifFalse: [^ numColons - excess - 1 "Has a final keywords as #nextPut::andCR"]].
firstChar isSpecial ifTrue:
[self size = 1 ifTrue: [^ 1].
2 to: self size do: [:i | (self at: i) isSpecial ifFalse: [^ -1]].
^ 1].
^ -1.!
----- Method: String>>numericSuffix (in category 'converting') -----
numericSuffix
^ self stemAndNumericSuffix last
"
'abc98' numericSuffix
'98abc' numericSuffix
"!
----- Method: String>>onlyLetters (in category 'converting') -----
onlyLetters
"answer the receiver with only letters"
^ self select:[:each | each isLetter]!
----- Method: String>>openInWorkspaceWithTitle: (in category 'user interface') -----
openInWorkspaceWithTitle: aTitle
"Open up a workspace with the receiver as its contents, with the given title"
(Workspace new contents: self) openLabel: aTitle!
----- Method: String>>padded:to:with: (in category 'copying') -----
padded: leftOrRight to: length with: char
leftOrRight = #left ifTrue:
[^ (String new: (length - self size max: 0) withAll: char) , self].
leftOrRight = #right ifTrue:
[^ self , (String new: (length - self size max: 0) withAll: char)].!
----- Method: String>>passwordFor: (in category 'password compatibility') -----
passwordFor: aServerDir
^ self.
!
----- Method: String>>printOn: (in category 'printing') -----
printOn: aStream
"Print inside string quotes, doubling inbedded quotes."
self storeOn: aStream!
----- Method: String>>putInteger32:at: (in category 'encoding') -----
putInteger32: anInteger at: location
| integer |
<primitive: 'putInteger' module: 'IntegerPokerPlugin'>
"IntegerPokerPlugin doPrimitive: #putInteger"
"the following is close to 20x faster than the above if the primitive is not compiled"
"PUTCOUNTER _ PUTCOUNTER + 1."
integer _ anInteger.
integer < 0 ifTrue: [integer := 1073741824 - integer. ].
self at: location+3 put: (Character value: (integer \\ 256)).
self at: location+2 put: (Character value: (integer bitShift: -8) \\ 256).
self at: location+1 put: (Character value: (integer bitShift: -16) \\ 256).
self at: location put: (Character value: (integer bitShift: -24) \\ 256).
"Smalltalk at: #PUTCOUNTER put: 0"!
----- Method: String>>putOn: (in category 'filter streaming') -----
putOn:aStream
^aStream nextPutAll: self.
!
----- Method: String>>replaceFrom:to:with:startingAt: (in category 'private') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
"Primitive. This destructively replaces elements from start to stop in the receiver starting at index, repStart, in the collection, replacement. Answer the receiver. Range checks are performed in the primitive only. Optional. See Object documentation whatIsAPrimitive."
<primitive: 105>
super replaceFrom: start to: stop with: replacement startingAt: repStart!
----- Method: String>>romanNumber (in category 'converting') -----
romanNumber
| value v1 v2 |
value _ v1 _ v2 _ 0.
self reverseDo:
[:each |
v1 _ #(1 5 10 50 100 500 1000) at: ('IVXLCDM' indexOf: each).
v1 >= v2
ifTrue: [value _ value + v1]
ifFalse: [value _ value - v1].
v2 _ v1].
^ value!
----- Method: String>>sameAs: (in category 'comparing') -----
sameAs: aString
"Answer whether the receiver sorts equal to aString. The
collation sequence is ascii with case differences ignored."
^(self compare: aString caseSensitive: false) = 2!
----- Method: String>>sansPeriodSuffix (in category 'converting') -----
sansPeriodSuffix
"Return a copy of the receiver up to, but not including, the first period. If the receiver's *first* character is a period, then just return the entire receiver. "
| likely |
likely _ self copyUpTo: $..
^ likely size == 0
ifTrue: [self]
ifFalse: [likely]!
----- Method: String>>shallowCopy (in category 'copying') -----
shallowCopy
^ self clone.
!
----- Method: String>>skipAnySubStr:startingAt: (in category 'accessing') -----
skipAnySubStr: delimiters startingAt: start
"Answer the index of the last character within the receiver, starting at start, that does NOT match one of the delimiters. delimiters is a Array of substrings (Characters also allowed). If the receiver is all delimiters, answer size + 1."
| any this ind ii |
ii _ start-1.
[(ii _ ii + 1) <= self size] whileTrue: [ "look for char that does not match"
any _ false.
delimiters do: [:delim |
delim isCharacter
ifTrue: [(self at: ii) == delim ifTrue: [any _ true]]
ifFalse: ["a substring"
delim size > (self size - ii + 1) ifFalse: "Here's where the one-off error was."
[ind _ 0.
this _ true.
delim do: [:dd |
dd == (self at: ii+ind) ifFalse: [this _ false].
ind _ ind + 1].
this ifTrue: [ii _ ii + delim size - 1. any _ true]]
ifTrue: [any _ false] "if the delim is too big, it can't match"]].
any ifFalse: [^ ii]].
^ self size + 1!
----- Method: String>>skipDelimiters:startingAt: (in category 'accessing') -----
skipDelimiters: delimiters startingAt: start
"Answer the index of the character within the receiver, starting at start, that does NOT match one of the delimiters. If the receiver does not contain any of the delimiters, answer size + 1. Assumes the delimiters to be a non-empty string."
start to: self size do: [:i |
delimiters detect: [:delim | delim = (self at: i)]
ifNone: [^ i]].
^ self size + 1!
----- Method: String>>splitInteger (in category 'converting') -----
splitInteger
"Answer an array that is a splitting of self into a string and an integer.
'43Sam' ==> #(43 'Sam'). 'Try90' ==> #('Try' 90)
BUT NOTE: 'Sam' ==> #('Sam' 0), and '90' ==> #('' 90) ie, (<string> <integer>)."
| pos |
(pos _ self findFirst: [:d | d isDigit not]) = 0 ifTrue: [^ Array with: '' with: self asNumber].
self first isDigit ifTrue: [
^ Array with: (self copyFrom: 1 to: pos - 1) asNumber
with: (self copyFrom: pos to: self size)].
(pos _ self findFirst: [:d | d isDigit]) = 0 ifTrue: [^ Array with: self with: 0].
^ Array with: (self copyFrom: 1 to: pos - 1)
with: (self copyFrom: pos to: self size) asNumber!
----- Method: String>>squeakToIso (in category 'internet') -----
squeakToIso
^self "no longer needed"!
----- Method: String>>squeakToMac (in category 'internet') -----
squeakToMac
"Convert the receiver from Squeak to MacRoman encoding"
^ self collect: [:each | each squeakToMac]!
----- Method: String>>squeakToUtf8 (in category 'converting') -----
squeakToUtf8
"Convert the receiver into a UTF8-encoded string"
^self convertToWithConverter: UTF8TextConverter new.!
----- Method: String>>startingAt:match:startingAt: (in category 'comparing') -----
startingAt: keyStart match: text startingAt: textStart
"Answer whether text matches the pattern in this string.
Matching ignores upper/lower case differences.
Where this string contains #, text may contain any character.
Where this string contains *, text may contain any sequence of characters."
| anyMatch matchStart matchEnd i matchStr j ii jj |
i _ keyStart.
j _ textStart.
"Check for any #'s"
[i > self size ifTrue: [^ j > text size "Empty key matches only empty string"].
(self at: i) = $#] whileTrue:
["# consumes one char of key and one char of text"
j > text size ifTrue: [^ false "no more text"].
i _ i+1. j _ j+1].
"Then check for *"
(self at: i) = $*
ifTrue: [i = self size ifTrue:
[^ true "Terminal * matches all"].
"* means next match string can occur anywhere"
anyMatch _ true.
matchStart _ i + 1]
ifFalse: ["Otherwise match string must occur immediately"
anyMatch _ false.
matchStart _ i].
"Now determine the match string"
matchEnd _ self size.
(ii _ self indexOf: $* startingAt: matchStart) > 0 ifTrue:
[ii = 1 ifTrue: [self error: '** not valid -- use * instead'].
matchEnd _ ii-1].
(ii _ self indexOf: $# startingAt: matchStart) > 0 ifTrue:
[ii = 1 ifTrue: [self error: '*# not valid -- use #* instead'].
matchEnd _ matchEnd min: ii-1].
matchStr _ self copyFrom: matchStart to: matchEnd.
"Now look for the match string"
[jj _ text findString: matchStr startingAt: j caseSensitive: false.
anyMatch ifTrue: [jj > 0] ifFalse: [jj = j]]
whileTrue:
["Found matchStr at jj. See if the rest matches..."
(self startingAt: matchEnd+1 match: text startingAt: jj + matchStr size) ifTrue:
[^ true "the rest matches -- success"].
"The rest did not match."
anyMatch ifFalse: [^ false].
"Preceded by * -- try for a later match"
j _ j+1].
^ false "Failed to find the match string"!
----- Method: String>>startsWithDigit (in category 'accessing') -----
startsWithDigit
"Answer whether the receiver's first character represents a digit"
^ self size > 0 and: [self first isDigit]!
----- Method: String>>stemAndNumericSuffix (in category 'converting') -----
stemAndNumericSuffix
"Parse the receiver into a string-valued stem and a numeric-valued suffix. 6/7/96 sw"
| stem suffix position |
stem _ self.
suffix _ 0.
position _ 1.
[stem endsWithDigit and: [stem size > 1]] whileTrue:
[suffix _ stem last digitValue * position + suffix.
position _ position * 10.
stem _ stem copyFrom: 1 to: stem size - 1].
^ Array with: stem with: suffix
"'Fred2305' stemAndNumericSuffix"!
----- Method: String>>storeOn: (in category 'printing') -----
storeOn: aStream
"Print inside string quotes, doubling inbedded quotes."
| x |
aStream nextPut: $'.
1 to: self size do:
[:i |
aStream nextPut: (x _ self at: i).
x = $' ifTrue: [aStream nextPut: x]].
aStream nextPut: $'!
----- Method: String>>stringRepresentation (in category 'printing') -----
stringRepresentation
"Answer a string that represents the receiver. For most objects this is simply its printString, but for strings themselves, it's themselves, to avoid the superfluous extra pair of quotes. 6/12/96 sw"
^ self !
----- Method: String>>stringhash (in category 'private') -----
stringhash
^ self hash.
!
----- Method: String>>subStrings (in category 'converting') -----
subStrings
"Answer an array of the substrings that compose the receiver."
#Collectn.
"Added 2000/04/08 For ANSI <readableString> protocol."
^ self substrings!
----- Method: String>>subStrings: (in category 'converting') -----
subStrings: separators
"Answer an array containing the substrings in the receiver separated
by the elements of separators."
| char result sourceStream subString |
#Collectn.
"Changed 2000/04/08 For ANSI <readableString> protocol."
(separators isString or:[separators allSatisfy: [:element | element isKindOf: Character]])
ifFalse: [^ self error: 'separators must be Characters.'].
sourceStream := ReadStream on: self.
result := OrderedCollection new.
subString := String new.
[sourceStream atEnd]
whileFalse:
[char := sourceStream next.
(separators includes: char)
ifTrue: [subString notEmpty
ifTrue:
[result add: subString copy.
subString := String new]]
ifFalse: [subString := subString , (String with: char)]].
subString notEmpty ifTrue: [result add: subString copy].
^ result asArray!
----- Method: String>>substrings (in category 'converting') -----
substrings
"Answer an array of the substrings that compose the receiver."
| result end beginning |
result _ WriteStream on: (Array new: 10).
end _ 0.
"find one substring each time through this loop"
[
"find the beginning of the next substring"
beginning _ self indexOfAnyOf: CSNonSeparators startingAt: end+1 ifAbsent: [ nil ].
beginning ~~ nil ]
whileTrue: [
"find the end"
end _ self indexOfAnyOf: CSSeparators startingAt: beginning ifAbsent: [ self size + 1 ].
end _ end - 1.
result nextPut: (self copyFrom: beginning to: end).
].
^result contents!
----- Method: String>>sunitAsSymbol (in category 'Camp Smalltalk') -----
sunitAsSymbol
^self asSymbol!
----- Method: String>>sunitMatch: (in category 'Camp Smalltalk') -----
sunitMatch: aString
^(self match: aString)
and: [aString numArgs = 0]!
----- Method: String>>sunitSubStrings (in category 'Camp Smalltalk') -----
sunitSubStrings
^self substrings!
----- Method: String>>surroundedBySingleQuotes (in category 'converting') -----
surroundedBySingleQuotes
"Answer the receiver with leading and trailing quotes. "
^ $' asString, self, $' asString!
----- Method: String>>tabDelimitedFieldsDo: (in category 'accessing') -----
tabDelimitedFieldsDo: aBlock
"Considering the receiver as a holder of tab-delimited fields, evaluate execute aBlock with each field in this string. The separatilng tabs are not included in what is passed to aBlock"
| start end |
"No senders but was useful enough in earlier work that it's retained for the moment."
start _ 1.
[start <= self size] whileTrue:
[end _ self indexOf: Character tab startingAt: start ifAbsent: [self size + 1].
end _ end - 1.
aBlock value: (self copyFrom: start to: end).
start _ end + 2]
"
'fred charlie elmo 2' tabDelimitedFieldsDo: [:aField | Transcript cr; show: aField]
"!
----- Method: String>>toCamelCase (in category 'converting') -----
toCamelCase
"convert 'an example string' to 'anExampleString'"
(self includes: Character space) ifFalse: [^self].
^String streamContents: [:strm |
| space start |
space := self indexOf: Character space.
strm nextPutAll: (self copyFrom: 1 to: space-1).
[ [start := space+1.
space := self indexOf: Character space startingAt: start.
space = start] whileTrue.
space = 0 ifTrue: [space := self size+1].
start <= self size ifTrue: [
strm nextPut: (self at: start) asUppercase.
strm nextPutAll: (self copyFrom: start+1 to: space-1)].
space < self size
] whileTrue].!
----- Method: String>>translateFrom:to:table: (in category 'converting') -----
translateFrom: start to: stop table: table
"translate the characters in the string by the given table, in place"
self class translate: self from: start to: stop table: table!
----- Method: String>>translateToLowercase (in category 'converting') -----
translateToLowercase
"Translate all characters to lowercase, in place"
self translateWith: LowercasingTable!
----- Method: String>>translateToUppercase (in category 'converting') -----
translateToUppercase
"Translate all characters to lowercase, in place"
self translateWith: UppercasingTable!
----- Method: String>>translateWith: (in category 'converting') -----
translateWith: table
"translate the characters in the string by the given table, in place"
^ self translateFrom: 1 to: self size table: table!
----- Method: String>>truncateTo: (in category 'converting') -----
truncateTo: smallSize
"return myself or a copy shortened to smallSize. 1/18/96 sw"
^ self size <= smallSize
ifTrue:
[self]
ifFalse:
[self copyFrom: 1 to: smallSize]!
----- Method: String>>truncateWithElipsisTo: (in category 'converting') -----
truncateWithElipsisTo: maxLength
"Return myself or a copy suitably shortened but with elipsis added"
^ self size <= maxLength
ifTrue:
[self]
ifFalse:
[(self copyFrom: 1 to: (maxLength - 3)), '...']
"'truncateWithElipsisTo:' truncateWithElipsisTo: 20"!
----- Method: String>>unescapePercents (in category 'converting') -----
unescapePercents
"decode %xx form. This is the opposite of #encodeForHTTP"
^ self unescapePercentsWithTextEncoding: 'utf-8'.!
----- Method: String>>unescapePercentsWithTextEncoding: (in category 'converting') -----
unescapePercentsWithTextEncoding: encodingName
"decode string including %XX form"
| ans c asciiVal pos oldPos specialChars encodedStream converter |
encodedStream _ ReadWriteStream on: String new.
converter _ TextConverter newForEncoding: encodingName.
ans _ WriteStream on: String new.
oldPos _ 1.
specialChars _ '+%' asCharacterSet.
[pos _ self indexOfAnyOf: specialChars startingAt: oldPos. pos > 0]
whileTrue: [
ans nextPutAll: (self copyFrom: oldPos to: pos - 1).
c _ self at: pos.
(c = $% and: [pos + 2 <= self size])
ifTrue: [
encodedStream reset.
[c = $% ] whileTrue: [
asciiVal _ (self at: pos+1) asUppercase digitValue * 16 +
(self at: pos+2) asUppercase digitValue.
asciiVal > 255 ifTrue: [^self]. "not really an escaped string".
encodedStream nextPut: (Character value: asciiVal).
pos _ pos + 3.
(pos <= self size)
ifTrue: [c _ self at: pos]
ifFalse: [c _ nil].
].
encodedStream position: 0.
[(c _ converter nextFromStream: encodedStream) notNil ] whileTrue: [
ans nextPut: c].
oldPos _ pos.
]
ifFalse: [ans nextPut: c.
oldPos _ pos + 1
].
].
(oldPos <= self size) ifTrue: [
ans nextPutAll: (self copyFrom: oldPos to: self size) ].
^ ans contents!
----- Method: String>>unparenthetically (in category 'converting') -----
unparenthetically
"If the receiver starts with (..( and ends with matching )..), strip them"
| curr |
curr _ self.
[((curr first = $() and: [curr last = $)])] whileTrue:
[curr _ curr copyFrom: 2 to: (curr size - 1)].
^ curr
"
'((fred the bear))' unparenthetically
"
!
----- Method: String>>unzipped (in category 'converting') -----
unzipped
| magic1 magic2 |
magic1 _ (self at: 1) asInteger.
magic2 _ (self at: 2) asInteger.
(magic1 = 16r1F and:[magic2 = 16r8B]) ifFalse:[^self].
^(GZipReadStream on: self) upToEnd!
----- Method: String>>utf8ToIso (in category 'internet') -----
utf8ToIso
"Only UTF-8 characters that maps to 8-bit ISO-8559-1 values are converted. Others raises an error"
| s i c v c2 v2 |
s _ WriteStream on: (String new: self size).
i _ 1.
[i <= self size] whileTrue: [
c _ self at: i. i_i+1.
v _ c asciiValue.
(v > 128)
ifFalse: [ s nextPut: c ]
ifTrue: [((v bitAnd: 252) == 192)
ifFalse: [self error: 'illegal UTF-8 ISO character']
ifTrue: [
(i > self size) ifTrue: [ self error: 'illegal end-of-string, expected 2nd byte of UTF-8'].
c2 _ self at: i. i_i+1.
v2 _ c2 asciiValue.
((v2 bitAnd: 192) = 128) ifFalse: [self error: 'illegal 2nd UTF-8 char'].
s nextPut: ((v2 bitAnd: 63) bitOr: ((v << 6) bitAnd: 192)) asCharacter]]].
^s contents.
!
----- Method: String>>utf8ToSqueak (in category 'converting') -----
utf8ToSqueak
"Convert the receiver from a UTF8-encoded string"
^self convertFromWithConverter: UTF8TextConverter new.!
----- Method: String>>withBlanksCondensed (in category 'converting') -----
withBlanksCondensed
"Return a copy of the receiver with leading/trailing blanks removed
and consecutive white spaces condensed."
| trimmed lastBlank |
trimmed _ self withBlanksTrimmed.
^String streamContents: [:stream |
lastBlank _ false.
trimmed do: [:c | (c isSeparator and: [lastBlank]) ifFalse: [stream nextPut: c].
lastBlank _ c isSeparator]].
" ' abc d ' withBlanksCondensed"
!
----- Method: String>>withBlanksTrimmed (in category 'converting') -----
withBlanksTrimmed
"Return a copy of the receiver from which leading and trailing blanks have been trimmed."
| first result |
first _ self findFirst: [:c | c isSeparator not].
first = 0 ifTrue: [^ '']. "no non-separator character"
result _ self
copyFrom: first
to: (self findLast: [:c | c isSeparator not]).
result isOctetString ifTrue: [^ result asOctetString] ifFalse: [^ result].
" ' abc d ' withBlanksTrimmed"
!
----- Method: String>>withCRs (in category 'formatting') -----
withCRs
"Return a copy of the receiver in which backslash (\) characters have been replaced with carriage returns."
^ self collect: [ :c | c = $\ ifTrue: [ Character cr ] ifFalse: [ c ]].!
----- Method: String>>withFirstCharacterDownshifted (in category 'converting') -----
withFirstCharacterDownshifted
"Return a copy with the first letter downShifted"
| answer |
self ifEmpty: [^ self copy].
answer _ self copy.
answer at: 1 put: (answer at: 1) asLowercase.
^ answer. !
----- Method: String>>withInternetLineEndings (in category 'internet') -----
withInternetLineEndings
"change line endings from CR's to CRLF's. This is probably in
prepration for sending a string over the Internet"
| cr lf |
cr _ Character cr.
lf _ Character linefeed.
^self class streamContents: [ :stream |
self do: [ :c |
stream nextPut: c.
c = cr ifTrue:[ stream nextPut: lf ]. ] ].!
----- Method: String>>withNoLineLongerThan: (in category 'converting') -----
withNoLineLongerThan: aNumber
"Answer a string with the same content as receiver, but rewrapped so that no line has more characters than the given number"
| listOfLines currentLast currentStart resultString putativeLast putativeLine crPosition |
aNumber isNumber not | (aNumber < 1) ifTrue: [self error: 'too narrow'].
listOfLines _ OrderedCollection new.
currentLast _ 0.
[currentLast < self size] whileTrue:
[currentStart _ currentLast + 1.
putativeLast _ (currentStart + aNumber - 1) min: self size.
putativeLine _ self copyFrom: currentStart to: putativeLast.
(crPosition _ putativeLine indexOf: Character cr) > 0 ifTrue:
[putativeLast _ currentStart + crPosition - 1.
putativeLine _ self copyFrom: currentStart to: putativeLast].
currentLast _ putativeLast == self size
ifTrue:
[putativeLast]
ifFalse:
[currentStart + putativeLine lastSpacePosition - 1].
currentLast <= currentStart ifTrue:
["line has NO spaces; baleout!!"
currentLast _ putativeLast].
listOfLines add: (self copyFrom: currentStart to: currentLast) withBlanksTrimmed].
listOfLines size > 0 ifFalse: [^ ''].
resultString _ listOfLines first.
2 to: listOfLines size do:
[:i | resultString _ resultString, String cr, (listOfLines at: i)].
^ resultString
"#(5 7 20) collect:
[:i | 'Fred the bear went down to the brook to read his book in silence' withNoLineLongerThan: i]"!
----- Method: String>>withSeparatorsCompacted (in category 'converting') -----
withSeparatorsCompacted
"replace each sequences of whitespace by a single space character"
"' test ' withSeparatorsCompacted = ' test '"
"' test test' withSeparatorsCompacted = ' test test'"
"'test test ' withSeparatorsCompacted = 'test test '"
| out in next isSeparator |
self isEmpty ifTrue: [^ self].
out _ WriteStream on: (String new: self size).
in _ self readStream.
isSeparator _ [:char | char asciiValue < 256
and: [CSSeparators includes: char]].
[in atEnd] whileFalse: [
next _ in next.
(isSeparator value: next)
ifTrue: [
out nextPut: $ .
[in atEnd or:
[next _ in next.
(isSeparator value: next)
ifTrue: [false]
ifFalse: [out nextPut: next. true]]] whileFalse]
ifFalse: [out nextPut: next]].
^ out contents!
----- Method: String>>withSqueakLineEndings (in category 'internet') -----
withSqueakLineEndings
"assume the string is textual, and that CR, LF, and CRLF are all
valid line endings. Replace each occurence with a single CR"
| cr lf input c crlf inPos outPos outString lineEndPos newOutPos |
cr _ Character cr.
lf _ Character linefeed.
crlf _ CharacterSet new.
crlf add: cr; add: lf.
inPos _ 1.
outPos _ 1.
outString _
String new: self size.
[ lineEndPos _ self indexOfAnyOf: crlf startingAt: inPos ifAbsent: [0].
lineEndPos ~= 0 ] whileTrue: [
newOutPos _ outPos + (lineEndPos - inPos + 1).
outString replaceFrom: outPos to: newOutPos - 2 with: self startingAt: inPos.
outString at: newOutPos-1 put: cr.
outPos _ newOutPos.
((self at: lineEndPos) = cr and: [ lineEndPos < self size and: [ (self at: lineEndPos+1) = lf ] ]) ifTrue: [
"CRLF ending"
inPos _ lineEndPos + 2 ]
ifFalse: [
"CR or LF ending"
inPos _ lineEndPos + 1 ]. ].
"no more line endings. copy the rest"
newOutPos _ outPos + (self size - inPos + 1).
outString replaceFrom: outPos to: newOutPos-1 with: self startingAt: inPos.
^outString copyFrom: 1 to: newOutPos-1
!
----- Method: String>>withoutLeadingDigits (in category 'converting') -----
withoutLeadingDigits
"Answer the portion of the receiver that follows any leading series of digits and blanks. If the receiver consists entirely of digits and blanks, return an empty string"
| firstNonDigit |
firstNonDigit _ (self findFirst: [:m | m isDigit not and: [m ~= $ ]]).
^ firstNonDigit > 0
ifTrue:
[self copyFrom: firstNonDigit to: self size]
ifFalse:
['']
"
'234Whoopie' withoutLeadingDigits
' 4321 BlastOff!!' withoutLeadingDigits
'wimpy' withoutLeadingDigits
' 89Ten ' withoutLeadingDigits
'78 92' withoutLeadingDigits
"
!
----- Method: String>>withoutQuoting (in category 'internet') -----
withoutQuoting
"remove the initial and final quote marks, if present"
"'''h''' withoutQuoting"
| quote |
self size < 2 ifTrue: [ ^self ].
quote _ self first.
(quote = $' or: [ quote = $" ])
ifTrue: [ ^self copyFrom: 2 to: self size - 1 ]
ifFalse: [ ^self ].!
----- Method: String>>withoutTrailingBlanks (in category 'converting') -----
withoutTrailingBlanks
"Return a copy of the receiver from which trailing blanks have been trimmed."
| last |
last _ self findLast: [:c | c isSeparator not].
last = 0 ifTrue: [^ '']. "no non-separator character"
^ self copyFrom: 1 to: last
" ' abc d ' withoutTrailingBlanks"
!
----- Method: String>>withoutTrailingDigits (in category 'converting') -----
withoutTrailingDigits
"Answer the portion of the receiver that precedes any trailing series of digits and blanks. If the receiver consists entirely of digits and blanks, return an empty string"
| firstDigit |
firstDigit _ (self findFirst: [:m | m isDigit or: [m = $ ]]).
^ firstDigit > 0
ifTrue:
[self copyFrom: 1 to: firstDigit-1]
ifFalse:
[self]
"
'Whoopie234' withoutTrailingDigits
' 4321 BlastOff!!' withoutLeadingDigits
'wimpy' withoutLeadingDigits
' 89Ten ' withoutLeadingDigits
'78 92' withoutLeadingDigits
"
!
----- Method: String>>writeLeadingCharRunsOn: (in category 'encoding') -----
writeLeadingCharRunsOn: stream
| runLength runValues runStart leadingChar |
self isEmpty ifTrue: [^ self].
runLength _ OrderedCollection new.
runValues _ OrderedCollection new.
runStart _ 1.
leadingChar _ (self at: runStart) leadingChar.
2 to: self size do: [:index |
(self at: index) leadingChar = leadingChar ifFalse: [
runValues add: leadingChar.
runLength add: (index - runStart).
leadingChar _ (self at: index) leadingChar.
runStart _ index.
].
].
runValues add: (self last) leadingChar.
runLength add: self size + 1 - runStart.
stream nextPut: $(.
runLength do: [:rr | rr printOn: stream. stream space].
stream skip: -1; nextPut: $).
runValues do: [:vv | vv printOn: stream. stream nextPut: $,].
stream skip: -1.
!
String subclass: #Symbol
instanceVariableNames: ''
classVariableNames: 'NewSymbols OneCharacterSymbols SymbolTable'
poolDictionaries: ''
category: 'Collections-Strings'!
!Symbol commentStamp: '<historical>' prior: 0!
I represent Strings that are created uniquely. Thus, someString asSymbol == someString asSymbol.!
Symbol variableByteSubclass: #ByteSymbol
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Strings'!
!ByteSymbol commentStamp: '<historical>' prior: 0!
This class represents the symbols containing 8bit characters.!
----- Method: ByteSymbol class>>findFirstInString:inSet:startingAt: (in category 'primitives') -----
findFirstInString: aString inSet: inclusionMap startingAt: start
^ByteString findFirstInString: aString inSet: inclusionMap startingAt: start!
----- Method: ByteSymbol class>>indexOfAscii:inString:startingAt: (in category 'primitives') -----
indexOfAscii: anInteger inString: aString startingAt: start
^ByteString indexOfAscii: anInteger inString: aString startingAt: start!
----- Method: ByteSymbol class>>stringHash:initialHash: (in category 'primitives') -----
stringHash: aString initialHash: speciesHash
<primitive: 'primitiveStringHash' module: 'MiscPrimitivePlugin'>
^ ByteString class stringHash: aString initialHash: speciesHash.
!
----- Method: ByteSymbol class>>translate:from:to:table: (in category 'primitives') -----
translate: aString from: start to: stop table: table
^ByteString translate: aString from: start to: stop table: table!
----- Method: ByteSymbol>>asByteArray (in category 'converting') -----
asByteArray
| ba sz |
sz := self byteSize.
ba := ByteArray new: sz.
ba replaceFrom: 1 to: sz with: self startingAt: 1.
^ba!
----- Method: ByteSymbol>>asOctetString (in category 'converting') -----
asOctetString
^ self!
----- Method: ByteSymbol>>at: (in category 'accessing') -----
at: index
"Primitive. Answer the Character stored in the field of the receiver
indexed by the argument. Fail if the index argument is not an Integer or
is out of bounds. Essential. See Object documentation whatIsAPrimitive."
<primitive: 63>
^ Character value: (super at: index)!
----- Method: ByteSymbol>>at:put: (in category 'accessing') -----
at: anInteger put: anObject
"You cannot modify the receiver."
self errorNoModification!
----- Method: ByteSymbol>>byteAt: (in category 'accessing') -----
byteAt: index
<primitive: 60>
^(self at: index) asciiValue!
----- Method: ByteSymbol>>byteAt:put: (in category 'accessing') -----
byteAt: anInteger put: anObject
"You cannot modify the receiver."
self errorNoModification!
----- Method: ByteSymbol>>byteSize (in category 'accessing') -----
byteSize
^self size!
----- Method: ByteSymbol>>findSubstring:in:startingAt:matchTable: (in category 'comparing') -----
findSubstring: key in: body startingAt: start matchTable: matchTable
"Answer the index in the string body at which the substring key first occurs, at or beyond start. The match is determined using matchTable, which can be used to effect, eg, case-insensitive matches. If no match is found, zero will be returned."
<primitive: 'primitiveFindSubstring' module: 'MiscPrimitivePlugin'>
^super findSubstring: key in: body startingAt: start matchTable: matchTable!
----- Method: ByteSymbol>>isByteString (in category 'testing') -----
isByteString
"Answer whether the receiver is a ByteString"
^true!
----- Method: ByteSymbol>>isOctetString (in category 'testing') -----
isOctetString
"Answer whether the receiver can be represented as a byte string.
This is different from asking whether the receiver *is* a ByteString
(i.e., #isByteString)"
^ true.
!
----- Method: ByteSymbol>>pvtAt:put: (in category 'private') -----
pvtAt: index put: aCharacter
"Primitive. Store the Character in the field of the receiver indicated by
the index. Fail if the index is not an Integer or is out of bounds, or if
the argument is not a Character. Essential. See Object documentation
whatIsAPrimitive."
<primitive: 64>
aCharacter isCharacter
ifFalse:[^self errorImproperStore].
index isInteger
ifTrue: [self errorSubscriptBounds: index]
ifFalse: [self errorNonIntegerIndex]!
----- Method: ByteSymbol>>species (in category 'accessing') -----
species
"Answer the preferred class for reconstructing the receiver."
^ByteString
!
----- Method: ByteSymbol>>string: (in category 'private') -----
string: aString
1 to: aString size do: [:j | self pvtAt: j put: (aString at: j)].
^self!
----- Method: Symbol class>>allSymbolTablesDo: (in category 'class initialization') -----
allSymbolTablesDo: aBlock
NewSymbols do: aBlock.
SymbolTable do: aBlock.!
----- Method: Symbol class>>allSymbolTablesDo:after: (in category 'class initialization') -----
allSymbolTablesDo: aBlock after: aSymbol
NewSymbols do: aBlock after: aSymbol.
SymbolTable do: aBlock after: aSymbol.!
----- Method: Symbol class>>allSymbols (in category 'access') -----
allSymbols
"Answer all interned symbols"
^Array streamContents:[:s|
s nextPutAll: NewSymbols.
s nextPutAll: OneCharacterSymbols.
s nextPutAll: SymbolTable.
].
!
----- Method: Symbol class>>compactSymbolTable (in category 'class initialization') -----
compactSymbolTable
"Reduce the size of the symbol table so that it holds all existing symbols + 25% (changed from 1000 since sets like to have 25% free and the extra space would grow back in a hurry)"
| oldSize |
Smalltalk garbageCollect.
oldSize _ SymbolTable array size.
SymbolTable growTo: SymbolTable size * 4 // 3 + 100.
^oldSize printString,' ',(oldSize - SymbolTable array size) printString, ' slot(s) reclaimed'!
----- Method: Symbol class>>compareTiming (in category 'class initialization') -----
compareTiming
"
Symbol compareTiming
"
| answer t selectorList implementorLists flattenedList md |
answer _ WriteStream on: String new.
SmalltalkImage current timeStamp: answer.
answer cr; cr.
answer nextPutAll: MethodDictionary instanceCount printString , ' method dictionaries';
cr;
cr.
answer nextPutAll: (MethodDictionary allInstances
inject: 0
into: [:sum :each | sum + each size]) printString , ' method dictionary entries';
cr;
cr.
md _ MethodDictionary allInstances.
t _ [100
timesRepeat: [md
do: [:each | each includesKey: #majorShrink]]] timeToRun.
answer nextPutAll: t printString , ' ms to check all method dictionaries for #majorShrink 1000 times';
cr;
cr.
selectorList _ Symbol selectorsContaining: 'help'.
t _ [3
timesRepeat: [selectorList
collect: [:each | self systemNavigation allImplementorsOf: each]]] timeToRun.
answer nextPutAll: t printString , ' ms to do #allImplementorsOf: for ' , selectorList size printString , ' selectors like *help* 3 times';
cr;
cr.
t _ [3
timesRepeat: [selectorList
do: [:eachSel | md
do: [:eachMd | eachMd includesKey: eachSel]]]] timeToRun.
answer nextPutAll: t printString , ' ms to do #includesKey: for ' , md size printString , ' methodDicts for ' , selectorList size printString , ' selectors like *help* 3 times';
cr;
cr.
#('help' 'majorShrink' )
do: [:substr |
answer nextPutAll: (Symbol selectorsContaining: substr) size printString , ' selectors containing "' , substr , '"';
cr.
t _ [3
timesRepeat: [selectorList _ Symbol selectorsContaining: substr]] timeToRun.
answer nextPutAll: t printString , ' ms to find Symbols containing *' , substr , '* 3 times';
cr.
t _ [3
timesRepeat: [selectorList _ Symbol selectorsContaining: substr.
implementorLists _ selectorList
collect: [:each | Smalltalk allImplementorsOf: each].
flattenedList _ SortedCollection new.
implementorLists
do: [:each | flattenedList addAll: each]]] timeToRun.
answer nextPutAll: t printString , ' ms to find implementors of *' , substr , '* 3 times';
cr;
cr].
StringHolder new contents: answer contents;
openLabel: 'timing'!
----- Method: Symbol class>>findInterned: (in category 'instance creation') -----
findInterned: aString
self hasInterned: aString ifTrue: [:symbol | ^symbol].
^nil.!
----- Method: Symbol class>>hasInterned:ifTrue: (in category 'private') -----
hasInterned: aString ifTrue: symBlock
"Answer with false if aString hasnt been interned (into a Symbol),
otherwise supply the symbol to symBlock and return true."
| symbol |
^ (symbol _ self lookup: aString)
ifNil: [false]
ifNotNil: [symBlock value: symbol.
true]!
----- Method: Symbol class>>initialize (in category 'class initialization') -----
initialize
"Symbol initialize"
Symbol rehash.
OneCharacterSymbols _ nil.
OneCharacterSymbols _ (1 to: 256) collect: [ :i | (i - 1) asCharacter asSymbol].
Smalltalk addToShutDownList: self.
!
----- Method: Symbol class>>intern: (in category 'instance creation') -----
intern: aStringOrSymbol
^(self lookup: aStringOrSymbol) ifNil:[
| aClass aSymbol |
aStringOrSymbol isSymbol ifTrue:[
aSymbol _ aStringOrSymbol.
] ifFalse:[
aClass := aStringOrSymbol isOctetString ifTrue:[ByteSymbol] ifFalse:[WideSymbol].
aSymbol := aClass new: aStringOrSymbol size.
aSymbol string: aStringOrSymbol.
].
NewSymbols add: aSymbol.
aSymbol].!
----- Method: Symbol class>>internCharacter: (in category 'instance creation') -----
internCharacter: aCharacter
aCharacter asciiValue > 256 ifTrue:[^self intern: aCharacter asString].
OneCharacterSymbols ifNil: [^self intern: aCharacter asString].
^OneCharacterSymbols at: aCharacter asciiValue + 1
!
----- Method: Symbol class>>lookup: (in category 'instance creation') -----
lookup: aStringOrSymbol
^(SymbolTable like: aStringOrSymbol) ifNil: [
NewSymbols like: aStringOrSymbol
]!
----- Method: Symbol class>>newFrom: (in category 'instance creation') -----
newFrom: aCollection
"Answer an instance of me containing the same elements as aCollection."
^ (aCollection as: String) asSymbol
" Symbol newFrom: {$P. $e. $n}
{$P. $e. $n} as: Symbol
"!
----- Method: Symbol class>>possibleSelectorsFor: (in category 'private') -----
possibleSelectorsFor: misspelled
"Answer an ordered collection of possible corrections
for the misspelled selector in order of likelyhood"
| numArgs candidates lookupString best binary short long first ss |
lookupString _ misspelled asLowercase. "correct uppercase selectors to lowercase"
numArgs _ lookupString numArgs.
(numArgs < 0 or: [lookupString size < 2]) ifTrue: [^ OrderedCollection new: 0].
first _ lookupString first.
short _ lookupString size - (lookupString size // 4 max: 3) max: 2.
long _ lookupString size + (lookupString size // 4 max: 3).
"First assemble candidates for detailed scoring"
candidates _ OrderedCollection new.
self allSymbolTablesDo: [:s | (((ss _ s size) >= short "not too short"
and: [ss <= long "not too long"
or: [(s at: 1) = first]]) "well, any length OK if starts w/same letter"
and: [s numArgs = numArgs]) "and numArgs is the same"
ifTrue: [candidates add: s]].
"Then further prune these by correctAgainst:"
best _ lookupString correctAgainst: candidates.
((misspelled last ~~ $:) and: [misspelled size > 1]) ifTrue: [
binary _ misspelled, ':'. "try for missing colon"
Symbol hasInterned: binary ifTrue: [:him | best addFirst: him]].
^ best!
----- Method: Symbol class>>readFrom: (in category 'instance creation') -----
readFrom: strm "Symbol readFromString: '#abc'"
strm peek = $# ifFalse: [self error: 'Symbols must be introduced by #'].
^ (Scanner new scan: strm) advance "Just do what the code scanner does"!
----- Method: Symbol class>>rehash (in category 'private') -----
rehash "Symbol rehash"
"Rebuild the hash table, reclaiming unreferenced Symbols."
SymbolTable := WeakSet withAll: self allSubInstances.
NewSymbols := WeakSet new.!
----- Method: Symbol class>>selectorsContaining: (in category 'access') -----
selectorsContaining: aString
"Answer a list of selectors that contain aString within them. Case-insensitive. Does return symbols that begin with a capital letter."
| size selectorList ascii |
selectorList _ OrderedCollection new.
(size _ aString size) = 0 ifTrue: [^selectorList].
aString size = 1 ifTrue:
[
ascii _ aString first asciiValue.
ascii < 128 ifTrue: [selectorList add: (OneCharacterSymbols at: ascii+1)]
].
aString first isLetter ifFalse:
[
aString size == 2 ifTrue:
[Symbol hasInterned: aString ifTrue:
[:s | selectorList add: s]].
^selectorList
].
selectorList _ selectorList copyFrom: 2 to: selectorList size.
self allSymbolTablesDo: [:each |
each size >= size ifTrue:
[(each findSubstring: aString in: each startingAt: 1
matchTable: CaseInsensitiveOrder) > 0
ifTrue: [selectorList add: each]]].
^selectorList reject: [:each | "reject non-selectors, but keep ones that begin with an uppercase"
each numArgs < 0 and: [each asString withFirstCharacterDownshifted numArgs < 0]].
"Symbol selectorsContaining: 'scon'"!
----- Method: Symbol class>>shutDown: (in category 'private') -----
shutDown: aboutToQuit
SymbolTable addAll: NewSymbols.
NewSymbols _ WeakSet new.!
----- Method: Symbol class>>thatStarts:skipping: (in category 'access') -----
thatStarts: leadingCharacters skipping: skipSym
"Answer a selector symbol that starts with leadingCharacters.
Symbols beginning with a lower-case letter handled directly here.
Ignore case after first char.
If skipSym is not nil, it is a previous answer; start searching after it.
If no symbols are found, answer nil.
Used by Alt-q (Command-q) routines"
| size firstMatch key |
size _ leadingCharacters size.
size = 0 ifTrue: [^skipSym ifNil: [#''] ifNotNil: [nil]].
firstMatch _ leadingCharacters at: 1.
size > 1 ifTrue: [key _ leadingCharacters copyFrom: 2 to: size].
self allSymbolTablesDo: [:each |
each size >= size ifTrue:
[
((each at: 1) == firstMatch and:
[key == nil or:
[(each findString: key startingAt: 2 caseSensitive: false) = 2]])
ifTrue: [^each]
]
] after: skipSym.
^nil
"Symbol thatStarts: 'sf' skipping: nil"
"Symbol thatStarts: 'sf' skipping: #sfpGetFile:with:with:with:with:with:with:with:with:"
"Symbol thatStarts: 'candidate' skipping: nil"
!
----- Method: Symbol class>>thatStartsCaseSensitive:skipping: (in category 'access') -----
thatStartsCaseSensitive: leadingCharacters skipping: skipSym
"Same as thatStarts:skipping: but caseSensitive"
| size firstMatch key |
size := leadingCharacters size.
size = 0 ifTrue: [^skipSym ifNil: [#''] ifNotNil: [nil]].
firstMatch := leadingCharacters at: 1.
size > 1 ifTrue: [key := leadingCharacters copyFrom: 2 to: size].
self allSymbolTablesDo: [:each |
each size >= size ifTrue:
[
((each at: 1) == firstMatch and:
[key == nil or:
[(each findString: key startingAt: 2 caseSensitive: true) = 2]])
ifTrue: [^each]
]
] after: skipSym.
^nil
!
----- Method: Symbol>>= (in category 'comparing') -----
= aSymbol
"Compare the receiver and aSymbol."
self == aSymbol ifTrue: [^ true].
self class == aSymbol class ifTrue: [^ false].
"Use String comparison otherwise"
^ super = aSymbol!
----- Method: Symbol>>asExplorerString (in category 'user interface') -----
asExplorerString
^ self printString!
----- Method: Symbol>>asMutator (in category 'converting') -----
asMutator
"Return a setter message from a getter message. For example,
#name asMutator returns #name:"
^ (self copyWith: $:) asSymbol!
----- Method: Symbol>>asString (in category 'converting') -----
asString
"Refer to the comment in String|asString."
| newString |
newString _ self species new: self size.
newString replaceFrom: 1 to: newString size with: self startingAt: 1.
^newString!
----- Method: Symbol>>asSymbol (in category 'converting') -----
asSymbol
"Refer to the comment in String|asSymbol."!
----- Method: Symbol>>at:put: (in category 'accessing') -----
at: anInteger put: anObject
"You cannot modify the receiver."
self errorNoModification!
----- Method: Symbol>>byteEncode: (in category 'filter streaming') -----
byteEncode:aStream
^aStream writeSymbol:self.
!
----- Method: Symbol>>capitalized (in category 'converting') -----
capitalized
^ self asString capitalized asSymbol!
----- Method: Symbol>>clone (in category 'copying') -----
clone
"Answer with the receiver, because Symbols are unique."!
----- Method: Symbol>>copy (in category 'copying') -----
copy
"Answer with the receiver, because Symbols are unique."!
----- Method: Symbol>>errorNoModification (in category 'private') -----
errorNoModification
self error: 'symbols can not be modified.'!
----- Method: Symbol>>flushCache (in category 'system primitives') -----
flushCache
"Tell the interpreter to remove all entries with this symbol as a selector from its method lookup cache, if it has one. This primitive must be called whenever a method is defined or removed.
NOTE: Only one of the two selective flush methods needs to be used.
Squeak 2.3 and later uses 116 (See CompiledMethod flushCache)."
<primitive: 119>
!
----- Method: Symbol>>isDoIt (in category 'testing') -----
isDoIt
^ (self == #DoIt) or: [self == #DoItIn:].!
----- Method: Symbol>>isInfix (in category 'testing') -----
isInfix
"Answer whether the receiver is an infix message selector."
^ self precedence == 2!
----- Method: Symbol>>isKeyword (in category 'testing') -----
isKeyword
"Answer whether the receiver is a message keyword."
^ self precedence == 3!
----- Method: Symbol>>isLiteral (in category 'testing') -----
isLiteral
"Answer whether the receiver is a valid Smalltalk literal."
^ true!
----- Method: Symbol>>isOrientedFill (in category 'printing') -----
isOrientedFill
"Needs to be implemented here because symbols can occupy 'color' slots of morphs."
^ false!
----- Method: Symbol>>isPvtSelector (in category 'testing') -----
isPvtSelector
"Answer whether the receiver is a private message selector, that is,
begins with 'pvt' followed by an uppercase letter, e.g. pvtStringhash."
^ (self beginsWith: 'pvt') and: [self size >= 4 and: [(self at: 4) isUppercase]]!
----- Method: Symbol>>isReallyString (in category 'testing') -----
isReallyString
^ false!
----- Method: Symbol>>isSymbol (in category 'testing') -----
isSymbol
^ true !
----- Method: Symbol>>isUnary (in category 'testing') -----
isUnary
"Answer whether the receiver is an unary message selector."
^ self precedence == 1!
----- Method: Symbol>>literalStringsDo: (in category 'translating') -----
literalStringsDo: aBlock
"Assuming the receiver receiver is a literal, evaluate aBlock with all Strings (but not Symbols) within it."
^ self!
----- Method: Symbol>>precedence (in category 'accessing') -----
precedence
"Answer the receiver's precedence, assuming it is a valid Smalltalk
message selector or 0 otherwise. The numbers are 1 for unary,
2 for binary and 3 for keyword selectors."
self size = 0 ifTrue: [^ 0].
self first isLetter ifFalse: [^ 2].
self last = $: ifTrue: [^ 3].
^ 1!
----- Method: Symbol>>replaceFrom:to:with:startingAt: (in category 'accessing') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
self errorNoModification!
----- Method: Symbol>>shallowCopy (in category 'copying') -----
shallowCopy
"Answer with the receiver, because Symbols are unique."!
----- Method: Symbol>>storeOn: (in category 'printing') -----
storeOn: aStream
aStream nextPut: $#.
(Scanner isLiteralSymbol: self)
ifTrue: [aStream nextPutAll: self]
ifFalse: [super storeOn: aStream]!
----- Method: Symbol>>string: (in category 'private') -----
string: aString
1 to: aString size do: [:j | super at: j put: (aString at: j)].
^self !
----- Method: Symbol>>sunitAsClass (in category 'Camp Smalltalk') -----
sunitAsClass
^SUnitNameResolver classNamed: self!
----- Method: Symbol>>veryDeepCopyWith: (in category 'copying') -----
veryDeepCopyWith: deepCopier
"Return self. I am immutable in the Morphic world. Do not record me."!
----- Method: Symbol>>withFirstCharacterDownshifted (in category 'converting') -----
withFirstCharacterDownshifted
"Answer an object like the receiver but with first character downshifted if necesary"
^self asString withFirstCharacterDownshifted asSymbol.!
Symbol variableWordSubclass: #WideSymbol
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Strings'!
!WideSymbol commentStamp: '<historical>' prior: 0!
This class represents the symbols containing 32bit characters.!
----- Method: WideSymbol class>>initialize (in category 'class initialization') -----
initialize
Smalltalk removeFromShutDownList: self. "@@@ Remove this later @@@"!
----- Method: WideSymbol class>>newFromStream: (in category 'instance creation') -----
newFromStream: s
"Use WideString rather than 'super' to avoid making multiple instance of WideSymbol"
^ self
intern: (WideString newFromStream: s)!
----- Method: WideSymbol>>at: (in category 'accessing') -----
at: index
"Answer the Character stored in the field of the receiver indexed by the argument."
^ Character value: (self wordAt: index).
!
----- Method: WideSymbol>>at:put: (in category 'accessing') -----
at: anInteger put: anObject
"You cannot modify the receiver."
self errorNoModification
!
----- Method: WideSymbol>>byteAt: (in category 'accessing') -----
byteAt: index
| d r |
d _ (index + 3) // 4.
r _ (index - 1) \\ 4 + 1.
^ (self wordAt: d) digitAt: ((4 - r) + 1).
!
----- Method: WideSymbol>>byteAt:put: (in category 'accessing') -----
byteAt: index put: aByte
self errorNoModification.!
----- Method: WideSymbol>>byteSize (in category 'accessing') -----
byteSize
^ self size * 4.
!
----- Method: WideSymbol>>fixUponLoad:seg: (in category 'private') -----
fixUponLoad: aProject seg: anImageSegment
"We are in an old project that is being loaded from disk.
Fix up conventions that have changed."
| ms |
"Yoshiki did not put MultiSymbols into outPointers in older
images!!
When all old images are gone, remove this method."
ms _ Symbol intern: self asString.
self == ms ifFalse: [
"For a project from older m17n image, this is necessary."
self becomeForward: ms.
aProject projectParameters at: #MultiSymbolInWrongPlace put: true
].
"MultiString>>capitalized was not implemented
correctly.
Fix eventual accessors and mutators here."
((self beginsWith: 'get')
and:[(self at: 4) asInteger < 256
and:[(self at: 4) isLowercase]]) ifTrue:[
ms _ self asString.
ms at: 4 put: (ms at: 4) asUppercase.
ms _ ms asSymbol.
self becomeForward: ms.
aProject projectParameters at: #MultiSymbolInWrongPlace put: true.
].
((self beginsWith: 'set')
and:[(self at: 4) asInteger < 256
and:[(self at: 4) isLowercase
and:[self last = $:
and:[(self occurrencesOf: $:) = 1]]]]) ifTrue:[
ms _ self asString.
ms at: 4 put: (ms at: 4) asUppercase.
ms _ ms asSymbol.
self becomeForward: ms.
aProject projectParameters at: #MultiSymbolInWrongPlace put: true.
].
^ super fixUponLoad: aProject seg: anImageSegment "me,
not the label"
!
----- Method: WideSymbol>>isWideString (in category 'testing') -----
isWideString
"Answer whether the receiver is a WideString"
^true!
----- Method: WideSymbol>>mutateJISX0208StringToUnicode (in category 'private') -----
mutateJISX0208StringToUnicode
| c |
1 to: self size do: [:i |
c _ self at: i.
(c leadingChar = JISX0208 leadingChar or: [
c leadingChar = (JISX0208 leadingChar bitShift: 2)]) ifTrue: [
self basicAt: i put: (Character leadingChar: JapaneseEnvironment leadingChar code: (c asUnicode)) asciiValue.
]
].
!
----- Method: WideSymbol>>pvtAt:put: (in category 'private') -----
pvtAt: index put: aCharacter
"Primitive. Store the Character in the field of the receiver indicated by
the index. Fail if the index is not an Integer or is out of bounds, or if
the argument is not a Character. Essential. See Object documentation
whatIsAPrimitive."
<primitive: 61>
index isInteger
ifTrue: [self errorSubscriptBounds: index]
ifFalse: [self errorNonIntegerIndex]!
----- Method: WideSymbol>>species (in category 'accessing') -----
species
"Answer the preferred class for reconstructing the receiver."
^WideString
!
----- Method: WideSymbol>>string: (in category 'private') -----
string: aString
1 to: aString size do: [:j | self pvtAt: j put: (aString at: j) asInteger].
^self!
----- Method: WideSymbol>>wordAt: (in category 'accessing') -----
wordAt: index
<primitive: 60>
^ (self basicAt: index).
!
----- Method: WideSymbol>>wordAt:put: (in category 'accessing') -----
wordAt: index put: anInteger
self errorNoModification.!
String variableWordSubclass: #WideString
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Strings'!
!WideString commentStamp: 'yo 10/19/2004 22:34' prior: 0!
This class represents the array of 32 bit wide characters.
!
----- Method: WideString class>>allMethodsWithEncodingTag: (in category 'enumeration') -----
allMethodsWithEncodingTag: encodingTag
"Answer a SortedCollection of all the methods that implement the message
aSelector."
| list adder num i |
list _ Set new.
adder _ [ :mrClass :mrSel |
list add: (
MethodReference new
setStandardClass: mrClass
methodSymbol: mrSel
)
].
num _ CompiledMethod allInstances size.
i _ 0.
'processing...' displayProgressAt: Sensor cursorPoint from: 0 to: num during: [:bar |
SystemNavigation new allBehaviorsDo: [ :class |
class selectors do: [:s |
bar value: (i _ i + 1).
(self string: (class sourceCodeAt: s) asString hasEncoding: encodingTag) ifTrue: [
adder value: class value: s.
]
]
]
].
^ list.
!
----- Method: WideString class>>allMultiStringMethods (in category 'enumeration') -----
allMultiStringMethods
"Answer a SortedCollection of all the methods that implement the message
aSelector."
| list adder num i |
list _ Set new.
adder _ [ :mrClass :mrSel |
list add: (
MethodReference new
setStandardClass: mrClass
methodSymbol: mrSel
)
].
num _ CompiledMethod allInstances size.
i _ 0.
'processing...' displayProgressAt: Sensor cursorPoint from: 0 to: num during: [:bar |
SystemNavigation new allBehaviorsDo: [ :class |
class selectors do: [:s |
bar value: (i _ i + 1).
((class sourceCodeAt: s) asString isOctetString) ifFalse: [
adder value: class value: s.
]
]
]
].
^ list.
!
----- Method: WideString class>>allNonAsciiMethods (in category 'enumeration') -----
allNonAsciiMethods
"Answer a SortedCollection of all the methods that implement the message
aSelector."
| list adder num i |
list _ Set new.
adder _ [ :mrClass :mrSel |
list add: (
MethodReference new
setStandardClass: mrClass
methodSymbol: mrSel
)
].
num _ CompiledMethod allInstances size.
i _ 0.
'processing...' displayProgressAt: Sensor cursorPoint from: 0 to: num during: [:bar |
SystemNavigation new allBehaviorsDo: [ :class |
class selectors do: [:s |
bar value: (i _ i + 1).
((class sourceCodeAt: s) asString isAsciiString) ifFalse: [
adder value: class value: s.
]
]
]
].
^ list.
!
----- Method: WideString class>>findFirstInString:inSet:startingAt: (in category 'primitives') -----
findFirstInString: aString inSet: inclusionMap startingAt: start
| i stringSize ascii more |
self var: #aString declareC: 'unsigned int *aString'.
self var: #inclusionMap declareC: 'char *inclusionMap'.
inclusionMap size ~= 256 ifTrue: [^ 0].
stringSize _ aString size.
more _ true.
i _ start - 1.
[more and: [i + 1 <= stringSize]] whileTrue: [
i _ i + 1.
ascii _ (aString at: i) asciiValue.
more _ ascii < 256 ifTrue: [(inclusionMap at: ascii + 1) = 0] ifFalse: [true].
].
i + 1 > stringSize ifTrue: [^ 0].
^ i.
!
----- Method: WideString class>>from: (in category 'instance creation') -----
from: aString
| newString |
(aString isMemberOf: self)
ifTrue: [^ aString copy].
newString _ self new: aString size.
1 to: aString size do: [:index | newString basicAt: index put: (aString basicAt: index)].
^ newString
!
----- Method: WideString class>>fromByteArray: (in category 'instance creation') -----
fromByteArray: aByteArray
| inst |
aByteArray size \\ 4 = 0 ifFalse: [^ ByteString fromByteArray: aByteArray ].
inst _ self new: aByteArray size // 4.
4 to: aByteArray size by: 4 do: [:i |
inst basicAt: i // 4
put: ((aByteArray at: i - 3) << 24) +
((aByteArray at: i - 2) << 16) +
((aByteArray at: i - 1) << 8) +
(aByteArray at: i)
].
^ inst
!
----- Method: WideString class>>fromISO2022JPString: (in category 'instance creation') -----
fromISO2022JPString: string
| tempFileName stream contents |
tempFileName _ Time millisecondClockValue printString , '.txt'.
FileDirectory default deleteFileNamed: tempFileName ifAbsent: [].
stream _ StandardFileStream fileNamed: tempFileName.
[stream nextPutAll: string]
ensure: [stream close].
stream _ FileStream fileNamed: tempFileName.
contents _ stream contentsOfEntireFile.
FileDirectory default deleteFileNamed: tempFileName ifAbsent: [].
^ contents
!
----- Method: WideString class>>fromPacked: (in category 'instance creation') -----
fromPacked: aLong
"Convert from a longinteger to a String of length 4."
| s val |
s _ self new: 1.
val _ (((aLong digitAt: 4) << 24) bitOr:((aLong digitAt: 3) << 16))
bitOr: (((aLong digitAt: 2) << 8) bitOr: (aLong digitAt: 1)).
s basicAt: 1 put: val.
^ s.
"WideString fromPacked: 'TEXT' asPacked"
!
----- Method: WideString class>>fromString: (in category 'instance creation') -----
fromString: aString
"Answer an instance of me that is a copy of the argument, aString."
| inst |
(aString isMemberOf: self) ifTrue: [
^ aString copy.
].
inst _ self new: aString size.
1 to: aString size do: [:pos |
inst basicAt: pos put: (aString basicAt: pos).
].
^ inst.
!
----- Method: WideString class>>indexOfAscii:inString:startingAt: (in category 'primitives') -----
indexOfAscii: anInteger inString: aString startingAt: start
| stringSize |
self var: #aCharacter declareC: 'int anInteger'.
self var: #aString declareC: 'unsigned int *aString'.
stringSize _ aString size.
start to: stringSize do: [:pos |
(aString at: pos) asciiValue = anInteger ifTrue: [^ pos]].
^ 0
!
----- Method: WideString class>>stringHash:initialHash: (in category 'primitives') -----
stringHash: aString initialHash: speciesHash
| stringSize hash low |
self var: #aHash declareC: 'int speciesHash'.
self var: #aString declareC: 'unsigned int *aString'.
stringSize _ aString size.
hash _ speciesHash bitAnd: 16rFFFFFFF.
1 to: stringSize do: [:pos |
hash _ hash + (aString at: pos) asciiValue.
"Begin hashMultiply"
low _ hash bitAnd: 16383.
hash _ (16r260D * low + ((16r260D * (hash bitShift: -14) + (16r0065 * low) bitAnd: 16383) * 16384)) bitAnd: 16r0FFFFFFF.
].
^ hash.
!
----- Method: WideString class>>translate:from:to:table: (in category 'primitives') -----
translate: aString from: start to: stop table: table
"translate the characters in the string by the given table, in place"
| char |
self var: #table declareC: 'unsigned char *table'.
self var: #aString declareC: 'unsigned int *aString'.
start to: stop do: [:i |
char _ aString basicAt: i.
char < 256 ifTrue: [
aString basicAt: i put: (table at: char+1) asciiValue
].
].
!
----- Method: WideString>>asFourCode (in category 'converting') -----
asFourCode
| result |
self size = 1 ifFalse: [^self error: 'must be exactly four octets'].
result _ self basicAt: 1.
(result bitAnd: 16r80000000) = 0
ifFalse: [self error: 'cannot resolve fourcode'].
(result bitAnd: 16r40000000) = 0 ifFalse: [^result - 16r80000000].
^ result
!
----- Method: WideString>>asPacked (in category 'converting') -----
asPacked
self inject: 0 into: [:pack :next | pack _ pack * 16r100000000 + next asInteger].
!
----- Method: WideString>>at: (in category 'accessing') -----
at: index
"Answer the Character stored in the field of the receiver indexed by the argument."
^ Character value: (self wordAt: index).
!
----- Method: WideString>>at:put: (in category 'accessing') -----
at: index put: aCharacter
"Store the Character in the field of the receiver indicated by the index."
aCharacter isCharacter ifFalse:[self errorImproperStore].
self wordAt: index put: aCharacter asInteger.
!
----- Method: WideString>>byteAt: (in category 'accessing') -----
byteAt: index
| d r |
d _ (index + 3) // 4.
r _ (index - 1) \\ 4 + 1.
^ (self wordAt: d) digitAt: ((4 - r) + 1).
!
----- Method: WideString>>byteAt:put: (in category 'accessing') -----
byteAt: index put: aByte
| d r w |
d _ (index + 3) // 4.
r _ (index - 1) \\ 4 + 1.
w _ (self wordAt: d) bitAnd: ((16rFF<<((4 - r)*8)) bitInvert32).
w _ w + (aByte<<((4 - r)*8)).
self basicAt: d put: w.
^ aByte.
!
----- Method: WideString>>byteSize (in category 'accessing') -----
byteSize
^ self size * 4.
!
----- Method: WideString>>copyFrom:to: (in category 'converting') -----
copyFrom: start to: stop
| n |
n _ super copyFrom: start to: stop.
n isOctetString ifTrue: [^ n asOctetString].
^ n.
!
----- Method: WideString>>includesUnifiedCharacter (in category 'testing') -----
includesUnifiedCharacter
^ self isUnicodeStringWithCJK
!
----- Method: WideString>>isUnicodeStringWithCJK (in category 'testing') -----
isUnicodeStringWithCJK
self do: [:c |
(c isTraditionalDomestic not and: [Unicode isUnifiedKanji: c charCode]) ifTrue: [
^ true
].
].
^ false.
!
----- Method: WideString>>isWideString (in category 'testing') -----
isWideString
"Answer whether the receiver is a WideString"
^true!
----- Method: WideString>>mutateJISX0208StringToUnicode (in category 'private') -----
mutateJISX0208StringToUnicode
| c |
1 to: self size do: [:i |
c _ self at: i.
(c leadingChar = JISX0208 leadingChar or: [
c leadingChar = (JISX0208 leadingChar bitShift: 2)]) ifTrue: [
self basicAt: i put: (Character leadingChar: JapaneseEnvironment leadingChar code: (c asUnicode)) asciiValue.
]
].
!
----- Method: WideString>>replaceFrom:to:with:startingAt: (in category 'accessing') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
<primitive: 105>
replacement class == String ifTrue: [
^ self replaceFrom: start to: stop with: (replacement asWideString) startingAt: repStart.
].
^ super replaceFrom: start to: stop with: replacement startingAt: repStart.
!
----- Method: WideString>>wordAt: (in category 'accessing') -----
wordAt: index
<primitive: 60>
^ (self basicAt: index).
!
----- Method: WideString>>wordAt:put: (in category 'accessing') -----
wordAt: index put: anInteger
<primitive: 61>
self basicAt: index put: anInteger.
!
ArrayedCollection variableWordSubclass: #WordArray
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Arrayed'!
!WordArray commentStamp: '<historical>' prior: 0!
WordArrays store 32-bit unsigned Integer values.
!
----- Method: WordArray class>>bobsTest (in category 'as yet unclassified') -----
bobsTest
| wa s1 s2 wa2 answer rawData |
"
WordArray bobsTest
"
answer _ OrderedCollection new.
wa _ WordArray with: 16r01020304 with: 16r05060708.
{false. true} do: [ :pad |
0 to: 3 do: [ :skip |
s1 _ RWBinaryOrTextStream on: ByteArray new.
s1 next: skip put: 0. "start at varying positions"
wa writeOn: s1.
pad ifTrue: [s1 next: 4-skip put: 0]. "force length to be multiple of 4"
rawData _ s1 contents.
s2 _ RWBinaryOrTextStream with: rawData.
s2 reset.
s2 skip: skip. "get to beginning of object"
wa2 _ WordArray newFromStream: s2.
answer add: {
rawData size.
skip.
wa2 = wa.
wa2 asArray collect: [ :each | each radix: 16]
}
].
].
^answer explore!
----- Method: WordArray class>>ccg:emitLoadFor:from:on: (in category 'plugin generation') -----
ccg: cg emitLoadFor: aString from: anInteger on: aStream
cg emitLoad: aString asIntPtrFrom: anInteger on: aStream!
----- Method: WordArray class>>ccg:prolog:expr:index: (in category 'plugin generation') -----
ccg: cg prolog: aBlock expr: aString index: anInteger
^cg
ccgLoad: aBlock
expr: aString
asUnsignedPtrFrom: anInteger
andThen: (cg ccgValBlock: 'isWords')!
----- Method: WordArray class>>ccgDeclareCForVar: (in category 'plugin generation') -----
ccgDeclareCForVar: aSymbolOrString
^'unsigned *', aSymbolOrString!
----- Method: WordArray>>asWordArray (in category 'converting') -----
asWordArray
^self!
----- Method: WordArray>>atAllPut: (in category 'accessing') -----
atAllPut: value
"Fill the receiver with the given value"
<primitive: 145>
super atAllPut: value!
----- Method: WordArray>>byteSize (in category 'accessing') -----
byteSize
^self size * 4!
----- Method: WordArray>>bytesPerElement (in category 'accessing') -----
bytesPerElement
"Number of bytes in each item. This multiplied by (self size)*8 gives the number of bits stored."
^ 4!
----- Method: WordArray>>defaultElement (in category 'accessing') -----
defaultElement
"Return the default element of the receiver"
^0!
----- Method: WordArray>>eToysEQ: (in category 'array arithmetic') -----
eToysEQ: other
| result |
result _ ByteArray new: self size.
other isNumber ifTrue: [
^ self primEQScalar: self and: other into: result.
].
other isCollection ifTrue: [
^ self primEQArray: self and: other into: result.
].
^ super = other.
!
----- Method: WordArray>>eToysGE: (in category 'array arithmetic') -----
eToysGE: other
| result |
result _ ByteArray new: self size.
other isNumber ifTrue: [
^ self primGEScalar: self and: other into: result.
].
other isCollection ifTrue: [
^ self primGEArray: self and: other into: result.
].
^ super >= other.
!
----- Method: WordArray>>eToysGT: (in category 'array arithmetic') -----
eToysGT: other
| result |
result _ ByteArray new: self size.
other isNumber ifTrue: [
^ self primGTScalar: self and: other into: result.
].
other isCollection ifTrue: [
^ self primGTArray: self and: other into: result.
].
^ super > other.
!
----- Method: WordArray>>eToysLE: (in category 'array arithmetic') -----
eToysLE: other
| result |
result _ ByteArray new: self size.
other isNumber ifTrue: [
^ self primLEScalar: self and: other into: result.
].
other isCollection ifTrue: [
^ self primLEArray: self and: other into: result.
].
^ super <= other.
!
----- Method: WordArray>>eToysLT: (in category 'array arithmetic') -----
eToysLT: other
| result |
result _ ByteArray new: self size.
other isNumber ifTrue: [
^ self primLTScalar: self and: other into: result.
].
other isCollection ifTrue: [
^ self primLTArray: self and: other into: result.
].
^ super < other.
!
----- Method: WordArray>>eToysNE: (in category 'array arithmetic') -----
eToysNE: other
| result |
result _ ByteArray new: self size.
other isNumber ifTrue: [
^ self primNEScalar: self and: other into: result.
].
other isCollection ifTrue: [
^ self primNEArray: self and: other into: result.
].
^ super ~= other.
!
----- Method: WordArray>>primEQArray:and:into: (in category 'array arithmetic primitives') -----
primEQArray: rcvr and: other into: result
<primitive: 'primitiveEQArrays' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveEQArrays."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) = (other at: i) ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primEQScalar:and:into: (in category 'array arithmetic primitives') -----
primEQScalar: rcvr and: other into: result
<primitive: 'primitiveEQScalar' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveEQScalar."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) = other ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primGEArray:and:into: (in category 'array arithmetic primitives') -----
primGEArray: rcvr and: other into: result
<primitive: 'primitiveGEArrays' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveGEArrays."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) >= (other at: i) ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primGEScalar:and:into: (in category 'array arithmetic primitives') -----
primGEScalar: rcvr and: other into: result
<primitive: 'primitiveGEScalar' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveGEScalar."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) >= other ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primGTArray:and:into: (in category 'array arithmetic primitives') -----
primGTArray: rcvr and: other into: result
<primitive: 'primitiveGTArrays' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveGTArrays."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) > (other at: i) ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primGTScalar:and:into: (in category 'array arithmetic primitives') -----
primGTScalar: rcvr and: other into: result
<primitive: 'primitiveGTScalar' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveGTScalar."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) > other ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primLEArray:and:into: (in category 'array arithmetic primitives') -----
primLEArray: rcvr and: other into: result
<primitive: 'primitiveLEArrays' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveLEArrays."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) <= (other at: i) ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primLEScalar:and:into: (in category 'array arithmetic primitives') -----
primLEScalar: rcvr and: other into: result
<primitive: 'primitiveLEScalar' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveLEScalar."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) <= other ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primLTArray:and:into: (in category 'array arithmetic primitives') -----
primLTArray: rcvr and: other into: result
<primitive: 'primitiveLTArrays' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveLTArrays."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) < (other at: i) ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primLTScalar:and:into: (in category 'array arithmetic primitives') -----
primLTScalar: rcvr and: other into: result
<primitive: 'primitiveLTScalar' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveLTScalar."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) < other ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primNEArray:and:into: (in category 'array arithmetic primitives') -----
primNEArray: rcvr and: other into: result
<primitive: 'primitiveNEArrays' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveNEArrays."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) ~= (other at: i) ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>primNEScalar:and:into: (in category 'array arithmetic primitives') -----
primNEScalar: rcvr and: other into: result
<primitive: 'primitiveNEScalar' module:'KedamaPlugin2'>
"^ KedamaPlugin doPrimitive: #primitiveNEScalar."
1 to: rcvr size do: [:i |
result at: i put: ((rcvr at: i) ~= other ifTrue: [1] ifFalse: [0]).
].
^ result.
!
----- Method: WordArray>>replaceFrom:to:with:startingAt: (in category 'private') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
<primitive: 105>
^super replaceFrom: start to: stop with: replacement startingAt: repStart !
WordArray variableWordSubclass: #WordArrayForSegment
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Arrayed'!
----- Method: WordArrayForSegment>>restoreEndianness (in category 'as yet unclassified') -----
restoreEndianness
"This word object was just read in from a stream. Do not correct the Endianness because the load primitive will reverse bytes as needed."
"^ self"
!
----- Method: WordArrayForSegment>>writeOn: (in category 'as yet unclassified') -----
writeOn: aByteStream
"Write quickly and disregard the endianness of the words. Store the array of bits onto the argument, aStream. (leading byte ~= 16r80) identifies this as raw bits (uncompressed)."
aByteStream nextInt32Put: self size. "4 bytes"
aByteStream nextPutAll: self
!
SequenceableCollection subclass: #Heap
instanceVariableNames: 'array tally sortBlock'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Sequenceable'!
!Heap commentStamp: '<historical>' prior: 0!
Class Heap implements a special data structure commonly referred to as 'heap'. Heaps are more efficient than SortedCollections if:
a) Elements are only removed at the beginning
b) Elements are added with arbitrary sort order.
The sort time for a heap is O(n log n) in all cases.
Instance variables:
array <Array> the data repository
tally <Integer> the number of elements in the heap
sortBlock <Block|nil> a two-argument block defining the sort order,
or nil in which case the default sort order is
[:element1 :element2| element1 <= element2]!
----- Method: Heap class>>heapExample (in category 'examples') -----
heapExample "Heap heapExample"
"Create a sorted collection of numbers, remove the elements
sequentially and add new objects randomly.
Note: This is the kind of benchmark a heap is designed for."
| n rnd array time sorted |
n _ 5000. "# of elements to sort"
rnd _ Random new.
array _ (1 to: n) collect:[:i| rnd next].
"First, the heap version"
time _ Time millisecondsToRun:[
sorted _ Heap withAll: array.
1 to: n do:[:i|
sorted removeFirst.
sorted add: rnd next].
].
Transcript cr; show:'Time for Heap: ', time printString,' msecs'.
"The quicksort version"
time _ Time millisecondsToRun:[
sorted _ SortedCollection withAll: array.
1 to: n do:[:i|
sorted removeFirst.
sorted add: rnd next].
].
Transcript cr; show:'Time for SortedCollection: ', time printString,' msecs'.
!
----- Method: Heap class>>heapSortExample (in category 'examples') -----
heapSortExample "Heap heapSortExample"
"Sort a random collection of Floats and compare the results with
SortedCollection (using the quick-sort algorithm) and
ArrayedCollection>>mergeSortFrom:to:by: (using the merge-sort algorithm)."
| n rnd array out time sorted |
n _ 10000. "# of elements to sort"
rnd _ Random new.
array _ (1 to: n) collect:[:i| rnd next].
"First, the heap version"
out _ Array new: n. "This is where we sort into"
time _ Time millisecondsToRun:[
sorted _ Heap withAll: array.
1 to: n do:[:i| sorted removeFirst].
].
Transcript cr; show:'Time for heap-sort: ', time printString,' msecs'.
"The quicksort version"
time _ Time millisecondsToRun:[
sorted _ SortedCollection withAll: array.
].
Transcript cr; show:'Time for quick-sort: ', time printString,' msecs'.
"The merge-sort version"
time _ Time millisecondsToRun:[
array mergeSortFrom: 1 to: array size by: [:v1 :v2| v1 <= v2].
].
Transcript cr; show:'Time for merge-sort: ', time printString,' msecs'.
!
----- Method: Heap class>>new (in category 'instance creation') -----
new
^self new: 10!
----- Method: Heap class>>new: (in category 'instance creation') -----
new: n
^super new setCollection: (Array new: n)!
----- Method: Heap class>>sortBlock: (in category 'instance creation') -----
sortBlock: aBlock
"Create a new heap sorted by the given block"
^self new sortBlock: aBlock!
----- Method: Heap class>>withAll: (in category 'instance creation') -----
withAll: aCollection
"Create a new heap with all the elements from aCollection"
^(self basicNew)
setCollection: aCollection asArray copy tally: aCollection size;
reSort;
yourself!
----- Method: Heap class>>withAll:sortBlock: (in category 'instance creation') -----
withAll: aCollection sortBlock: sortBlock
"Create a new heap with all the elements from aCollection"
^(self basicNew)
setCollection: aCollection asArray copy tally: aCollection size;
sortBlock: sortBlock;
yourself!
----- Method: Heap>>= (in category 'comparing') -----
= anObject
^ self == anObject
ifTrue: [true]
ifFalse: [anObject isHeap
ifTrue: [sortBlock = anObject sortBlock and: [super = anObject]]
ifFalse: [super = anObject]]!
----- Method: Heap>>add: (in category 'adding') -----
add: anObject
"Include newObject as one of the receiver's elements. Answer newObject."
tally = array size ifTrue:[self grow].
array at: (tally _ tally + 1) put: anObject.
self upHeap: tally.
^anObject!
----- Method: Heap>>array (in category 'private') -----
array
^array!
----- Method: Heap>>at: (in category 'accessing') -----
at: index
"Return the element at the given position within the receiver"
(index < 1 or:[index > tally]) ifTrue:[^self errorSubscriptBounds: index].
^array at: index!
----- Method: Heap>>at:put: (in category 'accessing') -----
at: index put: newObject
"Heaps are accessed with #add: not #at:put:"
^self shouldNotImplement!
----- Method: Heap>>do: (in category 'enumerating') -----
do: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument."
1 to: tally do:[:i| aBlock value: (array at: i)]!
----- Method: Heap>>downHeap: (in category 'private-heap') -----
downHeap: anIndex
"Check the heap downwards for correctness starting at anIndex.
Everything above (i.e. left of) anIndex is ok."
| value k n j |
anIndex = 0 ifTrue:[^self].
n _ tally bitShift: -1.
k _ anIndex.
value _ array at: anIndex.
[k <= n] whileTrue:[
j _ k + k.
"use max(j,j+1)"
(j < tally and:[self sorts: (array at: j+1) before: (array at: j)])
ifTrue:[ j _ j + 1].
"check if position k is ok"
(self sorts: value before: (array at: j))
ifTrue:[ "yes -> break loop"
n _ k - 1]
ifFalse:[ "no -> make room at j by moving j-th element to k-th position"
array at: k put: (array at: j).
"and try again with j"
k _ j]].
array at: k put: value.!
----- Method: Heap>>downHeapSingle: (in category 'private-heap') -----
downHeapSingle: anIndex
"This version is optimized for the case when only one element in the receiver can be at a wrong position. It avoids one comparison at each node when travelling down the heap and checks the heap upwards after the element is at a bottom position. Since the probability for being at the bottom of the heap is much larger than for being somewhere in the middle this version should be faster."
| value k n j |
anIndex = 0 ifTrue:[^self].
n _ tally bitShift: -1.
k _ anIndex.
value _ array at: anIndex.
[k <= n] whileTrue:[
j _ k + k.
"use max(j,j+1)"
(j < tally and:[self sorts: (array at: j+1) before: (array at: j)])
ifTrue:[ j _ j + 1].
array at: k put: (array at: j).
"and try again with j"
k _ j].
array at: k put: value.
self upHeap: k!
----- Method: Heap>>first (in category 'accessing') -----
first
"Return the first element in the receiver"
self emptyCheck.
^array at: 1!
----- Method: Heap>>grow (in category 'growing') -----
grow
"Become larger."
self growTo: self size + self growSize.!
----- Method: Heap>>growSize (in category 'growing') -----
growSize
"Return the size by which the receiver should grow if there are no empty slots left."
^array size max: 5!
----- Method: Heap>>growTo: (in category 'growing') -----
growTo: newSize
"Grow to the requested size."
| newArray |
newArray _ Array new: (newSize max: tally).
newArray replaceFrom: 1 to: array size with: array startingAt: 1.
array _ newArray!
----- Method: Heap>>isEmpty (in category 'testing') -----
isEmpty
"Answer whether the receiver contains any elements."
^tally = 0!
----- Method: Heap>>isHeap (in category 'testing') -----
isHeap
^ true!
----- Method: Heap>>privateRemoveAt: (in category 'private') -----
privateRemoveAt: index
"Remove the element at the given index and make sure the sorting order is okay"
| removed |
removed _ array at: index.
array at: index put: (array at: tally).
array at: tally put: nil.
tally _ tally - 1.
index > tally ifFalse:[
"Use #downHeapSingle: since only one element has been removed"
self downHeapSingle: index].
^removed!
----- Method: Heap>>reSort (in category 'accessing') -----
reSort
"Resort the entire heap"
self isEmpty ifTrue:[^self].
tally // 2 to: 1 by: -1 do:[:i| self downHeap: i].!
----- Method: Heap>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: aBlock
"Remove oldObject as one of the receiver's elements. If several of the
elements are equal to oldObject, only one is removed. If no element is
equal to oldObject, answer the result of evaluating anExceptionBlock.
Otherwise, answer the argument, oldObject."
1 to: tally do:[:i|
(array at: i) = oldObject ifTrue:[^self privateRemoveAt: i]].
^aBlock value!
----- Method: Heap>>removeAt: (in category 'removing') -----
removeAt: index
"Remove the element at given position"
(index < 1 or:[index > tally]) ifTrue:[^self errorSubscriptBounds: index].
^self privateRemoveAt: index!
----- Method: Heap>>removeFirst (in category 'removing') -----
removeFirst
"Remove the first element from the receiver"
^self removeAt: 1!
----- Method: Heap>>setCollection: (in category 'private') -----
setCollection: aCollection
array _ aCollection.
tally _ 0.!
----- Method: Heap>>setCollection:tally: (in category 'private') -----
setCollection: aCollection tally: newTally
array _ aCollection.
tally _ newTally.!
----- Method: Heap>>size (in category 'accessing') -----
size
"Answer how many elements the receiver contains."
^ tally!
----- Method: Heap>>sortBlock (in category 'accessing') -----
sortBlock
^sortBlock!
----- Method: Heap>>sortBlock: (in category 'accessing') -----
sortBlock: aBlock
sortBlock _ aBlock.
sortBlock fixTemps.
self reSort.!
----- Method: Heap>>sorts:before: (in category 'testing') -----
sorts: element1 before: element2
"Return true if element1 should be sorted before element2.
This method defines the sort order in the receiver"
^sortBlock == nil
ifTrue:[element1 <= element2]
ifFalse:[sortBlock value: element1 value: element2].!
----- Method: Heap>>species (in category 'private') -----
species
^ Array!
----- Method: Heap>>trim (in category 'growing') -----
trim
"Remove any empty slots in the receiver."
self growTo: self size.!
----- Method: Heap>>upHeap: (in category 'private-heap') -----
upHeap: anIndex
"Check the heap upwards for correctness starting at anIndex.
Everything below anIndex is ok."
| value k kDiv2 tmp |
anIndex = 0 ifTrue:[^self].
k _ anIndex.
value _ array at: anIndex.
[ (k > 1) and:[self sorts: value before: (tmp _ array at: (kDiv2 _ k bitShift: -1))] ]
whileTrue:[
array at: k put: tmp.
k _ kDiv2].
array at: k put: value.!
SequenceableCollection subclass: #Interval
instanceVariableNames: 'start stop step'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Sequenceable'!
!Interval commentStamp: '<historical>' prior: 0!
I represent a finite arithmetic progression.!
----- Method: Interval class>>from:to: (in category 'instance creation') -----
from: startInteger to: stopInteger
"Answer an instance of me, starting at startNumber, ending at
stopNumber, and with an interval increment of 1."
^self new
setFrom: startInteger
to: stopInteger
by: 1!
----- Method: Interval class>>from:to:by: (in category 'instance creation') -----
from: startInteger to: stopInteger by: stepInteger
"Answer an instance of me, starting at startNumber, ending at
stopNumber, and with an interval increment of stepNumber."
^self new
setFrom: startInteger
to: stopInteger
by: stepInteger!
----- Method: Interval class>>new (in category 'instance creation') -----
new
"Primitive. Create and answer with a new instance of the receiver
(a class) with no indexable fields. Fail if the class is indexable. Override
SequenceableCollection new. Essential. See Object documentation
whatIsAPrimitive."
<primitive: 70>
self isVariable ifTrue: [ ^ self new: 0 ].
"space must be low"
Smalltalk signalLowSpace.
^ self new "retry if user proceeds"
!
----- Method: Interval class>>newFrom: (in category 'instance creation') -----
newFrom: aCollection
"Answer an instance of me containing the same elements as aCollection."
| newInterval n |
(n := aCollection size) <= 1 ifTrue: [
n = 0 ifTrue: [^self from: 1 to: 0].
^self from: aCollection first to: aCollection last].
newInterval := self from: aCollection first to: aCollection last
by: (aCollection last - aCollection first) // (n - 1).
aCollection ~= newInterval
ifTrue: [self error: 'The argument is not an arithmetic progression'].
^newInterval
" Interval newFrom: {1. 2. 3}
{33. 5. -23} as: Interval
{33. 5. -22} as: Interval (an error)
(-4 to: -12 by: -1) as: Interval
"!
----- Method: Interval>>+ (in category 'arithmetic') -----
+ number
^ start + number to: stop + number by: step!
----- Method: Interval>>- (in category 'arithmetic') -----
- number
^ start - number to: stop - number by: step!
----- Method: Interval>>= (in category 'comparing') -----
= anObject
^ self == anObject
ifTrue: [true]
ifFalse: [anObject isInterval
ifTrue: [start = anObject first
and: [step = anObject increment
and: [self last = anObject last]]]
ifFalse: [super = anObject]]!
----- Method: Interval>>add: (in category 'adding') -----
add: newObject
"Adding to an Interval is not allowed."
self shouldNotImplement!
----- Method: Interval>>at: (in category 'accessing') -----
at: anInteger
"Answer the anInteger'th element."
(anInteger >= 1 and: [anInteger <= self size])
ifTrue: [^start + (step * (anInteger - 1))]
ifFalse: [self errorSubscriptBounds: anInteger]!
----- Method: Interval>>at:put: (in category 'accessing') -----
at: anInteger put: anObject
"Storing into an Interval is not allowed."
self error: 'you can not store into an interval'!
----- Method: Interval>>collect: (in category 'enumerating') -----
collect: aBlock
| nextValue result |
result _ self species new: self size.
nextValue _ start.
1 to: result size do:
[:i |
result at: i put: (aBlock value: nextValue).
nextValue _ nextValue + step].
^ result!
----- Method: Interval>>copy (in category 'copying') -----
copy
"Return a copy of me. Override the superclass because my species is
Array and copy, as inherited from SequenceableCollection, uses
copyFrom:to:, which creates a new object of my species."
^self shallowCopy!
----- Method: Interval>>do: (in category 'enumerating') -----
do: aBlock
| aValue |
aValue _ start.
step < 0
ifTrue: [[stop <= aValue]
whileTrue:
[aBlock value: aValue.
aValue _ aValue + step]]
ifFalse: [[stop >= aValue]
whileTrue:
[aBlock value: aValue.
aValue _ aValue + step]]!
----- Method: Interval>>extent (in category 'accessing') -----
extent
"Answer the max - min of the receiver interval."
"(10 to: 50) extent"
^stop - start!
----- Method: Interval>>first (in category 'accessing') -----
first
"Refer to the comment in SequenceableCollection|first."
^start!
----- Method: Interval>>hash (in category 'comparing') -----
hash
"Hash is reimplemented because = is implemented."
^(((start hash bitShift: 2)
bitOr: stop hash)
bitShift: 1)
bitOr: self size!
----- Method: Interval>>hashMappedBy: (in category 'comparing') -----
hashMappedBy: map
"My hash is independent of my oop."
^self hash!
----- Method: Interval>>includes: (in category 'nil') -----
includes: aNumber
^ aNumber between: self first and: self last!
----- Method: Interval>>increment (in category 'accessing') -----
increment
"Answer the receiver's interval increment."
^step!
----- Method: Interval>>isInterval (in category 'testing') -----
isInterval
^ true!
----- Method: Interval>>last (in category 'accessing') -----
last
"Refer to the comment in SequenceableCollection|last."
^stop - (stop - start \\ step)!
----- Method: Interval>>permutationsDo: (in category 'enumerating') -----
permutationsDo: aBlock
"Repeatly value aBlock with a single copy of the receiver. Reorder the copy
so that aBlock is presented all (self size factorial) possible permutations."
"(1 to: 4) permutationsDo: [:each | Transcript cr; show: each printString]"
self asArray permutationsDo: aBlock
!
----- Method: Interval>>printOn: (in category 'printing') -----
printOn: aStream
aStream nextPut: $(;
print: start;
nextPutAll: ' to: ';
print: stop.
step ~= 1 ifTrue: [aStream nextPutAll: ' by: '; print: step].
aStream nextPut: $)!
----- Method: Interval>>rangeIncludes: (in category 'accessing') -----
rangeIncludes: aNumber
"Return true if the number lies in the interval between start and stop."
step >= 0
ifTrue: [^ aNumber between: start and: stop]
ifFalse: [^ aNumber between: stop and: start]
!
----- Method: Interval>>remove: (in category 'removing') -----
remove: newObject
"Removing from an Interval is not allowed."
self error: 'elements cannot be removed from an Interval'!
----- Method: Interval>>reverseDo: (in category 'enumerating') -----
reverseDo: aBlock
"Evaluate aBlock for each element of my interval, in reverse order."
| aValue |
aValue _ stop.
step < 0
ifTrue: [[start >= aValue]
whileTrue:
[aBlock value: aValue.
aValue _ aValue - step]]
ifFalse: [[start <= aValue]
whileTrue:
[aBlock value: aValue.
aValue _ aValue - step]]!
----- Method: Interval>>setFrom:to:by: (in category 'private') -----
setFrom: startInteger to: stopInteger by: stepInteger
start _ startInteger.
stop _ stopInteger.
step _ stepInteger!
----- Method: Interval>>shallowCopy (in category 'copying') -----
shallowCopy
"Without this method, #copy would return an array instead of a new interval.
The whole problem is burried in the class hierarchy and every fix will worsen
the problem, so once the whole issue is resolved one should come back to this
method fix it."
^ self class from: start to: stop by: step!
----- Method: Interval>>size (in category 'accessing') -----
size
"Answer how many elements the receiver contains."
step < 0
ifTrue: [start < stop
ifTrue: [^ 0]
ifFalse: [^ stop - start // step + 1]]
ifFalse: [stop < start
ifTrue: [^ 0]
ifFalse: [^ stop - start // step + 1]]!
----- Method: Interval>>species (in category 'private') -----
species
^Array!
----- Method: Interval>>start (in category 'accessing') -----
start
^ start!
----- Method: Interval>>stop (in category 'accessing') -----
stop
^ stop!
----- Method: Interval>>storeOn: (in category 'printing') -----
storeOn: aStream
"This is possible because we know numbers store and print the same."
self printOn: aStream!
----- Method: Interval>>valuesInclude: (in category 'private') -----
valuesInclude: aNumber
"Private - answer whether or not aNumber is one of the enumerated values in this interval."
| val |
val _ (aNumber - self first) asFloat / self increment.
^ val fractionPart abs < (step * 1.0e-10)!
SequenceableCollection subclass: #LinkedList
instanceVariableNames: 'firstLink lastLink'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Sequenceable'!
!LinkedList commentStamp: '<historical>' prior: 0!
I represent a collection of links, which are containers for other objects. Using the message sequence addFirst:/removeLast causes the receiver to behave as a stack; using addLast:/removeFirst causes the receiver to behave as a queue.!
----- Method: LinkedList>>add: (in category 'adding') -----
add: aLink
"Add aLink to the end of the receiver's list. Answer aLink."
^self addLast: aLink!
----- Method: LinkedList>>add:after: (in category 'adding') -----
add: link after: otherLink
"Add otherLink after link in the list. Answer aLink."
| savedLink |
savedLink := otherLink nextLink.
otherLink nextLink: link.
link nextLink: savedLink.
^link.!
----- Method: LinkedList>>add:before: (in category 'adding') -----
add: link before: otherLink
| aLink |
firstLink == otherLink ifTrue: [^ self addFirst: link].
aLink _ firstLink.
[aLink == nil] whileFalse: [
aLink nextLink == otherLink ifTrue: [
link nextLink: aLink nextLink.
aLink nextLink: link.
^ link
].
aLink _ aLink nextLink.
].
^ self errorNotFound: otherLink!
----- Method: LinkedList>>addFirst: (in category 'adding') -----
addFirst: aLink
"Add aLink to the beginning of the receiver's list. Answer aLink."
self isEmpty ifTrue: [lastLink _ aLink].
aLink nextLink: firstLink.
firstLink _ aLink.
^aLink!
----- Method: LinkedList>>addLast: (in category 'adding') -----
addLast: aLink
"Add aLink to the end of the receiver's list. Answer aLink."
self isEmpty
ifTrue: [firstLink _ aLink]
ifFalse: [lastLink nextLink: aLink].
lastLink _ aLink.
^aLink!
----- Method: LinkedList>>at: (in category 'accessing') -----
at: index
| i |
i _ 0.
self do: [:link |
(i _ i + 1) = index ifTrue: [^ link]].
^ self errorSubscriptBounds: index!
----- Method: LinkedList>>do: (in category 'enumerating') -----
do: aBlock
| aLink |
aLink _ firstLink.
[aLink == nil] whileFalse:
[aBlock value: aLink.
aLink _ aLink nextLink]!
----- Method: LinkedList>>first (in category 'accessing') -----
first
"Answer the first link. Create an error notification if the receiver is
empty."
self emptyCheck.
^firstLink!
----- Method: LinkedList>>isEmpty (in category 'testing') -----
isEmpty
^firstLink == nil!
----- Method: LinkedList>>last (in category 'accessing') -----
last
"Answer the last link. Create an error notification if the receiver is
empty."
self emptyCheck.
^lastLink!
----- Method: LinkedList>>remove:ifAbsent: (in category 'removing') -----
remove: aLink ifAbsent: aBlock
"Remove aLink from the receiver. If it is not there, answer the result of
evaluating aBlock."
| tempLink |
aLink == firstLink
ifTrue: [firstLink _ aLink nextLink.
aLink == lastLink
ifTrue: [lastLink _ nil]]
ifFalse: [tempLink _ firstLink.
[tempLink == nil ifTrue: [^aBlock value].
tempLink nextLink == aLink]
whileFalse: [tempLink _ tempLink nextLink].
tempLink nextLink: aLink nextLink.
aLink == lastLink
ifTrue: [lastLink _ tempLink]].
aLink nextLink: nil.
^aLink!
----- Method: LinkedList>>removeFirst (in category 'removing') -----
removeFirst
"Remove the first element and answer it. If the receiver is empty, create
an error notification."
| oldLink |
self emptyCheck.
oldLink _ firstLink.
firstLink == lastLink
ifTrue: [firstLink _ nil. lastLink _ nil]
ifFalse: [firstLink _ oldLink nextLink].
oldLink nextLink: nil.
^oldLink!
----- Method: LinkedList>>removeLast (in category 'removing') -----
removeLast
"Remove the receiver's last element and answer it. If the receiver is
empty, create an error notification."
| oldLink aLink |
self emptyCheck.
oldLink _ lastLink.
firstLink == lastLink
ifTrue: [firstLink _ nil. lastLink _ nil]
ifFalse: [aLink _ firstLink.
[aLink nextLink == oldLink] whileFalse:
[aLink _ aLink nextLink].
aLink nextLink: nil.
lastLink _ aLink].
oldLink nextLink: nil.
^oldLink!
----- Method: LinkedList>>species (in category 'enumerating') -----
species
^ Array!
SequenceableCollection subclass: #OrderedCollection
instanceVariableNames: 'array firstIndex lastIndex'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Sequenceable'!
!OrderedCollection commentStamp: '<historical>' prior: 0!
I represent a collection of objects ordered by the collector.!
----- Method: OrderedCollection class>>new (in category 'instance creation') -----
new
^ self new: 10!
----- Method: OrderedCollection class>>new: (in category 'instance creation') -----
new: anInteger
^ super basicNew setCollection: (Array new: anInteger)!
----- Method: OrderedCollection class>>newFrom: (in category 'instance creation') -----
newFrom: aCollection
"Answer an instance of me containing the same elements as aCollection."
| newCollection |
newCollection _ self new: aCollection size.
newCollection addAll: aCollection.
^newCollection
" OrderedCollection newFrom: {1. 2. 3}
{1. 2. 3} as: OrderedCollection
{4. 2. 7} as: SortedCollection
"!
----- Method: OrderedCollection class>>ofSize: (in category 'instance creation') -----
ofSize: n
"Create a new collection of size n with nil as its elements.
This method exists because OrderedCollection new: n creates an
empty collection, not one of size n."
| collection |
collection _ self new: n.
collection setContents: (collection collector).
^ collection
!
----- Method: OrderedCollection>>add: (in category 'adding') -----
add: newObject
^self addLast: newObject!
----- Method: OrderedCollection>>add:after: (in category 'adding') -----
add: newObject after: oldObject
"Add the argument, newObject, as an element of the receiver. Put it in
the sequence just succeeding oldObject. Answer newObject."
| index |
index _ self find: oldObject.
self insert: newObject before: index + 1.
^newObject!
----- Method: OrderedCollection>>add:afterIndex: (in category 'adding') -----
add: newObject afterIndex: index
"Add the argument, newObject, as an element of the receiver. Put it in
the sequence just after index. Answer newObject."
self insert: newObject before: firstIndex + index.
^ newObject!
----- Method: OrderedCollection>>add:before: (in category 'adding') -----
add: newObject before: oldObject
"Add the argument, newObject, as an element of the receiver. Put it in
the sequence just preceding oldObject. Answer newObject."
| index |
index _ self find: oldObject.
self insert: newObject before: index.
^newObject!
----- Method: OrderedCollection>>add:beforeIndex: (in category 'adding') -----
add: newObject beforeIndex: index
"Add the argument, newObject, as an element of the receiver. Put it in
the sequence just before index. Answer newObject."
self add: newObject afterIndex: index - 1.
^ newObject!
----- Method: OrderedCollection>>addAll: (in category 'adding') -----
addAll: aCollection
"Add each element of aCollection at my end. Answer aCollection."
^ self addAllLast: aCollection!
----- Method: OrderedCollection>>addAllFirst: (in category 'adding') -----
addAllFirst: anOrderedCollection
"Add each element of anOrderedCollection at the beginning of the
receiver. Answer anOrderedCollection."
anOrderedCollection reverseDo: [:each | self addFirst: each].
^anOrderedCollection!
----- Method: OrderedCollection>>addAllFirstUnlessAlreadyPresent: (in category 'adding') -----
addAllFirstUnlessAlreadyPresent: anOrderedCollection
"Add each element of anOrderedCollection at the beginning of the receiver, preserving the order, but do not add any items that are already in the receiver. Answer anOrderedCollection."
anOrderedCollection reverseDo:
[:each | (self includes: each) ifFalse: [self addFirst: each]].
^ anOrderedCollection!
----- Method: OrderedCollection>>addAllLast: (in category 'adding') -----
addAllLast: anOrderedCollection
"Add each element of anOrderedCollection at the end of the receiver.
Answer anOrderedCollection."
anOrderedCollection do: [:each | self addLast: each].
^anOrderedCollection!
----- Method: OrderedCollection>>addFirst: (in category 'adding') -----
addFirst: newObject
"Add newObject to the beginning of the receiver. Answer newObject."
firstIndex = 1 ifTrue: [self makeRoomAtFirst].
firstIndex _ firstIndex - 1.
array at: firstIndex put: newObject.
^ newObject!
----- Method: OrderedCollection>>addLast: (in category 'adding') -----
addLast: newObject
"Add newObject to the end of the receiver. Answer newObject."
lastIndex = array size ifTrue: [self makeRoomAtLast].
lastIndex _ lastIndex + 1.
array at: lastIndex put: newObject.
^ newObject!
----- Method: OrderedCollection>>at: (in category 'accessing') -----
at: anInteger
"Answer my element at index anInteger. at: is used by a knowledgeable
client to access an existing element"
(anInteger < 1 or: [anInteger + firstIndex - 1 > lastIndex])
ifTrue: [self errorNoSuchElement]
ifFalse: [^ array at: anInteger + firstIndex - 1]!
----- Method: OrderedCollection>>at:ifAbsentPut: (in category 'adding') -----
at: index ifAbsentPut: block
"Return value at index, however, if value does not exist (nil or out of bounds) then add block's value at index (growing self if necessary)"
| v |
index <= self size ifTrue: [
^ (v _ self at: index)
ifNotNil: [v]
ifNil: [self at: index put: block value]
].
[self size < index] whileTrue: [self add: nil].
^ self at: index put: block value!
----- Method: OrderedCollection>>at:put: (in category 'accessing') -----
at: anInteger put: anObject
"Put anObject at element index anInteger. at:put: cannot be used to
append, front or back, to an ordered collection; it is used by a
knowledgeable client to replace an element."
| index |
index _ anInteger asInteger.
(index < 1 or: [index + firstIndex - 1 > lastIndex])
ifTrue: [self errorNoSuchElement]
ifFalse: [^array at: index + firstIndex - 1 put: anObject]!
----- Method: OrderedCollection>>capacity (in category 'accessing') -----
capacity
"Answer the current capacity of the receiver."
^ array size!
----- Method: OrderedCollection>>collect: (in category 'enumerating') -----
collect: aBlock
"Evaluate aBlock with each of my elements as the argument. Collect the
resulting values into a collection that is like me. Answer the new
collection. Override superclass in order to use addLast:, not at:put:."
| newCollection |
newCollection _ self species new: self size.
firstIndex to: lastIndex do:
[:index |
newCollection addLast: (aBlock value: (array at: index))].
^ newCollection!
----- Method: OrderedCollection>>collect:from:to: (in category 'enumerating') -----
collect: aBlock from: fromIndex to: toIndex
"Override superclass in order to use addLast:, not at:put:."
| result |
(fromIndex < 1 or:[toIndex + firstIndex - 1 > lastIndex])
ifTrue: [^self errorNoSuchElement].
result _ self species new: toIndex - fromIndex + 1.
firstIndex + fromIndex - 1 to: firstIndex + toIndex - 1 do:
[:index | result addLast: (aBlock value: (array at: index))].
^ result
!
----- Method: OrderedCollection>>collector (in category 'private') -----
collector "Private"
^ array!
----- Method: OrderedCollection>>copyEmpty (in category 'copying') -----
copyEmpty
"Answer a copy of the receiver that contains no elements."
^self species new!
----- Method: OrderedCollection>>copyFrom:to: (in category 'copying') -----
copyFrom: startIndex to: endIndex
"Answer a copy of the receiver that contains elements from position
startIndex to endIndex."
| targetCollection |
endIndex < startIndex ifTrue: [^self species new: 0].
targetCollection _ self species new: endIndex + 1 - startIndex.
startIndex to: endIndex do: [:index | targetCollection addLast: (self at: index)].
^ targetCollection!
----- Method: OrderedCollection>>copyReplaceFrom:to:with: (in category 'copying') -----
copyReplaceFrom: start to: stop with: replacementCollection
"Answer a copy of the receiver with replacementCollection's elements in
place of the receiver's start'th to stop'th elements. This does not expect
a 1-1 map from replacementCollection to the start to stop elements, so it
will do an insert or append."
| newOrderedCollection delta startIndex stopIndex |
"if start is less than 1, ignore stop and assume this is inserting at the front.
if start greater than self size, ignore stop and assume this is appending.
otherwise, it is replacing part of me and start and stop have to be within my
bounds. "
delta _ 0.
startIndex _ start.
stopIndex _ stop.
start < 1
ifTrue: [startIndex _ stopIndex _ 0]
ifFalse: [startIndex > self size
ifTrue: [startIndex _ stopIndex _ self size + 1]
ifFalse:
[(stopIndex < (startIndex - 1) or: [stopIndex > self size])
ifTrue: [self errorOutOfBounds].
delta _ stopIndex - startIndex + 1]].
newOrderedCollection _
self species new: self size + replacementCollection size - delta.
1 to: startIndex - 1 do: [:index | newOrderedCollection add: (self at: index)].
1 to: replacementCollection size do:
[:index | newOrderedCollection add: (replacementCollection at: index)].
stopIndex + 1 to: self size do: [:index | newOrderedCollection add: (self at: index)].
^newOrderedCollection!
----- Method: OrderedCollection>>copyWith: (in category 'copying') -----
copyWith: newElement
"Answer a copy of the receiver that is 1 bigger than the receiver and
includes the argument, newElement, at the end."
| newCollection |
newCollection _ self copy.
newCollection add: newElement.
^newCollection!
----- Method: OrderedCollection>>do: (in category 'enumerating') -----
do: aBlock
"Override the superclass for performance reasons."
| index |
index _ firstIndex.
[index <= lastIndex]
whileTrue:
[aBlock value: (array at: index).
index _ index + 1]!
----- Method: OrderedCollection>>errorConditionNotSatisfied (in category 'private') -----
errorConditionNotSatisfied
self error: 'no element satisfies condition'!
----- Method: OrderedCollection>>errorNoSuchElement (in category 'private') -----
errorNoSuchElement
self error: 'attempt to index non-existent element in an ordered collection'!
----- Method: OrderedCollection>>find: (in category 'private') -----
find: oldObject
" This method answers an index in the range firstIndex .. lastIndex, which is meant for internal use only.
Never use this method in your code, the methods for public use are:
#indexOf:
#indexOf:ifAbsent: "
| index |
index _ firstIndex.
[index <= lastIndex]
whileTrue:
[(array at: index) = oldObject ifTrue: [^ index].
index _ index + 1].
self errorNotFound: oldObject!
----- Method: OrderedCollection>>grow (in category 'adding') -----
grow
"Become larger. Typically, a subclass has to override this if the subclass
adds instance variables."
| newArray |
newArray _ Array new: self size + self growSize.
newArray replaceFrom: 1 to: array size with: array startingAt: 1.
array _ newArray!
----- Method: OrderedCollection>>growSize (in category 'adding') -----
growSize
^ array size max: 2!
----- Method: OrderedCollection>>insert:before: (in category 'private') -----
insert: anObject before: spot
" spot is an index in the range firstIndex .. lastIndex, such an index is not known from outside the collection.
Never use this method in your code, it is meant for private use by OrderedCollection only.
The methods for use are:
#add:before: to insert an object before another object
#add:beforeIndex: to insert an object before a given position. "
| "index" delta spotIndex|
spotIndex _ spot.
delta _ spotIndex - firstIndex.
firstIndex = 1
ifTrue:
[self makeRoomAtFirst.
spotIndex _ firstIndex + delta].
firstIndex _ firstIndex - 1.
array
replaceFrom: firstIndex
to: spotIndex - 2
with: array
startingAt: firstIndex + 1.
array at: spotIndex - 1 put: anObject.
" index _ firstIndex _ firstIndex - 1.
[index < (spotIndex - 1)]
whileTrue:
[array at: index put: (array at: index + 1).
index _ index + 1].
array at: index put: anObject."
^ anObject!
----- Method: OrderedCollection>>makeRoomAtFirst (in category 'private') -----
makeRoomAtFirst
| delta index |
delta _ array size - self size.
delta = 0 ifTrue:
[self grow.
delta _ array size - self size].
lastIndex = array size ifTrue: [^ self]. "just in case we got lucky"
index _ array size.
[index > delta]
whileTrue:
[array at: index put: (array at: index - delta + firstIndex - 1).
array at: index - delta + firstIndex - 1 put: nil.
index _ index - 1].
firstIndex _ delta + 1.
lastIndex _ array size!
----- Method: OrderedCollection>>makeRoomAtLast (in category 'private') -----
makeRoomAtLast
| newLast delta |
newLast _ self size.
array size - self size = 0 ifTrue: [self grow].
(delta _ firstIndex - 1) = 0 ifTrue: [^ self].
"we might be here under false premises or grow did the job for us"
1 to: newLast do:
[:index |
array at: index put: (array at: index + delta).
array at: index + delta put: nil].
firstIndex _ 1.
lastIndex _ newLast!
----- Method: OrderedCollection>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: absentBlock
| index |
index _ firstIndex.
[index <= lastIndex]
whileTrue:
[oldObject = (array at: index)
ifTrue:
[self removeIndex: index.
^ oldObject]
ifFalse: [index _ index + 1]].
^ absentBlock value!
----- Method: OrderedCollection>>removeAllSuchThat: (in category 'removing') -----
removeAllSuchThat: aBlock
"Remove each element of the receiver for which aBlock evaluates to true.
The method in Collection is O(N^2), this is O(N)."
| n |
n _ firstIndex.
firstIndex to: lastIndex do: [:index |
(aBlock value: (array at: index)) ifFalse: [
array at: n put: (array at: index).
n _ n + 1]].
n to: lastIndex do: [:index | array at: index put: nil].
lastIndex _ n - 1!
----- Method: OrderedCollection>>removeAt: (in category 'removing') -----
removeAt: index
| removed |
removed _ self at: index.
self removeIndex: index + firstIndex - 1.
^removed!
----- Method: OrderedCollection>>removeFirst (in category 'removing') -----
removeFirst
"Remove the first element of the receiver and answer it. If the receiver is
empty, create an error notification."
| firstObject |
self emptyCheck.
firstObject _ array at: firstIndex.
array at: firstIndex put: nil.
firstIndex _ firstIndex + 1.
^ firstObject!
----- Method: OrderedCollection>>removeFirst: (in category 'removing') -----
removeFirst: n
"Remove first n object into an array"
| list |
list _ Array new: n.
1 to: n do: [:i |
list at: i put: self removeFirst].
^ list!
----- Method: OrderedCollection>>removeIndex: (in category 'private') -----
removeIndex: removedIndex
" removedIndex is an index in the range firstIndex .. lastIndex, such an index is not known from outside the collection.
Never use this method in your code, it is meant for private use by OrderedCollection only.
The method for public use is:
#removeAt: "
array
replaceFrom: removedIndex
to: lastIndex - 1
with: array
startingAt: removedIndex+1.
array at: lastIndex put: nil.
lastIndex _ lastIndex - 1.!
----- Method: OrderedCollection>>removeLast (in category 'removing') -----
removeLast
"Remove the last element of the receiver and answer it. If the receiver is
empty, create an error notification."
| lastObject |
self emptyCheck.
lastObject _ array at: lastIndex.
array at: lastIndex put: nil.
lastIndex _ lastIndex - 1.
^ lastObject!
----- Method: OrderedCollection>>removeLast: (in category 'removing') -----
removeLast: n
"Remove last n object into an array with last in last position"
| list |
list _ Array new: n.
n to: 1 by: -1 do: [:i |
list at: i put: self removeLast].
^ list!
----- Method: OrderedCollection>>reset (in category 'private') -----
reset
firstIndex _ array size // 3 max: 1.
lastIndex _ firstIndex - 1!
----- Method: OrderedCollection>>resetTo: (in category 'private') -----
resetTo: index
firstIndex _ index.
lastIndex _ firstIndex - 1!
----- Method: OrderedCollection>>reverseDo: (in category 'enumerating') -----
reverseDo: aBlock
"Override the superclass for performance reasons."
| index |
index _ lastIndex.
[index >= firstIndex]
whileTrue:
[aBlock value: (array at: index).
index _ index - 1]!
----- Method: OrderedCollection>>reversed (in category 'copying') -----
reversed
"Answer a copy of the receiver with element order reversed. "
| newCol |
newCol _ self species new.
self reverseDo:
[:elem | newCol addLast: elem].
^ newCol
"#(2 3 4 'fred') reversed"!
----- Method: OrderedCollection>>select: (in category 'enumerating') -----
select: aBlock
"Evaluate aBlock with each of my elements as the argument. Collect into
a new collection like the receiver, only those elements for which aBlock
evaluates to true."
| newCollection element |
newCollection _ self copyEmpty.
firstIndex to: lastIndex do:
[:index |
(aBlock value: (element _ array at: index))
ifTrue: [newCollection addLast: element]].
^ newCollection!
----- Method: OrderedCollection>>setCollection: (in category 'private') -----
setCollection: anArray
array _ anArray.
self reset!
----- Method: OrderedCollection>>setContents: (in category 'private') -----
setContents: anArray
array _ anArray.
firstIndex _ 1.
lastIndex _ array size.!
----- Method: OrderedCollection>>size (in category 'accessing') -----
size
"Answer how many elements the receiver contains."
^ lastIndex - firstIndex + 1!
----- Method: OrderedCollection>>sort: (in category 'sorting') -----
sort: aSortBlock
"Sort this collection using aSortBlock. The block should take two arguments
and return true if the first element should preceed the second one.
If aSortBlock is nil then <= is used for comparison."
self ifNotEmpty: [
array
mergeSortFrom: firstIndex
to: lastIndex
by: aSortBlock ]!
----- Method: OrderedCollection>>with:collect: (in category 'enumerating') -----
with: otherCollection collect: twoArgBlock
"Collect and return the result of evaluating twoArgBlock with
corresponding elements from this collection and otherCollection."
| result |
otherCollection size = self size ifFalse: [self error: 'otherCollection must be the same size'].
result _ self species new: self size.
1 to: self size do:
[:index | result addLast: (twoArgBlock value: (self at: index)
value: (otherCollection at: index))].
^ result!
----- Method: OrderedCollection>>withIndexCollect: (in category 'enumerating') -----
withIndexCollect: elementAndIndexBlock
"Just like with:collect: except that the iteration index supplies the second argument to the block. Override superclass in order to use addLast:, not at:put:."
| newCollection |
newCollection _ self species new: self size.
firstIndex to: lastIndex do:
[:index |
newCollection addLast: (elementAndIndexBlock
value: (array at: index)
value: index - firstIndex + 1)].
^ newCollection!
OrderedCollection subclass: #SortedCollection
instanceVariableNames: 'sortBlock'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Sequenceable'!
!SortedCollection commentStamp: '<historical>' prior: 0!
I represent a collection of objects ordered by some property of the objects themselves. The ordering is specified in a BlockContext.!
----- Method: SortedCollection class>>new: (in category 'instance creation') -----
new: anInteger
"The default sorting function is a <= comparison on elements."
^(super new: anInteger) "sortBlock: [:x :y | x <= y]" "nil sortBlock OK"!
----- Method: SortedCollection class>>sortBlock: (in category 'instance creation') -----
sortBlock: aBlock
"Answer an instance of me such that its elements are sorted according to
the criterion specified in aBlock."
^(super new: 10) sortBlock: aBlock!
----- Method: SortedCollection>>= (in category 'comparing') -----
= aSortedCollection
"Answer true if my and aSortedCollection's species are the same,
and if our blocks are the same, and if our elements are the same."
self species = aSortedCollection species ifFalse: [^ false].
sortBlock = aSortedCollection sortBlock
ifTrue: [^ super = aSortedCollection]
ifFalse: [^ false]!
----- Method: SortedCollection>>add: (in category 'adding') -----
add: newObject
^ super insert: newObject before: (self indexForInserting: newObject)!
----- Method: SortedCollection>>addAll: (in category 'adding') -----
addAll: aCollection
aCollection size > (self size // 3)
ifTrue:
[aCollection do: [:each | self addLast: each].
self reSort]
ifFalse: [aCollection do: [:each | self add: each]].
^ aCollection!
----- Method: SortedCollection>>addFirst: (in category 'adding') -----
addFirst: newObject
self shouldNotImplement!
----- Method: SortedCollection>>at:put: (in category 'accessing') -----
at: anInteger put: anObject
self shouldNotImplement!
----- Method: SortedCollection>>collect: (in category 'enumerating') -----
collect: aBlock
"Evaluate aBlock with each of my elements as the argument. Collect the
resulting values into an OrderedCollection. Answer the new collection.
Override the superclass in order to produce an OrderedCollection instead
of a SortedCollection."
| newCollection |
newCollection _ OrderedCollection new: self size.
self do: [:each | newCollection addLast: (aBlock value: each)].
^ newCollection!
----- Method: SortedCollection>>copy (in category 'copying') -----
copy
| newCollection |
newCollection _ self species sortBlock: sortBlock.
newCollection addAll: self.
^newCollection!
----- Method: SortedCollection>>copyEmpty (in category 'adding') -----
copyEmpty
"Answer a copy of the receiver without any of the receiver's elements."
^self species sortBlock: sortBlock!
----- Method: SortedCollection>>defaultSort:to: (in category 'private') -----
defaultSort: i to: j
"Sort elements i through j of self to be nondescending according to
sortBlock." "Assume the default sort block ([:x :y | x <= y])."
| di dij dj tt ij k l n |
"The prefix d means the data at that index."
(n _ j + 1 - i) <= 1 ifTrue: [^self]. "Nothing to sort."
"Sort di,dj."
di _ array at: i.
dj _ array at: j.
(di <= dj) "i.e., should di precede dj?"
ifFalse:
[array swap: i with: j.
tt _ di.
di _ dj.
dj _ tt].
n > 2
ifTrue: "More than two elements."
[ij _ (i + j) // 2. "ij is the midpoint of i and j."
dij _ array at: ij. "Sort di,dij,dj. Make dij be their median."
(di <= dij) "i.e. should di precede dij?"
ifTrue:
[(dij <= dj) "i.e., should dij precede dj?"
ifFalse:
[array swap: j with: ij.
dij _ dj]]
ifFalse: "i.e. di should come after dij"
[array swap: i with: ij.
dij _ di].
n > 3
ifTrue: "More than three elements."
["Find k>i and l<j such that dk,dij,dl are in reverse order.
Swap k and l. Repeat this procedure until k and l pass each other."
k _ i.
l _ j.
[[l _ l - 1. k <= l and: [dij <= (array at: l)]]
whileTrue. "i.e. while dl succeeds dij"
[k _ k + 1. k <= l and: [(array at: k) <= dij]]
whileTrue. "i.e. while dij succeeds dk"
k <= l]
whileTrue:
[array swap: k with: l].
"Now l<k (either 1 or 2 less), and di through dl are all less than or equal to dk
through dj. Sort those two segments."
self defaultSort: i to: l.
self defaultSort: k to: j]]!
----- Method: SortedCollection>>indexForInserting: (in category 'private') -----
indexForInserting: newObject
| index low high |
low _ firstIndex.
high _ lastIndex.
sortBlock isNil
ifTrue: [[index _ high + low // 2. low > high]
whileFalse:
[((array at: index) <= newObject)
ifTrue: [low _ index + 1]
ifFalse: [high _ index - 1]]]
ifFalse: [[index _ high + low // 2. low > high]
whileFalse:
[(sortBlock value: (array at: index) value: newObject)
ifTrue: [low _ index + 1]
ifFalse: [high _ index - 1]]].
^low!
----- Method: SortedCollection>>insert:before: (in category 'private') -----
insert: anObject before: spot
self shouldNotImplement!
----- Method: SortedCollection>>median (in category 'accessing') -----
median
"Return the middle element, or as close as we can get."
^ self at: self size + 1 // 2!
----- Method: SortedCollection>>reSort (in category 'private') -----
reSort
self sort: firstIndex to: lastIndex!
----- Method: SortedCollection>>sort: (in category 'sorting') -----
sort: aSortBlock
"Sort this collection using aSortBlock. The block should take two arguments
and return true if the first element should preceed the second one.
If aSortBlock is nil then <= is used for comparison."
super sort: aSortBlock.
sortBlock := aSortBlock!
----- Method: SortedCollection>>sort:to: (in category 'private') -----
sort: i to: j
"Sort elements i through j of self to be nondescending according to
sortBlock."
| di dij dj tt ij k l n |
sortBlock ifNil: [^self defaultSort: i to: j].
"The prefix d means the data at that index."
(n _ j + 1 - i) <= 1 ifTrue: [^self]. "Nothing to sort."
"Sort di,dj."
di _ array at: i.
dj _ array at: j.
(sortBlock value: di value: dj) "i.e., should di precede dj?"
ifFalse:
[array swap: i with: j.
tt _ di.
di _ dj.
dj _ tt].
n > 2
ifTrue: "More than two elements."
[ij _ (i + j) // 2. "ij is the midpoint of i and j."
dij _ array at: ij. "Sort di,dij,dj. Make dij be their median."
(sortBlock value: di value: dij) "i.e. should di precede dij?"
ifTrue:
[(sortBlock value: dij value: dj) "i.e., should dij precede dj?"
ifFalse:
[array swap: j with: ij.
dij _ dj]]
ifFalse: "i.e. di should come after dij"
[array swap: i with: ij.
dij _ di].
n > 3
ifTrue: "More than three elements."
["Find k>i and l<j such that dk,dij,dl are in reverse order.
Swap k and l. Repeat this procedure until k and l pass each other."
k _ i.
l _ j.
[[l _ l - 1. k <= l and: [sortBlock value: dij value: (array at: l)]]
whileTrue. "i.e. while dl succeeds dij"
[k _ k + 1. k <= l and: [sortBlock value: (array at: k) value: dij]]
whileTrue. "i.e. while dij succeeds dk"
k <= l]
whileTrue:
[array swap: k with: l].
"Now l<k (either 1 or 2 less), and di through dl are all less than or equal to dk
through dj. Sort those two segments."
self sort: i to: l.
self sort: k to: j]]!
----- Method: SortedCollection>>sortBlock (in category 'accessing') -----
sortBlock
"Answer the blockContext which is the criterion for sorting elements of
the receiver."
^sortBlock!
----- Method: SortedCollection>>sortBlock: (in category 'accessing') -----
sortBlock: aBlock
"Make the argument, aBlock, be the criterion for ordering elements of the
receiver."
aBlock
ifNotNil: [sortBlock := aBlock fixTemps]
ifNil: [sortBlock := aBlock].
"The sortBlock must copy its home context, so as to avoid circularities!!"
"Therefore sortBlocks with side effects may not work right"
self size > 0 ifTrue: [self reSort]!
----- Method: SequenceableCollection class>>new:streamContents: (in category 'stream creation') -----
new: newSize streamContents: blockWithArg
| stream |
stream := WriteStream on: (self new: newSize).
blockWithArg value: stream.
stream position = newSize
ifTrue: [ ^stream originalContents ]
ifFalse: [ ^stream contents ]!
----- Method: SequenceableCollection class>>streamContents: (in category 'stream creation') -----
streamContents: blockWithArg
| stream |
stream _ WriteStream on: (self new: 100).
blockWithArg value: stream.
^stream contents!
----- Method: SequenceableCollection class>>streamContents:limitedTo: (in category 'stream creation') -----
streamContents: blockWithArg limitedTo: sizeLimit
| stream |
stream _ LimitedWriteStream on: (self new: (100 min: sizeLimit)).
stream setLimit: sizeLimit limitBlock: [^ stream contents].
blockWithArg value: stream.
^ stream contents
"
String streamContents: [:s | 1000 timesRepeat: [s nextPutAll: 'Junk']] limitedTo: 25
'JunkJunkJunkJunkJunkJunkJ'
"!
----- Method: SequenceableCollection>>, (in category 'copying') -----
, otherCollection
"Concatenate two Strings or Collections."
^ self copyReplaceFrom: self size + 1
to: self size
with: otherCollection
"
#(2 4 6 8) , #(who do we appreciate)
((2989 printStringBase: 16) copyFrom: 4 to: 6) , ' boy!!'
"!
----- Method: SequenceableCollection>>= (in category 'comparing') -----
= otherCollection
"Answer true if the receiver is equivalent to the otherCollection.
First test for identity, then rule out different species and sizes of
collections. As a last resort, examine each element of the receiver
and the otherCollection."
self == otherCollection ifTrue: [^ true].
self species == otherCollection species ifFalse: [^ false].
^ self hasEqualElements: otherCollection!
----- Method: SequenceableCollection>>@ (in category 'converting') -----
@ aCollection
^ self with: aCollection collect: [:a :b | a @ b]!
----- Method: SequenceableCollection>>after: (in category 'accessing') -----
after: target
"Answer the element after target. Raise an error if target is not
in the receiver, or if there are no elements after it."
^ self after: target ifAbsent: [self errorNotFound: target]!
----- Method: SequenceableCollection>>after:ifAbsent: (in category 'accessing') -----
after: target ifAbsent: exceptionBlock
"Answer the element after target. Answer the result of evaluation
the exceptionBlock if target is not in the receiver, or if there are
no elements after it."
| index |
index _ self indexOf: target.
^ index == 0
ifTrue: [exceptionBlock value]
ifFalse: [index = self size
ifTrue: [exceptionBlock value]
ifFalse: [self at: index + 1]]!
----- Method: SequenceableCollection>>allButFirst (in category 'accessing') -----
allButFirst
"Answer a copy of the receiver containing all but the first
element. Raise an error if there are not enough elements."
^ self allButFirst: 1!
----- Method: SequenceableCollection>>allButFirst: (in category 'accessing') -----
allButFirst: n
"Answer a copy of the receiver containing all but the first n
elements. Raise an error if there are not enough elements."
^ self copyFrom: n + 1 to: self size!
----- Method: SequenceableCollection>>allButFirstDo: (in category 'enumerating') -----
allButFirstDo: block
2 to: self size do:
[:index | block value: (self at: index)]!
----- Method: SequenceableCollection>>allButLast (in category 'accessing') -----
allButLast
"Answer a copy of the receiver containing all but the last
element. Raise an error if there are not enough elements."
^ self allButLast: 1!
----- Method: SequenceableCollection>>allButLast: (in category 'accessing') -----
allButLast: n
"Answer a copy of the receiver containing all but the last n
elements. Raise an error if there are not enough elements."
^ self copyFrom: 1 to: self size - n!
----- Method: SequenceableCollection>>allButLastDo: (in category 'enumerating') -----
allButLastDo: block
1 to: self size - 1 do:
[:index | block value: (self at: index)]!
----- Method: SequenceableCollection>>anyOne (in category 'accessing') -----
anyOne
^ self first!
----- Method: SequenceableCollection>>asArray (in category 'converting') -----
asArray
"Answer an Array whose elements are the elements of the receiver."
^ Array withAll: self!
----- Method: SequenceableCollection>>asByteArray (in category 'converting') -----
asByteArray
"Answer a ByteArray whose elements are the elements of the receiver."
^ ByteArray withAll: self!
----- Method: SequenceableCollection>>asColorArray (in category 'converting') -----
asColorArray
^ColorArray withAll: self!
----- Method: SequenceableCollection>>asDigitsAt:in:do: (in category 'private') -----
asDigitsAt: anInteger in: aCollection do: aBlock
"(0 to: 1) asDigitsToPower: 4 do: [:each | Transcript cr; show: each printString]"
self do:
[:each |
aCollection at: anInteger put: each.
anInteger = aCollection size
ifTrue: [aBlock value: aCollection]
ifFalse: [self asDigitsAt: anInteger + 1 in: aCollection do: aBlock]].!
----- Method: SequenceableCollection>>asDigitsToPower:do: (in category 'enumerating') -----
asDigitsToPower: anInteger do: aBlock
"Repeatedly value aBlock with a single Array. Adjust the collection
so that aBlock is presented all (self size raisedTo: anInteger) possible
combinations of the receiver's elements taken as digits of an anInteger long number."
"(0 to: 1) asDigitsToPower: 4 do: [:each | Transcript cr; show: each printString]"
| aCollection |
aCollection _ Array new: anInteger.
self asDigitsAt: 1 in: aCollection do: aBlock!
----- Method: SequenceableCollection>>asFloatArray (in category 'converting') -----
asFloatArray
"Answer a FloatArray whose elements are the elements of the receiver, in
the same order."
| floatArray |
floatArray _ FloatArray new: self size.
1 to: self size do:[:i| floatArray at: i put: (self at: i) asFloat ].
^floatArray!
----- Method: SequenceableCollection>>asIntegerArray (in category 'converting') -----
asIntegerArray
"Answer an IntegerArray whose elements are the elements of the receiver, in
the same order."
| intArray |
intArray _ IntegerArray new: self size.
1 to: self size do:[:i| intArray at: i put: (self at: i)].
^intArray!
----- Method: SequenceableCollection>>asPointArray (in category 'converting') -----
asPointArray
"Answer an PointArray whose elements are the elements of the receiver, in
the same order."
| pointArray |
pointArray _ PointArray new: self size.
1 to: self size do:[:i| pointArray at: i put: (self at: i)].
^pointArray!
----- Method: SequenceableCollection>>asStringWithCr (in category 'converting') -----
asStringWithCr
"Convert to a string with returns between items. Elements are
usually strings.
Useful for labels for PopUpMenus."
| labelStream |
labelStream _ WriteStream on: (String new: 200).
self do: [:each |
each isString
ifTrue: [labelStream nextPutAll: each; cr]
ifFalse: [each printOn: labelStream. labelStream cr]].
self size > 0 ifTrue: [labelStream skip: -1].
^ labelStream contents!
----- Method: SequenceableCollection>>asWordArray (in category 'converting') -----
asWordArray
"Answer a WordArray whose elements are the elements of the receiver, in
the same order."
| wordArray |
wordArray _ WordArray new: self size.
1 to: self size do:[:i| wordArray at: i put: (self at: i)].
^wordArray!
----- Method: SequenceableCollection>>at:ifAbsent: (in category 'accessing') -----
at: index ifAbsent: exceptionBlock
"Answer the element at my position index. If I do not contain an element
at index, answer the result of evaluating the argument, exceptionBlock."
(index between: 1 and: self size) ifTrue: [^ self at: index].
^ exceptionBlock value!
----- Method: SequenceableCollection>>at:incrementBy: (in category 'accessing') -----
at: index incrementBy: value
^self at: index put: (self at: index) + value!
----- Method: SequenceableCollection>>atAll: (in category 'accessing') -----
atAll: indexArray
"Answer a new collection like the receiver which contains all elements
of the receiver at the indices of indexArray."
"#('one' 'two' 'three' 'four') atAll: #(3 2 4)"
| newCollection |
newCollection _ self species ofSize: indexArray size.
1 to: indexArray size do:
[:index |
newCollection at: index put: (self at: (indexArray at: index))].
^ newCollection!
----- Method: SequenceableCollection>>atAll:put: (in category 'accessing') -----
atAll: aCollection put: anObject
"Put anObject at every index specified by the elements of aCollection."
aCollection do: [:index | self at: index put: anObject].
^ anObject!
----- Method: SequenceableCollection>>atAll:putAll: (in category 'accessing') -----
atAll: indexArray putAll: valueArray
"Store the elements of valueArray into the slots
of this collection selected by indexArray."
indexArray with: valueArray do: [:index :value | self at: index put: value].
^ valueArray!
----- Method: SequenceableCollection>>atAllPut: (in category 'accessing') -----
atAllPut: anObject
"Put anObject at every one of the receiver's indices."
| size |
(size _ self size) > 26 "first method faster from 27 accesses and on"
ifTrue: [self from: 1 to: size put: anObject]
ifFalse: [1 to: size do: [:index | self at: index put: anObject]]!
----- Method: SequenceableCollection>>atLast: (in category 'accessing') -----
atLast: indexFromEnd
"Return element at indexFromEnd from the last position.
atLast: 1, returns the last element"
^ self atLast: indexFromEnd ifAbsent: [self error: 'index out of range']!
----- Method: SequenceableCollection>>atLast:ifAbsent: (in category 'accessing') -----
atLast: indexFromEnd ifAbsent: block
"Return element at indexFromEnd from the last position.
atLast: 1 ifAbsent: [] returns the last element"
^ self at: self size + 1 - indexFromEnd ifAbsent: block!
----- Method: SequenceableCollection>>atLast:put: (in category 'accessing') -----
atLast: indexFromEnd put: obj
"Set the element at indexFromEnd from the last position.
atLast: 1 put: obj, sets the last element"
^ self at: self size + 1 - indexFromEnd put: obj!
----- Method: SequenceableCollection>>atPin: (in category 'accessing') -----
atPin: index
"Return the index'th element of me if possible.
Return the first or last element if index is out of bounds."
index < 1 ifTrue: [^ self first].
index > self size ifTrue: [^ self last].
^ self at: index!
----- Method: SequenceableCollection>>atRandom: (in category 'accessing') -----
atRandom: aGenerator
"Answer a random element of the receiver. Uses aGenerator which
should be kept by the user in a variable and used every time. Use
this instead of #atRandom for better uniformity of random numbers
because only you use the generator. Causes an error if self has no
elements."
^ self at: (aGenerator nextInt: self size)!
----- Method: SequenceableCollection>>atWrap: (in category 'accessing') -----
atWrap: index
"Answer the index'th element of the receiver. If index is out of bounds,
let it wrap around from the end to the beginning until it is in bounds."
^ self at: index - 1 \\ self size + 1!
----- Method: SequenceableCollection>>atWrap:put: (in category 'accessing') -----
atWrap: index put: value
"Store value into the index'th element of the receiver. If index is out
of bounds, let it wrap around from the end to the beginning until it
is in bounds. Answer value."
^ self at: index - 1 \\ self size + 1 put: value!
----- Method: SequenceableCollection>>before: (in category 'accessing') -----
before: target
"Answer the receiver's element immediately before target. Raise an
error if target is not an element of the receiver, or if there are no
elements before it (i.e. it is the first element)."
^ self before: target ifAbsent: [self errorNotFound: target]!
----- Method: SequenceableCollection>>before:ifAbsent: (in category 'accessing') -----
before: target ifAbsent: exceptionBlock
"Answer the receiver's element immediately before target. Answer
the result of evaluating the exceptionBlock if target is not an element
of the receiver, or if there are no elements before it."
| index |
index _ self indexOf: target.
^ index == 0
ifTrue: [exceptionBlock value]
ifFalse: [index == 1
ifTrue: [exceptionBlock value]
ifFalse: [self at: index - 1]]!
----- Method: SequenceableCollection>>beginsWith: (in category 'testing') -----
beginsWith: aSequenceableCollection
(aSequenceableCollection isEmpty or: [self size < aSequenceableCollection size]) ifTrue: [^false].
aSequenceableCollection withIndexDo: [:each :index | (self at: index) ~= each ifTrue: [^false]].
^true!
----- Method: SequenceableCollection>>collect: (in category 'enumerating') -----
collect: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Collect the resulting values into a collection like the receiver. Answer
the new collection."
| newCollection |
newCollection _ self species new: self size.
1 to: self size do:
[:index |
newCollection at: index put: (aBlock value: (self at: index))].
^ newCollection!
----- Method: SequenceableCollection>>collect:from:to: (in category 'enumerating') -----
collect: aBlock from: firstIndex to: lastIndex
"Refer to the comment in Collection|collect:."
| size result j |
size _ lastIndex - firstIndex + 1.
result _ self species new: size.
j _ firstIndex.
1 to: size do: [:i | result at: i put: (aBlock value: (self at: j)). j _ j + 1].
^ result!
----- Method: SequenceableCollection>>collectWithIndex: (in category 'enumerating') -----
collectWithIndex: elementAndIndexBlock
"Use the new version with consistent naming"
^ self withIndexCollect: elementAndIndexBlock!
----- Method: SequenceableCollection>>combinations:atATimeDo: (in category 'enumerating') -----
combinations: kk atATimeDo: aBlock
"Take the items in the receiver, kk at a time, and evaluate the block for each combination. Hand in an array of elements of self as the block argument. Each combination only occurs once, and order of the elements does not matter. There are (self size take: kk) combinations."
" 'abcde' combinations: 3 atATimeDo: [:each | Transcript cr; show: each printString]"
| aCollection |
aCollection _ Array new: kk.
self combinationsAt: 1 in: aCollection after: 0 do: aBlock!
----- Method: SequenceableCollection>>combinationsAt:in:after:do: (in category 'private') -----
combinationsAt: jj in: aCollection after: nn do: aBlock
"Choose k of N items and put in aCollection. jj-1 already chosen. Indexes of items are in numerical order, to avoid the same combo being used twice. In this slot, we are allowed to use items in self indexed by nn+1 to self size. nn is the index used for position jj-1."
"(1 to: 6) combinationsSize: 3 do: [:each | Transcript cr; show: each printString]"
nn+1 to: self size do: [:index |
aCollection at: jj put: (self at: index).
jj = aCollection size
ifTrue: [aBlock value: aCollection]
ifFalse: [self combinationsAt: jj + 1 in: aCollection after: index do: aBlock]].!
----- Method: SequenceableCollection>>concatenation (in category 'converting') -----
concatenation
|result index|
result _ Array new: (self inject: 0 into: [:sum :each | sum + each size]).
index _ 0.
self do: [:each | each do: [:item | result at: (index _ index+1) put: item]].
^result!
----- Method: SequenceableCollection>>copyAfter: (in category 'copying') -----
copyAfter: anElement
"Answer a copy of the receiver from after the first occurence
of anElement up to the end. If no such element exists, answer
an empty copy."
^ self allButFirst: (self indexOf: anElement ifAbsent: [^ self copyEmpty])!
----- Method: SequenceableCollection>>copyAfterLast: (in category 'copying') -----
copyAfterLast: anElement
"Answer a copy of the receiver from after the last occurence
of anElement up to the end. If no such element exists, answer
an empty copy."
^ self allButFirst: (self lastIndexOf: anElement ifAbsent: [^ self copyEmpty])!
----- Method: SequenceableCollection>>copyEmpty (in category 'copying') -----
copyEmpty
^ self species new: 0!
----- Method: SequenceableCollection>>copyFrom:to: (in category '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 |
newSize _ stop - start + 1.
^(self species new: newSize)
replaceFrom: 1
to: newSize
with: self
startingAt: start!
----- Method: SequenceableCollection>>copyLast: (in category 'copying') -----
copyLast: num
"Deprecated. Use #last:"
^ self last: num!
----- Method: SequenceableCollection>>copyReplaceAll:with: (in category 'copying') -----
copyReplaceAll: oldSubstring with: newSubstring
"Default is not to do token matching.
See also String copyReplaceTokens:with:"
^ self copyReplaceAll: oldSubstring with: newSubstring asTokens: false
"'How now brown cow?' copyReplaceAll: 'ow' with: 'ello'"
"'File asFile Files File''s File' copyReplaceTokens: 'File' with: 'Pile'"!
----- Method: SequenceableCollection>>copyReplaceAll:with:asTokens: (in category 'private') -----
copyReplaceAll: oldSubstring with: newSubstring asTokens: ifTokens
"Answer a copy of the receiver in which all occurrences of
oldSubstring have been replaced by newSubstring.
ifTokens (valid for Strings only) specifies that the characters
surrounding the recplacement must not be alphanumeric.
Bruce Simth, must be incremented by 1 and not
newSubstring if ifTokens is true. See example below. "
| aString startSearch currentIndex endIndex |
(ifTokens and: [(self isString) not])
ifTrue: [(self isKindOf: Text) ifFalse: [
self error: 'Token replacement only valid for Strings']].
aString _ self.
startSearch _ 1.
[(currentIndex _ aString indexOfSubCollection: oldSubstring startingAt: startSearch)
> 0]
whileTrue:
[endIndex _ currentIndex + oldSubstring size - 1.
(ifTokens not
or: [(currentIndex = 1
or: [(aString at: currentIndex-1) isAlphaNumeric not])
and: [endIndex = aString size
or: [(aString at: endIndex+1) isAlphaNumeric not]]])
ifTrue: [aString _ aString
copyReplaceFrom: currentIndex
to: endIndex
with: newSubstring.
startSearch _ currentIndex + newSubstring size]
ifFalse: [
ifTokens
ifTrue: [startSearch _ currentIndex + 1]
ifFalse: [startSearch _ currentIndex + newSubstring size]]].
^ aString
"Test case:
'test te string' copyReplaceAll: 'te' with: 'longone' asTokens: true "
!
----- Method: SequenceableCollection>>copyReplaceFrom:to:with: (in category 'copying') -----
copyReplaceFrom: start to: stop with: replacementCollection
"Answer a copy of the receiver satisfying the following conditions: If
stop is less than start, then this is an insertion; stop should be exactly
start-1, start = 1 means insert before the first character, start = size+1
means append after last character. Otherwise, this is a replacement; start
and stop have to be within the receiver's bounds."
| newSequenceableCollection newSize endReplacement |
newSize _ self size - (stop - start + 1) + replacementCollection size.
endReplacement _ start - 1 + replacementCollection size.
newSequenceableCollection _ self species new: newSize.
start > 1 ifTrue:[
newSequenceableCollection
replaceFrom: 1
to: start - 1
with: self
startingAt: 1].
start <= endReplacement ifTrue:[
newSequenceableCollection
replaceFrom: start
to: endReplacement
with: replacementCollection
startingAt: 1].
endReplacement < newSize ifTrue:[
newSequenceableCollection
replaceFrom: endReplacement + 1
to: newSize
with: self
startingAt: stop + 1].
^newSequenceableCollection!
----- Method: SequenceableCollection>>copyUpTo: (in category 'copying') -----
copyUpTo: anElement
"Answer all elements up to but not including anObject. If there
is no such object, answer a copy of the receiver."
^ self first: (self indexOf: anElement ifAbsent: [^ self copy]) - 1!
----- Method: SequenceableCollection>>copyUpToLast: (in category 'copying') -----
copyUpToLast: anElement
"Answer a copy of the receiver from index 1 to the last occurrence of
anElement, not including anElement."
^ self first: (self lastIndexOf: anElement ifAbsent: [^ self copy]) - 1!
----- Method: SequenceableCollection>>copyWith: (in category 'copying') -----
copyWith: newElement
"Answer a copy of the receiver that is 1 bigger than the receiver and has
newElement at the last element."
| newIC |
newIC _ self species new: self size + 1.
newIC
replaceFrom: 1
to: self size
with: self
startingAt: 1.
newIC at: newIC size put: newElement.
^newIC!
----- Method: SequenceableCollection>>copyWithFirst: (in category 'copying') -----
copyWithFirst: newElement
"Answer a copy of the receiver that is 1 bigger than the receiver with newElement as the first element."
| newIC |
newIC _ self species ofSize: self size + 1.
newIC
replaceFrom: 2
to: self size + 1
with: self
startingAt: 1.
newIC at: 1 put: newElement.
^ newIC!
----- Method: SequenceableCollection>>copyWithoutFirst (in category 'copying') -----
copyWithoutFirst
"Deprecatd. Return a copy of the receiver which doesn't include
the first element."
^ self allButFirst!
----- Method: SequenceableCollection>>copyWithoutIndex: (in category 'copying') -----
copyWithoutIndex: index
"Return a copy containing all elements except the index-th."
| copy |
copy := self species ofSize: self size - 1.
copy replaceFrom: 1 to: index-1 with: self startingAt: 1.
copy replaceFrom: index to: copy size with: self startingAt: index+1.
^ copy!
----- Method: SequenceableCollection>>customizeExplorerContents (in category 'accessing') -----
customizeExplorerContents
^ true.
!
----- Method: SequenceableCollection>>do: (in category 'enumerating') -----
do: aBlock
"Refer to the comment in Collection|do:."
1 to: self size do:
[:index | aBlock value: (self at: index)]!
----- Method: SequenceableCollection>>do:displayingProgress: (in category 'enumerating') -----
do: aBlock displayingProgress: aString
aString
displayProgressAt: Sensor cursorPoint
from: 0 to: self size
during:
[:bar |
self withIndexDo:
[:each :i |
bar value: i.
aBlock value: each]]!
----- Method: SequenceableCollection>>do:separatedBy: (in category 'enumerating') -----
do: elementBlock separatedBy: separatorBlock
"Evaluate the elementBlock for all elements in the receiver,
and evaluate the separatorBlock between."
1 to: self size do:
[:index |
index = 1 ifFalse: [separatorBlock value].
elementBlock value: (self at: index)]!
----- Method: SequenceableCollection>>do:without: (in category 'enumerating') -----
do: aBlock without: anItem
"Enumerate all elements in the receiver.
Execute aBlock for those elements that are not equal to the given item"
"Refer to the comment in Collection|do:."
1 to: self size do:
[:index | anItem = (self at: index) ifFalse:[aBlock value: (self at: index)]]!
----- Method: SequenceableCollection>>doWithIndex: (in category 'enumerating') -----
doWithIndex: elementAndIndexBlock
"Use the new version with consistent naming"
^ self withIndexDo: elementAndIndexBlock!
----- Method: SequenceableCollection>>eighth (in category 'accessing') -----
eighth
"Answer the eighth element of the receiver.
Raise an error if there are not enough elements."
^ self at: 8!
----- Method: SequenceableCollection>>endsWith: (in category 'testing') -----
endsWith: aSequenceableCollection
| start |
(aSequenceableCollection isEmpty or: [self size < aSequenceableCollection size]) ifTrue: [^false].
start _ self size - aSequenceableCollection size.
aSequenceableCollection withIndexDo: [:each :index | (self at: start + index) ~= each ifTrue: [^false]].
^true!
----- Method: SequenceableCollection>>errorOutOfBounds (in category 'private') -----
errorOutOfBounds
self error: 'indices are out of bounds'!
----- Method: SequenceableCollection>>fifth (in category 'accessing') -----
fifth
"Answer the fifth element of the receiver.
Raise an error if there are not enough elements."
^ self at: 5!
----- Method: SequenceableCollection>>findBinary: (in category 'enumerating') -----
findBinary: aBlock
"Search for an element in the receiver using binary search.
The argument aBlock is a one-element block returning
0 - if the element is the one searched for
<0 - if the search should continue in the first half
>0 - if the search should continue in the second half
If no matching element is found, raise an error.
Examples:
#(1 3 5 7 11 15 23) findBinary:[:arg| 11 - arg]
"
^self findBinary: aBlock ifNone: [self errorNotFound: aBlock]!
----- Method: SequenceableCollection>>findBinary:ifNone: (in category 'enumerating') -----
findBinary: aBlock ifNone: exceptionBlock
"Search for an element in the receiver using binary search.
The argument aBlock is a one-element block returning
0 - if the element is the one searched for
<0 - if the search should continue in the first half
>0 - if the search should continue in the second half
If no matching element is found, evaluate exceptionBlock."
| index low high test item |
low _ 1.
high _ self size.
[index _ high + low // 2.
low > high] whileFalse:[
test _ aBlock value: (item _ self at: index).
test = 0
ifTrue:[^item]
ifFalse:[test > 0
ifTrue: [low _ index + 1]
ifFalse: [high _ index - 1]]].
^exceptionBlock value!
----- Method: SequenceableCollection>>findBinaryIndex: (in category 'enumerating') -----
findBinaryIndex: aBlock
"Search for an element in the receiver using binary search.
The argument aBlock is a one-element block returning
0 - if the element is the one searched for
<0 - if the search should continue in the first half
>0 - if the search should continue in the second half
If no matching element is found, raise an error.
Examples:
#(1 3 5 7 11 15 23) findBinaryIndex:[:arg| 11 - arg]
"
^self findBinaryIndex: aBlock ifNone: [self errorNotFound: aBlock]!
----- Method: SequenceableCollection>>findBinaryIndex:ifNone: (in category 'enumerating') -----
findBinaryIndex: aBlock ifNone: exceptionBlock
"Search for an element in the receiver using binary search.
The argument aBlock is a one-element block returning
0 - if the element is the one searched for
<0 - if the search should continue in the first half
>0 - if the search should continue in the second half
If no matching element is found, evaluate exceptionBlock."
| index low high test |
low _ 1.
high _ self size.
[index _ high + low // 2.
low > high] whileFalse:[
test _ aBlock value: (self at: index).
test = 0
ifTrue:[^index]
ifFalse:[test > 0
ifTrue: [low _ index + 1]
ifFalse: [high _ index - 1]]].
^exceptionBlock value!
----- Method: SequenceableCollection>>findFirst: (in category 'enumerating') -----
findFirst: aBlock
"Return the index of my first element for which aBlock evaluates as true."
| index |
index _ 0.
[(index _ index + 1) <= self size] whileTrue:
[(aBlock value: (self at: index)) ifTrue: [^index]].
^ 0!
----- Method: SequenceableCollection>>findLast: (in category 'enumerating') -----
findLast: aBlock
"Return the index of my last element for which aBlock evaluates as true."
| index |
index _ self size + 1.
[(index _ index - 1) >= 1] whileTrue:
[(aBlock value: (self at: index)) ifTrue: [^index]].
^ 0!
----- Method: SequenceableCollection>>first (in category 'accessing') -----
first
"Answer the first element of the receiver."
^ self at: 1!
----- Method: SequenceableCollection>>first: (in category 'accessing') -----
first: n
"Answer the first n elements of the receiver.
Raise an error if there are not enough elements."
^ self copyFrom: 1 to: n!
----- Method: SequenceableCollection>>forceTo:paddingStartWith: (in category 'copying') -----
forceTo: length paddingStartWith: elem
"Force the length of the collection to length, padding
the beginning of the result if necessary with elem.
Note that this makes a copy."
| newCollection padLen |
newCollection _ self species new: length.
padLen _ length - self size max: 0.
newCollection
from: 1
to: padLen
put: elem.
newCollection
replaceFrom: padLen + 1
to: ((padLen + self size) min: length)
with: self
startingAt: 1.
^ newCollection!
----- Method: SequenceableCollection>>forceTo:paddingWith: (in category 'copying') -----
forceTo: length paddingWith: elem
"Force the length of the collection to length, padding
if necessary with elem. Note that this makes a copy."
| newCollection copyLen |
newCollection _ self species new: length.
copyLen _ self size min: length.
newCollection replaceFrom: 1 to: copyLen with: self startingAt: 1.
newCollection from: copyLen + 1 to: length put: elem.
^ newCollection!
----- Method: SequenceableCollection>>fourth (in category 'accessing') -----
fourth
"Answer the fourth element of the receiver.
Raise an error if there are not enough elements."
^ self at: 4!
----- Method: SequenceableCollection>>from:to:do: (in category 'enumerating') -----
from: start to: stop do: aBlock
"Evaluate aBlock for all elements between start and stop (inclusive)."
start to: stop do: [:index | aBlock value: (self at: index)]!
----- Method: SequenceableCollection>>from:to:put: (in category 'accessing') -----
from: startIndex to: endIndex put: anObject
"Put anObject in all indexes between startIndex
and endIndex. Very fast. Faster than to:do: for
more than 26 positions. Answer anObject"
| written toWrite thisWrite |
startIndex > endIndex ifTrue: [^self].
self at: startIndex put: anObject.
written _ 1.
toWrite _ endIndex - startIndex + 1.
[written < toWrite] whileTrue:
[
thisWrite _ written min: toWrite - written.
self
replaceFrom: startIndex + written
to: startIndex + written + thisWrite - 1
with: self startingAt: startIndex.
written _ written + thisWrite
].
^anObject!
----- Method: SequenceableCollection>>groupsOf:atATimeCollect: (in category 'enumerating') -----
groupsOf: n atATimeCollect: aBlock
"Evaluate aBlock with my elements taken n at a time. Ignore any
leftovers at the end.
Allows use of a flattened
array for things that naturally group into groups of n.
If aBlock has a single argument, pass it an array of n items,
otherwise, pass the items as separate arguments.
See also pairsDo:"
| passArray args |
passArray := aBlock numArgs = 1.
^(n
to: self size
by: n)
collect: [:index |
args := (self copyFrom: index - n + 1 to: index) asArray.
passArray
ifTrue: [aBlock value: args]
ifFalse: [aBlock valueWithArguments: args]]!
----- Method: SequenceableCollection>>groupsOf:atATimeDo: (in category 'enumerating') -----
groupsOf: n atATimeDo: aBlock
"Evaluate aBlock with my elements taken n at a time. Ignore any leftovers at the end.
Allows use of a flattened
array for things that naturally group into groups of n.
If aBlock has a single argument, pass it an array of n items,
otherwise, pass the items as separate arguments.
See also pairsDo:"
| passArray args |
passArray := (aBlock numArgs = 1).
n
to: self size
by: n
do: [:index |
args := (self copyFrom: index - n + 1 to: index) asArray.
passArray ifTrue: [ aBlock value: args ]
ifFalse: [ aBlock valueWithArguments: args ]].!
----- Method: SequenceableCollection>>hasEqualElements: (in category 'comparing') -----
hasEqualElements: otherCollection
"Answer whether the receiver's size is the same as otherCollection's
size, and each of the receiver's elements equal the corresponding
element of otherCollection.
This should probably replace the current definition of #= ."
| size |
(otherCollection isKindOf: SequenceableCollection) ifFalse: [^ false].
(size _ self size) = otherCollection size ifFalse: [^ false].
1 to: size do:
[:index |
(self at: index) = (otherCollection at: index) ifFalse: [^ false]].
^ true!
----- Method: SequenceableCollection>>hash (in category 'comparing') -----
hash
| hash |
hash _ self species hash.
1 to: self size do: [:i | hash _ (hash + (self at: i) hash) hashMultiply].
^hash!
----- Method: SequenceableCollection>>identityIndexOf: (in category 'accessing') -----
identityIndexOf: anElement
"Answer the index of anElement within the receiver. If the receiver does
not contain anElement, answer 0."
^self identityIndexOf: anElement ifAbsent: [0]!
----- Method: SequenceableCollection>>identityIndexOf:ifAbsent: (in category 'accessing') -----
identityIndexOf: anElement ifAbsent: exceptionBlock
"Answer the index of anElement within the receiver. If the receiver does
not contain anElement, answer the result of evaluating the argument,
exceptionBlock."
1 to: self size do:
[:i | (self at: i) == anElement ifTrue: [^ i]].
^ exceptionBlock value!
----- Method: SequenceableCollection>>includes: (in category 'testing') -----
includes: anObject
"Answer whether anObject is one of the receiver's elements."
^ (self indexOf: anObject) ~= 0!
----- Method: SequenceableCollection>>indexOf: (in category 'accessing') -----
indexOf: anElement
"Answer the index of the first occurence of anElement within the
receiver. If the receiver does not contain anElement, answer 0."
^ self indexOf: anElement ifAbsent: [0]!
----- Method: SequenceableCollection>>indexOf:ifAbsent: (in category 'accessing') -----
indexOf: anElement ifAbsent: exceptionBlock
"Answer the index of the first occurence of anElement within the
receiver. If the receiver does not contain anElement, answer the
result of evaluating the argument, exceptionBlock."
^ self indexOf: anElement startingAt: 1 ifAbsent: exceptionBlock!
----- Method: SequenceableCollection>>indexOf:startingAt:ifAbsent: (in category 'accessing') -----
indexOf: anElement startingAt: start ifAbsent: exceptionBlock
"Answer the index of the first occurence of anElement after start
within the receiver. If the receiver does not contain anElement,
answer the result of evaluating the argument, exceptionBlock."
start to: self size do:
[:index |
(self at: index) = anElement ifTrue: [^ index]].
^ exceptionBlock value!
----- Method: SequenceableCollection>>indexOfSubCollection:startingAt: (in category 'accessing') -----
indexOfSubCollection: aSubCollection startingAt: anIndex
"Answer the index of the receiver's first element, such that that element
equals the first element of aSubCollection, and the next elements equal
the rest of the elements of aSubCollection. Begin the search at element
anIndex of the receiver. If no such match is found, answer 0."
^self
indexOfSubCollection: aSubCollection
startingAt: anIndex
ifAbsent: [0]!
----- Method: SequenceableCollection>>indexOfSubCollection:startingAt:ifAbsent: (in category 'accessing') -----
indexOfSubCollection: sub startingAt: start ifAbsent: exceptionBlock
"Answer the index of the receiver's first element, such that that element
equals the first element of sub, and the next elements equal
the rest of the elements of sub. Begin the search at element
start of the receiver. If no such match is found, answer the result of
evaluating argument, exceptionBlock."
| first index |
sub isEmpty ifTrue: [^ exceptionBlock value].
first _ sub first.
start to: self size - sub size + 1 do:
[:startIndex |
(self at: startIndex) = first ifTrue:
[index _ 1.
[(self at: startIndex+index-1) = (sub at: index)]
whileTrue:
[index = sub size ifTrue: [^startIndex].
index _ index+1]]].
^ exceptionBlock value!
----- Method: SequenceableCollection>>integerAt: (in category 'accessing') -----
integerAt: index
"Return the integer at the given index"
^self at: index!
----- Method: SequenceableCollection>>integerAt:put: (in category 'accessing') -----
integerAt: index put: value
"Return the integer at the given index"
^self at: index put: value!
----- Method: SequenceableCollection>>isSequenceable (in category 'testing') -----
isSequenceable
^ true!
----- Method: SequenceableCollection>>keysAndValuesDo: (in category 'enumerating') -----
keysAndValuesDo: aBlock
"Enumerate the receiver with all the keys (aka indices) and values."
1 to: self size do: [:index | aBlock value: index value: (self at: index)]!
----- Method: SequenceableCollection>>last (in category 'accessing') -----
last
"Answer the last element of the receiver.
Raise an error if the collection is empty."
| size |
(size _ self size) = 0 ifTrue: [self errorEmptyCollection].
^ self at: size!
----- Method: SequenceableCollection>>last: (in category 'accessing') -----
last: n
"Answer the last n elements of the receiver.
Raise an error if there are not enough elements."
| size |
size _ self size.
^ self copyFrom: size - n + 1 to: size!
----- Method: SequenceableCollection>>lastIndexOf: (in category 'accessing') -----
lastIndexOf: anElement
"Answer the index of the last occurence of anElement within the
receiver. If the receiver does not contain anElement, answer 0."
^ self lastIndexOf: anElement startingAt: self size ifAbsent: [0]!
----- Method: SequenceableCollection>>lastIndexOf:ifAbsent: (in category 'accessing') -----
lastIndexOf: anElement ifAbsent: exceptionBlock
"Answer the index of the last occurence of anElement within the
receiver. If the receiver does not contain anElement, answer the
result of evaluating the argument, exceptionBlock."
^self lastIndexOf: anElement startingAt: self size ifAbsent: exceptionBlock!
----- Method: SequenceableCollection>>lastIndexOf:startingAt:ifAbsent: (in category 'accessing') -----
lastIndexOf: anElement startingAt: lastIndex ifAbsent: exceptionBlock
"Answer the index of the last occurence of anElement within the
receiver. If the receiver does not contain anElement, answer the
result of evaluating the argument, exceptionBlock."
lastIndex to: 1 by: -1 do:
[:index |
(self at: index) = anElement ifTrue: [^ index]].
^ exceptionBlock value!
----- Method: SequenceableCollection>>middle (in category 'accessing') -----
middle
"Answer the middle element of the receiver."
self emptyCheck.
^ self at: self size // 2 + 1!
----- Method: SequenceableCollection>>nextToLast (in category 'enumerating') -----
nextToLast
^self at: self size - 1!
----- Method: SequenceableCollection>>ninth (in category 'accessing') -----
ninth
"Answer the ninth element of the receiver.
Raise an error if there are not enough elements."
^ self at: 9!
----- Method: SequenceableCollection>>overlappingPairsCollect: (in category 'enumerating') -----
overlappingPairsCollect: aBlock
"Answer the result of evaluating aBlock with all of the overlapping pairs of my elements."
| retval |
retval _ self species new: self size - 1.
1 to: self size - 1
do: [:i | retval at: i put: (aBlock value: (self at: i) value: (self at: i + 1)) ].
^retval!
----- Method: SequenceableCollection>>overlappingPairsDo: (in category 'enumerating') -----
overlappingPairsDo: aBlock
"Emit overlapping pairs of my elements into aBlock"
1 to: self size - 1
do: [:i | aBlock value: (self at: i) value: (self at: i + 1)]!
----- Method: SequenceableCollection>>overlappingPairsWithIndexDo: (in category 'enumerating') -----
overlappingPairsWithIndexDo: aBlock
"Emit overlapping pairs of my elements into aBlock, along with an index."
1 to: self size - 1
do: [:i | aBlock value: (self at: i) value: (self at: i + 1) value: i ]!
----- Method: SequenceableCollection>>paddedWith:do: (in category 'enumerating') -----
paddedWith: otherCollection do: twoArgBlock
"Evaluate twoArgBlock with corresponding elements from this collection and otherCollection.
Missing elements from either will be passed as nil."
1 to: (self size max: otherCollection size) do:
[:index | twoArgBlock value: (self at: index ifAbsent: [])
value: (otherCollection at: index ifAbsent: [])]!
----- Method: SequenceableCollection>>pairsCollect: (in category 'enumerating') -----
pairsCollect: aBlock
"Evaluate aBlock with my elements taken two at a time, and return an Array with the results"
^ (1 to: self size // 2) collect:
[:index | aBlock value: (self at: 2 * index - 1) value: (self at: 2 * index)]
"
#(1 'fred' 2 'charlie' 3 'elmer') pairsCollect:
[:a :b | b, ' is number ', a printString]
"!
----- Method: SequenceableCollection>>pairsDo: (in category 'enumerating') -----
pairsDo: aBlock
"Evaluate aBlock with my elements taken two at a time. If there's an odd number of items, ignore the last one. Allows use of a flattened array for things that naturally group into pairs. See also pairsCollect:"
1 to: self size // 2 do:
[:index | aBlock value: (self at: 2 * index - 1) value: (self at: 2 * index)]
"
#(1 'fred' 2 'charlie' 3 'elmer') pairsDo:
[:a :b | Transcript cr; show: b, ' is number ', a printString]
"!
----- Method: SequenceableCollection>>permutationsDo: (in category 'enumerating') -----
permutationsDo: aBlock
"Repeatly value aBlock with a single copy of the receiver. Reorder the copy
so that aBlock is presented all (self size factorial) possible permutations."
"(1 to: 4) permutationsDo: [:each | Transcript cr; show: each printString]"
self shallowCopy permutationsStartingAt: 1 do: aBlock!
----- Method: SequenceableCollection>>permutationsStartingAt:do: (in category 'private') -----
permutationsStartingAt: anInteger do: aBlock
"#(1 2 3 4) permutationsDo: [:each | Transcript cr; show: each printString]"
anInteger > self size ifTrue: [^self].
anInteger = self size ifTrue: [^aBlock value: self].
anInteger to: self size do:
[:i | self swap: anInteger with: i.
self permutationsStartingAt: anInteger + 1 do: aBlock.
self swap: anInteger with: i]!
----- Method: SequenceableCollection>>polynomialEval: (in category 'enumerating') -----
polynomialEval: thisX
| sum valToPower |
"Treat myself as the coeficients of a polynomial in X. Evaluate it with thisX. First element is the constant and last is the coeficient for the highest power."
" #(1 2 3) polynomialEval: 2 " "is 3*X^2 + 2*X + 1 with X = 2"
sum _ self first.
valToPower _ thisX.
2 to: self size do: [:ind |
sum _ sum + ((self at: ind) * valToPower).
valToPower _ valToPower * thisX].
^ sum!
----- Method: SequenceableCollection>>readStream (in category 'converting') -----
readStream
^ ReadStream on: self!
----- Method: SequenceableCollection>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: anExceptionBlock
"SequencableCollections cannot implement removing."
self shouldNotImplement!
----- Method: SequenceableCollection>>replace: (in category 'enumerating') -----
replace: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Collect the resulting values into self."
1 to: self size do: [ :index |
self at: index put: (aBlock value: (self at: index)) ]!
----- Method: SequenceableCollection>>replaceAll:with: (in category 'accessing') -----
replaceAll: oldObject with: newObject
"Replace all occurences of oldObject with newObject"
| index |
index _ self
indexOf: oldObject
startingAt: 1
ifAbsent: [0].
[index = 0]
whileFalse:
[self at: index put: newObject.
index _ self
indexOf: oldObject
startingAt: index + 1
ifAbsent: [0]]!
----- Method: SequenceableCollection>>replaceFrom:to:with: (in category 'accessing') -----
replaceFrom: start to: stop with: replacement
"This destructively replaces elements from start to stop in the receiver.
Answer the receiver itself. Use copyReplaceFrom:to:with: for
insertion/deletion which may alter the size of the result."
replacement size = (stop - start + 1)
ifFalse: [self error: 'Size of replacement doesnt match'].
^self replaceFrom: start to: stop with: replacement startingAt: 1!
----- Method: SequenceableCollection>>replaceFrom:to:with:startingAt: (in category 'accessing') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
"This destructively replaces elements from start to stop in the receiver
starting at index, repStart, in the sequenceable collection,
replacementCollection. Answer the receiver. No range checks are
performed."
| index repOff |
repOff _ repStart - start.
index _ start - 1.
[(index _ index + 1) <= stop]
whileTrue: [self at: index put: (replacement at: repOff + index)]!
----- Method: SequenceableCollection>>reverse (in category 'converting') -----
reverse
^ self reversed!
----- Method: SequenceableCollection>>reverseDo: (in category 'enumerating') -----
reverseDo: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument,
starting with the last element and taking each in sequence up to the
first. For SequenceableCollections, this is the reverse of the enumeration
for do:."
self size to: 1 by: -1 do: [:index | aBlock value: (self at: index)]!
----- Method: SequenceableCollection>>reverseWith:do: (in category 'enumerating') -----
reverseWith: aSequenceableCollection do: aBlock
"Evaluate aBlock with each of the receiver's elements, in reverse order,
along with the
corresponding element, also in reverse order, from
aSequencableCollection. "
self size ~= aSequenceableCollection size ifTrue: [^ self errorNoMatch].
self size
to: 1
by: -1
do: [:index | aBlock value: (self at: index)
value: (aSequenceableCollection at: index)]!
----- Method: SequenceableCollection>>reversed (in category 'converting') -----
reversed
"Answer a copy of the receiver with element order reversed."
"Example: 'frog' reversed"
| n result src |
n _ self size.
result _ self species new: n.
src _ n + 1.
1 to: n do: [:i | result at: i put: (self at: (src _ src - 1))].
^ result
!
----- Method: SequenceableCollection>>second (in category 'accessing') -----
second
"Answer the second element of the receiver.
Raise an error if there are not enough elements."
^ self at: 2!
----- Method: SequenceableCollection>>select: (in category 'enumerating') -----
select: aBlock
"Refer to the comment in Collection|select:."
| aStream |
aStream _ WriteStream on: (self species new: self size).
1 to: self size do:
[:index |
(aBlock value: (self at: index))
ifTrue: [aStream nextPut: (self at: index)]].
^ aStream contents!
----- Method: SequenceableCollection>>seventh (in category 'accessing') -----
seventh
"Answer the seventh element of the receiver.
Raise an error if there are not enough elements."
^ self at: 7!
----- Method: SequenceableCollection>>shallowCopy (in category 'copying') -----
shallowCopy
^self copyFrom: 1 to: self size!
----- Method: SequenceableCollection>>shuffled (in category 'copying') -----
shuffled
^ self shuffledBy: Collection randomForPicking
"Examples:
($A to: $Z) shuffled
"!
----- Method: SequenceableCollection>>shuffledBy: (in category 'shuffling') -----
shuffledBy: aRandom
| copy |
copy _ self shallowCopy.
copy size to: 1 by: -1 do:
[:i | copy swap: i with: ((1 to: i) atRandom: aRandom)].
^ copy!
----- Method: SequenceableCollection>>sixth (in category 'accessing') -----
sixth
"Answer the sixth element of the receiver.
Raise an error if there are not enough elements."
^ self at: 6!
----- Method: SequenceableCollection>>sortBy: (in category 'copying') -----
sortBy: aBlock
"Create a copy that is sorted. Sort criteria is the block that accepts two arguments.
When the block is true, the first arg goes first ([:a :b | a > b] sorts in descending
order)."
^ (self asSortedCollection: aBlock) asOrderedCollection!
----- Method: SequenceableCollection>>swap:with: (in category 'accessing') -----
swap: oneIndex with: anotherIndex
"Move the element at oneIndex to anotherIndex, and vice-versa."
| element |
element _ self at: oneIndex.
self at: oneIndex put: (self at: anotherIndex).
self at: anotherIndex put: element!
----- Method: SequenceableCollection>>third (in category 'accessing') -----
third
"Answer the third element of the receiver.
Raise an error if there are not enough elements."
^ self at: 3!
----- Method: SequenceableCollection>>upTo: (in category 'enumerating') -----
upTo: anObject
"Deprecated. Use copyUpTo:"
^ self copyUpTo: anObject!
----- Method: SequenceableCollection>>with:collect: (in category 'enumerating') -----
with: otherCollection collect: twoArgBlock
"Collect and return the result of evaluating twoArgBlock with corresponding elements from this collection and otherCollection."
| result |
otherCollection size = self size ifFalse: [self error: 'otherCollection must be the same size'].
result _ self species new: self size.
1 to: self size do:
[:index | result at: index put:
(twoArgBlock
value: (self at: index)
value: (otherCollection at: index))].
^ result!
----- Method: SequenceableCollection>>with:do: (in category 'enumerating') -----
with: otherCollection do: twoArgBlock
"Evaluate twoArgBlock with corresponding elements from this collection and otherCollection."
otherCollection size = self size ifFalse: [self error: 'otherCollection must be the same size'].
1 to: self size do:
[:index |
twoArgBlock value: (self at: index)
value: (otherCollection at: index)]!
----- Method: SequenceableCollection>>withIndexCollect: (in category 'enumerating') -----
withIndexCollect: elementAndIndexBlock
"Just like with:collect: except that the iteration index supplies the second argument to the block."
| result |
result _ self species new: self size.
1 to: self size do:
[:index | result at: index put:
(elementAndIndexBlock
value: (self at: index)
value: index)].
^ result!
----- Method: SequenceableCollection>>withIndexDo: (in category 'enumerating') -----
withIndexDo: elementAndIndexBlock
"Just like with:do: except that the iteration index supplies the second argument to the block."
1 to: self size do:
[:index |
elementAndIndexBlock
value: (self at: index)
value: index]!
----- Method: SequenceableCollection>>writeStream (in category 'converting') -----
writeStream
^ WriteStream on: self!
Collection subclass: #Set
instanceVariableNames: 'tally array'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!Set commentStamp: '<historical>' prior: 0!
I represent a set of objects without duplicates. I can hold anything that responds to
#hash and #=, except for nil. My instances will automatically grow, if necessary,
Note that I rely on #=, not #==. If you want a set using #==, use IdentitySet.
Instance structure:
array An array whose non-nil elements are the elements of the set,
and whose nil elements are empty slots. There is always at least one nil.
In fact I try to keep my "load" at 75% or less so that hashing will work well.
tally The number of elements in the set. The array size is always greater than this.
The core operation is #findElementOrNil:, which either finds the position where an
object is stored in array, if it is present, or finds a suitable position holding nil, if
its argument is not present in array,!
Set subclass: #Dictionary
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!Dictionary commentStamp: '<historical>' prior: 0!
I represent a set of elements that can be viewed from one of two perspectives: a set of associations, or a container of values that are externally named where the name can be any object that responds to =. The external name is referred to as the key. I inherit many operations from Set.!
----- Method: Dictionary class>>newFrom: (in category 'instance creation') -----
newFrom: aDict
"Answer an instance of me containing the same associations as aDict.
Error if any key appears twice."
| newDictionary |
newDictionary _ self new: aDict size.
aDict associationsDo:
[:x |
(newDictionary includesKey: x key)
ifTrue: [self error: 'Duplicate key: ', x key printString]
ifFalse: [newDictionary add: x]].
^ newDictionary
" NewDictionary newFrom: {1->#a. 2->#b. 3->#c}
{1->#a. 2->#b. 3->#c} as: NewDictionary
NewDictionary newFrom: {1->#a. 2->#b. 1->#c}
{1->#a. 2->#b. 1->#c} as: NewDictionary
"!
----- Method: Dictionary class>>newFromPairs: (in category 'instance creation') -----
newFromPairs: anArray
"Answer an instance of me associating (anArray at:i) to (anArray at: i+i)
for each odd i. anArray must have an even number of entries."
| newDictionary |
newDictionary := self new: (anArray size/2).
1 to: (anArray size-1) by: 2 do: [ :i|
newDictionary at: (anArray at: i) put: (anArray at: i+1).
].
^ newDictionary
" Dictionary newFromPairs: {'Red' . Color red . 'Blue' . Color blue . 'Green' . Color green}. "!
----- Method: Dictionary>>= (in category 'comparing') -----
= aDictionary
"Two dictionaries are equal if
(a) they are the same 'kind' of thing.
(b) they have the same set of keys.
(c) for each (common) key, they have the same value"
self == aDictionary ifTrue: [ ^ true ].
(aDictionary isKindOf: Dictionary) ifFalse: [^false].
self size = aDictionary size ifFalse: [^false].
self associationsDo: [:assoc|
(aDictionary at: assoc key ifAbsent: [^false]) = assoc value
ifFalse: [^false]].
^true
!
----- Method: Dictionary>>add: (in category 'adding') -----
add: anAssociation
| index element |
index _ self findElementOrNil: anAssociation key.
element _ array at: index.
element == nil
ifTrue: [self atNewIndex: index put: anAssociation]
ifFalse: [element value: anAssociation value].
^ anAssociation!
----- Method: Dictionary>>addAll: (in category 'adding') -----
addAll: aKeyedCollection
aKeyedCollection == self ifFalse: [
aKeyedCollection keysAndValuesDo: [:key :value |
self at: key put: value]].
^aKeyedCollection!
----- Method: Dictionary>>associationAt: (in category 'accessing') -----
associationAt: key
^ self associationAt: key ifAbsent: [self errorKeyNotFound]!
----- Method: Dictionary>>associationAt:ifAbsent: (in category 'accessing') -----
associationAt: key ifAbsent: aBlock
"Answer the association with the given key.
If key is not found, return the result of evaluating aBlock."
| index assoc |
index _ self findElementOrNil: key.
assoc _ array at: index.
nil == assoc ifTrue: [ ^ aBlock value ].
^ assoc!
----- Method: Dictionary>>associationDeclareAt: (in category 'accessing') -----
associationDeclareAt: aKey
"Return an existing association, or create and return a new one. Needed as a single message by ImageSegment.prepareToBeSaved."
| existing |
^ self associationAt: aKey ifAbsent: [
(Undeclared includesKey: aKey)
ifTrue:
[existing _ Undeclared associationAt: aKey.
Undeclared removeKey: aKey.
self add: existing]
ifFalse:
[self add: aKey -> false]]!
----- Method: Dictionary>>associations (in category 'accessing') -----
associations
"Answer a Collection containing the receiver's associations."
| out |
out _ WriteStream on: (Array new: self size).
self associationsDo: [:value | out nextPut: value].
^ out contents!
----- Method: Dictionary>>associationsDo: (in category 'enumerating') -----
associationsDo: aBlock
"Evaluate aBlock for each of the receiver's elements (key/value
associations)."
super do: aBlock!
----- Method: Dictionary>>associationsSelect: (in category 'enumerating') -----
associationsSelect: aBlock
"Evaluate aBlock with each of my associations as the argument. Collect
into a new dictionary, only those associations for which aBlock evaluates
to true."
| newCollection |
newCollection _ self species new.
self associationsDo:
[:each |
(aBlock value: each) ifTrue: [newCollection add: each]].
^newCollection!
----- Method: Dictionary>>at: (in category 'accessing') -----
at: key
"Answer the value associated with the key."
^ self at: key ifAbsent: [self errorKeyNotFound]!
----- Method: Dictionary>>at:ifAbsent: (in category 'accessing') -----
at: key ifAbsent: aBlock
"Answer the value associated with the key or, if key isn't found,
answer the result of evaluating aBlock."
| assoc |
assoc _ array at: (self findElementOrNil: key).
assoc ifNil: [^ aBlock value].
^ assoc value!
----- Method: Dictionary>>at:ifAbsentPut: (in category 'accessing') -----
at: key ifAbsentPut: aBlock
"Return the value at the given key.
If key is not included in the receiver store the result
of evaluating aBlock as new value."
^ self at: key ifAbsent: [self at: key put: aBlock value]!
----- Method: Dictionary>>at:ifPresent: (in category 'accessing') -----
at: key ifPresent: aBlock
"Lookup the given key in the receiver. If it is present, answer the value of evaluating the given block with the value associated with the key. Otherwise, answer nil."
| v |
v _ self at: key ifAbsent: [^ nil].
^ aBlock value: v
!
----- Method: Dictionary>>at:ifPresentAndInMemory: (in category 'accessing') -----
at: key ifPresentAndInMemory: aBlock
"Lookup the given key in the receiver. If it is present, answer the value of evaluating the given block with the value associated with the key. Otherwise, answer nil."
| v |
v _ self at: key ifAbsent: [^ nil].
v isInMemory ifFalse: [^ nil].
^ aBlock value: v
!
----- Method: Dictionary>>at:put: (in category 'accessing') -----
at: key put: anObject
"Set the value at key to be anObject. If key is not found, create a
new entry for key and set is value to anObject. Answer anObject."
| index assoc |
index _ self findElementOrNil: key.
assoc _ array at: index.
assoc
ifNil: [self atNewIndex: index put: (Association key: key value: anObject)]
ifNotNil: [assoc value: anObject].
^ anObject!
----- Method: Dictionary>>collect: (in category 'enumerating') -----
collect: aBlock
"Evaluate aBlock with each of my values as the argument. Collect the
resulting values into a collection that is like me. Answer with the new
collection."
| newCollection |
newCollection _ OrderedCollection new: self size.
self do: [:each | newCollection add: (aBlock value: each)].
^ newCollection!
----- Method: Dictionary>>copy (in category 'private') -----
copy
"Must copy the associations, or later store will affect both the
original and the copy"
^ self shallowCopy withArray:
(array collect: [:assoc |
assoc ifNil: [nil]
ifNotNil: [Association key: assoc key
value: assoc value]])!
----- Method: Dictionary>>customizeExplorerContents (in category 'accessing') -----
customizeExplorerContents
^ true.
!
----- Method: Dictionary>>declare:from: (in category 'adding') -----
declare: key from: aDictionary
"Add key to the receiver. If key already exists, do nothing. If aDictionary
includes key, then remove it from aDictionary and use its association as
the element of the receiver."
(self includesKey: key) ifTrue: [^ self].
(aDictionary includesKey: key)
ifTrue:
[self add: (aDictionary associationAt: key).
aDictionary removeKey: key]
ifFalse:
[self add: key -> nil]!
----- Method: Dictionary>>do: (in category 'enumerating') -----
do: aBlock
super do: [:assoc | aBlock value: assoc value]!
----- Method: Dictionary>>errorKeyNotFound (in category 'private') -----
errorKeyNotFound
self error: 'key not found'!
----- Method: Dictionary>>errorValueNotFound (in category 'private') -----
errorValueNotFound
self error: 'value not found'!
----- Method: Dictionary>>explorerContentsWithIndexCollect: (in category 'user interface') -----
explorerContentsWithIndexCollect: twoArgBlock
| sortedKeys |
sortedKeys _ self keys asSortedCollection: [:x :y |
((x isString and: [y isString])
or: [x isNumber and: [y isNumber]])
ifTrue: [x < y]
ifFalse: [x class == y class
ifTrue: [x printString < y printString]
ifFalse: [x class name < y class name]]].
^ sortedKeys collect: [:k | twoArgBlock value: (self at: k) value: k].
!
----- Method: Dictionary>>flattenOnStream: (in category 'filter streaming') -----
flattenOnStream:aStream
^aStream writeDictionary:self.
!
----- Method: Dictionary>>hasBindingThatBeginsWith: (in category 'testing') -----
hasBindingThatBeginsWith: aString
"Answer true if the receiver has a key that begins with aString, false otherwise"
self keysDo:[:each |
(each beginsWith: aString)
ifTrue:[^true]].
^false!
----- Method: Dictionary>>includes: (in category 'testing') -----
includes: anObject
self do: [:each | anObject = each ifTrue: [^true]].
^false!
----- Method: Dictionary>>includesAssociation: (in category 'testing') -----
includesAssociation: anAssociation
^ (self
associationAt: anAssociation key
ifAbsent: [ ^ false ]) value = anAssociation value
!
----- Method: Dictionary>>includesIdentity: (in category 'testing') -----
includesIdentity: anObject
"Answer whether anObject is one of the values of the receiver. Contrast #includes: in which there is only an equality check, here there is an identity check"
self do: [:each | anObject == each ifTrue: [^ true]].
^ false!
----- Method: Dictionary>>includesKey: (in category 'testing') -----
includesKey: key
"Answer whether the receiver has a key equal to the argument, key."
self at: key ifAbsent: [^false].
^true!
----- Method: Dictionary>>keyAt: (in category 'private') -----
keyAt: index
"May be overridden by subclasses so that fixCollisions will work"
| assn |
assn _ array at: index.
assn == nil ifTrue: [^ nil]
ifFalse: [^ assn key]!
----- Method: Dictionary>>keyAtIdentityValue: (in category 'accessing') -----
keyAtIdentityValue: value
"Answer the key that is the external name for the argument, value. If
there is none, answer nil.
Note: There can be multiple keys with the same value. Only one is returned."
^self keyAtIdentityValue: value ifAbsent: [self errorValueNotFound]!
----- Method: Dictionary>>keyAtIdentityValue:ifAbsent: (in category 'accessing') -----
keyAtIdentityValue: value ifAbsent: exceptionBlock
"Answer the key that is the external name for the argument, value. If
there is none, answer the result of evaluating exceptionBlock.
Note: There can be multiple keys with the same value. Only one is returned."
self associationsDo:
[:association | value == association value ifTrue: [^association key]].
^exceptionBlock value!
----- Method: Dictionary>>keyAtValue: (in category 'accessing') -----
keyAtValue: value
"Answer the key that is the external name for the argument, value. If
there is none, answer nil."
^self keyAtValue: value ifAbsent: [self errorValueNotFound]!
----- Method: Dictionary>>keyAtValue:ifAbsent: (in category 'accessing') -----
keyAtValue: value ifAbsent: exceptionBlock
"Answer the key that is the external name for the argument, value. If
there is none, answer the result of evaluating exceptionBlock.
: Use =, not ==, so stings like 'this' can be found. Note that MethodDictionary continues to use == so it will be fast."
self associationsDo:
[:association | value = association value ifTrue: [^association key]].
^exceptionBlock value!
----- Method: Dictionary>>keyForIdentity: (in category 'accessing') -----
keyForIdentity: anObject
"If anObject is one of the values of the receive, return its key, else return nil. Contrast #keyAtValue: in which there is only an equality check, here there is an identity check"
self associationsDo: [:assoc | assoc value == anObject ifTrue: [^ assoc key]].
^ nil!
----- Method: Dictionary>>keys (in category 'accessing') -----
keys
"Answer a Set containing the receiver's keys."
| aSet |
aSet _ Set new: self size.
self keysDo: [:key | aSet add: key].
^ aSet!
----- Method: Dictionary>>keysAndValuesDo: (in category 'enumerating') -----
keysAndValuesDo: aBlock
^self associationsDo:[:assoc|
aBlock value: assoc key value: assoc value].!
----- Method: Dictionary>>keysAndValuesRemove: (in category 'removing') -----
keysAndValuesRemove: keyValueBlock
"Removes all entries for which keyValueBlock returns true."
"When removing many items, you must not do it while iterating over the dictionary, since it may be changing. This method takes care of tallying the removals in a first pass, and then performing all the deletions afterward. Many places in the sytem could be simplified by using this method."
| removals |
removals _ OrderedCollection new.
self associationsDo:
[:assoc | (keyValueBlock value: assoc key value: assoc value)
ifTrue: [removals add: assoc key]].
removals do:
[:aKey | self removeKey: aKey]!
----- Method: Dictionary>>keysDo: (in category 'enumerating') -----
keysDo: aBlock
"Evaluate aBlock for each of the receiver's keys."
self associationsDo: [:association | aBlock value: association key]!
----- Method: Dictionary>>keysSortedSafely (in category 'accessing') -----
keysSortedSafely
"Answer a SortedCollection containing the receiver's keys."
| sortedKeys |
sortedKeys _ SortedCollection new: self size.
sortedKeys sortBlock:
[:x :y | "Should really be use <obj, string, num> compareSafely..."
((x isString and: [y isString])
or: [x isNumber and: [y isNumber]])
ifTrue: [x < y]
ifFalse: [x class == y class
ifTrue: [x printString < y printString]
ifFalse: [x class name < y class name]]].
self keysDo: [:each | sortedKeys addLast: each].
^ sortedKeys reSort!
----- Method: Dictionary>>noCheckAdd: (in category 'private') -----
noCheckAdd: anObject
"Must be defined separately for Dictionary because (self findElementOrNil:) expects a key, not an association. 9/7/96 tk"
array at: (self findElementOrNil: anObject key) put: anObject.
tally _ tally + 1!
----- Method: Dictionary>>occurrencesOf: (in category 'testing') -----
occurrencesOf: anObject
"Answer how many of the receiver's elements are equal to anObject."
| count |
count _ 0.
self do: [:each | anObject = each ifTrue: [count _ count + 1]].
^count!
----- Method: Dictionary>>printElementsOn: (in category 'printing') -----
printElementsOn: aStream
aStream nextPut: $(.
self size > 100
ifTrue: [aStream nextPutAll: 'size '.
self size printOn: aStream]
ifFalse: [self keysSortedSafely
do: [:key | aStream print: key;
nextPutAll: '->';
print: (self at: key);
space]].
aStream nextPut: $)!
----- Method: Dictionary>>rehash (in category 'private') -----
rehash
"Smalltalk rehash."
| newSelf |
newSelf _ self species new: self size.
self associationsDo: [:each | newSelf noCheckAdd: each].
array _ newSelf array!
----- Method: Dictionary>>remove: (in category 'removing') -----
remove: anObject
self shouldNotImplement!
----- Method: Dictionary>>remove:ifAbsent: (in category 'removing') -----
remove: anObject ifAbsent: exceptionBlock
self shouldNotImplement!
----- Method: Dictionary>>removeKey: (in category 'removing') -----
removeKey: key
"Remove key from the receiver.
If key is not in the receiver, notify an error."
^ self removeKey: key ifAbsent: [self errorKeyNotFound]!
----- Method: Dictionary>>removeKey:ifAbsent: (in category 'removing') -----
removeKey: key ifAbsent: aBlock
"Remove key (and its associated value) from the receiver. If key is not in
the receiver, answer the result of evaluating aBlock. Otherwise, answer
the value externally named by key."
| index assoc |
index _ self findElementOrNil: key.
assoc _ array at: index.
assoc == nil ifTrue: [ ^ aBlock value ].
array at: index put: nil.
tally _ tally - 1.
self fixCollisionsFrom: index.
^ assoc value!
----- Method: Dictionary>>removeUnreferencedKeys (in category 'removing') -----
removeUnreferencedKeys "Undeclared removeUnreferencedKeys"
^ self unreferencedKeys do: [:key | self removeKey: key].!
----- Method: Dictionary>>scanFor: (in category 'private') -----
scanFor: anObject
"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements."
| element start finish |
finish _ array size.
start _ (anObject hash \\ finish) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == nil or: [element key = anObject])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == nil or: [element key = anObject])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
----- Method: Dictionary>>select: (in category 'enumerating') -----
select: aBlock
"Evaluate aBlock with each of my values as the argument. Collect into a
new dictionary, only those associations for which aBlock evaluates to
true."
| newCollection |
newCollection _ self species new.
self associationsDo:
[:each |
(aBlock value: each value) ifTrue: [newCollection add: each]].
^newCollection!
----- Method: Dictionary>>storeOn: (in category 'printing') -----
storeOn: aStream
| noneYet |
aStream nextPutAll: '(('.
aStream nextPutAll: self class name.
aStream nextPutAll: ' new)'.
noneYet _ true.
self associationsDo:
[:each |
noneYet
ifTrue: [noneYet _ false]
ifFalse: [aStream nextPut: $;].
aStream nextPutAll: ' add: '.
aStream store: each].
noneYet ifFalse: [aStream nextPutAll: '; yourself'].
aStream nextPut: $)!
----- Method: Dictionary>>unreferencedKeys (in category 'removing') -----
unreferencedKeys
"TextConstants unreferencedKeys"
| n |
^'Scanning for references . . .'
displayProgressAt: Sensor cursorPoint
from: 0
to: self size
during:
[:bar |
n := 0.
self keys select:
[:key |
bar value: (n := n + 1).
(self systemNavigation allCallsOn: (self associationAt: key)) isEmpty]]!
----- Method: Dictionary>>valueAtNewKey:put:atIndex:declareFrom: (in category 'private') -----
valueAtNewKey: aKey put: anObject atIndex: index declareFrom: aDictionary
"Support for coordinating class variable and global declarations
with variables that have been put in Undeclared so as to
redirect all references to the undeclared variable."
(aDictionary includesKey: aKey)
ifTrue:
[self atNewIndex: index
put: ((aDictionary associationAt: aKey) value: anObject).
aDictionary removeKey: aKey]
ifFalse:
[self atNewIndex: index put: (Association key: aKey value: anObject)]!
----- Method: Dictionary>>values (in category 'accessing') -----
values
"Answer a Collection containing the receiver's values."
| out |
out _ WriteStream on: (Array new: self size).
self valuesDo: [:value | out nextPut: value].
^ out contents!
----- Method: Dictionary>>valuesDo: (in category 'enumerating') -----
valuesDo: aBlock
"Evaluate aBlock for each of the receiver's values."
self associationsDo: [:association | aBlock value: association value]!
Dictionary subclass: #IdentityDictionary
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!IdentityDictionary commentStamp: 'ls 06/15/02 22:35' prior: 0!
Like a Dictionary, except that keys are compared with #== instead of #= .
See the comment of IdentitySet for more information.!
----- Method: IdentityDictionary>>fasterKeys (in category 'private') -----
fasterKeys
"This was taking some time in publishing and we didn't really need a Set"
| answer index |
answer _ Array new: self size.
index _ 0.
self keysDo: [:key | answer at: (index _ index + 1) put: key].
^ answer!
----- Method: IdentityDictionary>>keyAtValue:ifAbsent: (in category 'accessing') -----
keyAtValue: value ifAbsent: exceptionBlock
"Answer the key that is the external name for the argument, value. If
there is none, answer the result of evaluating exceptionBlock."
self associationsDo:
[:association | value == association value ifTrue: [^ association key]].
^ exceptionBlock value!
----- Method: IdentityDictionary>>keys (in category 'private') -----
keys
"Answer a Set containing the receiver's keys."
| aSet |
aSet _ IdentitySet new: self size.
self keysDo: [:key | aSet add: key].
^ aSet!
----- Method: IdentityDictionary>>scanFor: (in category 'private') -----
scanFor: anObject
"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements."
| finish hash start element |
finish _ array size.
finish > 4096
ifTrue: [hash _ anObject identityHash * (finish // 4096)]
ifFalse: [hash _ anObject identityHash].
start _ (hash \\ finish) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == nil or: [element key == anObject])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == nil or: [element key == anObject])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
Dictionary subclass: #PluggableDictionary
instanceVariableNames: 'hashBlock equalBlock'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!PluggableDictionary commentStamp: '<historical>' prior: 0!
Class PluggableDictionary allows the redefinition of hashing and equality by clients. This is in particular useful if the clients know about specific properties of the objects stored in the dictionary. See the class comment of PluggableSet for an example.
Instance variables:
hashBlock <BlockContext> A one argument block used for hashing the elements.
equalBlock <BlockContext> A two argument block used for comparing the elements.
!
----- Method: PluggableDictionary class>>integerDictionary (in category 'instance creation') -----
integerDictionary
^ self new hashBlock: [:integer | integer hash \\ 1064164 * 1009]!
----- Method: PluggableDictionary>>copy (in category 'copying') -----
copy
^super copy postCopyBlocks!
----- Method: PluggableDictionary>>equalBlock (in category 'accessing') -----
equalBlock
"Return the block used for comparing the elements in the receiver."
^equalBlock!
----- Method: PluggableDictionary>>equalBlock: (in category 'accessing') -----
equalBlock: aBlock
"Set a new equality block. The block must accept two arguments and return true if the argumets are considered to be equal, false otherwise"
equalBlock _ aBlock.!
----- Method: PluggableDictionary>>hashBlock (in category 'accessing') -----
hashBlock
"Return the block used for hashing the elements in the receiver."
^hashBlock!
----- Method: PluggableDictionary>>hashBlock: (in category 'accessing') -----
hashBlock: aBlock
"Set a new hash block. The block must accept one argument and must return the hash value of the given argument."
hashBlock _ aBlock.!
----- Method: PluggableDictionary>>keys (in category 'accessing') -----
keys
"Answer a Set containing the receiver's keys."
| aSet |
aSet _ PluggableSet new: self size.
self equalBlock ifNotNil: [aSet equalBlock: self equalBlock fixTemps].
self hashBlock ifNotNil: [aSet hashBlock: self hashBlock fixTemps].
self keysDo: [:key | aSet add: key].
^ aSet!
----- Method: PluggableDictionary>>postCopyBlocks (in category 'copying') -----
postCopyBlocks
hashBlock _ hashBlock copy.
equalBlock _ equalBlock copy.
"Fix temps in case we're referring to outside stuff"
hashBlock ifNotNil: [hashBlock fixTemps].
equalBlock ifNotNil: [equalBlock fixTemps]!
----- Method: PluggableDictionary>>scanFor: (in category 'private') -----
scanFor: anObject
"Scan the key array for the first slot containing either a nil
(indicating
an empty slot) or an element that matches anObject. Answer the index
of that slot or zero if no slot is found. This method will be
overridden
in various subclasses that have different interpretations for matching
elements."
| element start finish |
finish _ array size.
start _ (hashBlock ifNil: [anObject hash]
ifNotNil: [hashBlock value: anObject])
\\ finish + 1.
"Search from (hash mod size) to the end."
start to: finish do: [:index | ((element _ array at: index) == nil or:
[equalBlock ifNil: [element key = anObject]
ifNotNil: [equalBlock value: element key value: anObject]])
ifTrue: [^ index]].
"Search from 1 to where we started."
1 to: start - 1 do: [:index | ((element _ array at: index) == nil or:
[equalBlock ifNil: [element key = anObject]
ifNotNil: [equalBlock value: element key value: anObject]])
ifTrue: [^ index]].
^ 0"No match AND no empty slot"!
Dictionary subclass: #WeakKeyDictionary
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Weak'!
!WeakKeyDictionary commentStamp: '<historical>' prior: 0!
I am a dictionary holding only weakly on my keys. This is a bit dangerous since at any time my keys can go away. Clients are responsible to register my instances by WeakArray such that the appropriate actions can be taken upon loss of any keys.
See WeakRegistry for an example of use.
!
WeakKeyDictionary subclass: #WeakIdentityKeyDictionary
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Weak'!
!WeakIdentityKeyDictionary commentStamp: '<historical>' prior: 0!
This class represents an identity dictionary with weak keys.!
----- Method: WeakIdentityKeyDictionary>>scanFor: (in category 'private') -----
scanFor: anObject
"ar 10/21/2000: The method has been copied to this location to indicate that whenever #scanFor: changes #scanForNil: must be changed in the receiver as well."
"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements."
| element start finish hash |
finish _ array size.
finish > 4096
ifTrue: [hash _ anObject identityHash * (finish // 4096)]
ifFalse: [hash _ anObject identityHash].
start _ (hash \\ finish) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == nil or: [element key == anObject])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == nil or: [element key == anObject])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
----- Method: WeakIdentityKeyDictionary>>scanForNil: (in category 'private') -----
scanForNil: anObject
"Private. Scan the key array for the first slot containing nil (indicating an empty slot). Answer the index of that slot."
| start finish hash |
finish _ array size.
finish > 4096
ifTrue: [hash _ anObject identityHash * (finish // 4096)]
ifFalse: [hash _ anObject identityHash].
start _ (hash \\ array size) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | (array at: index) == nil ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | (array at: index) == nil ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
WeakIdentityKeyDictionary subclass: #WeakKeyToCollectionDictionary
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Weak'!
!WeakKeyToCollectionDictionary commentStamp: '<historical>' prior: 0!
This class represents an identity dictionary with weak keys, whose values are collections.
Keys not in the dictionary are mapped to the empty collection. Conversely, if a collection becomes empty, the mapping can be removed to save time and space. However, because this requires re-hashing, it does not pay to do this to eagerly.!
----- Method: WeakKeyToCollectionDictionary>>finalizeValues (in category 'as yet unclassified') -----
finalizeValues
self rehash!
----- Method: WeakKeyToCollectionDictionary>>rehash (in category 'as yet unclassified') -----
rehash
"Rehash the receiver. Reimplemented to remove nils from the collections
that appear as values, and to entirely remove associations with empty collections
as values."
| oldArray assoc cleanedValue newIndex |
oldArray := array.
array := Array new: oldArray size.
tally := 0.
1 to: array size do: [:i |
assoc := oldArray at: i.
(assoc notNil
and: [(cleanedValue := assoc value copyWithout: nil) notEmpty])
ifTrue: [newIndex := self scanForNil: assoc key.
assoc value: cleanedValue.
self atNewIndex: newIndex put: assoc]]!
----- Method: WeakKeyDictionary>>add: (in category 'adding') -----
add: anAssociation
self at: anAssociation key put: anAssociation value.
^ anAssociation!
----- Method: WeakKeyDictionary>>at:put: (in category 'accessing') -----
at: key put: anObject
"Set the value at key to be anObject. If key is not found, create a new
entry for key and set is value to anObject. Answer anObject."
| index element |
key isNil ifTrue:[^anObject].
index _ self findElementOrNil: key.
element _ array at: index.
element == nil
ifTrue: [self atNewIndex: index put: (WeakKeyAssociation key: key value: anObject)]
ifFalse: [element value: anObject].
^ anObject!
----- Method: WeakKeyDictionary>>finalizeValues (in category 'finalization') -----
finalizeValues
"remove all nil keys and rehash the receiver afterwards"
| assoc |
1 to: array size do:[:i|
assoc _ array at: i.
(assoc notNil and:[assoc key == nil]) ifTrue:[array at: i put: nil].
].
self rehash.!
----- Method: WeakKeyDictionary>>finalizeValues: (in category 'finalization') -----
finalizeValues: finiObjects
"Remove all associations with key == nil and value is in finiObjects.
This method is folded with #rehash for efficiency."
| oldArray assoc newIndex |
oldArray _ array.
array _ Array new: oldArray size.
tally _ 0.
1 to: array size do:[:i|
assoc _ oldArray at: i.
assoc ifNotNil:[
(assoc key == nil and:[finiObjects includes: assoc value]) ifFalse:[
newIndex _ self scanForNil: assoc key.
self atNewIndex: newIndex put: assoc].
].
].!
----- Method: WeakKeyDictionary>>fixCollisionsFrom: (in category 'private') -----
fixCollisionsFrom: oldIndex
"The element at index has been removed and replaced by nil."
self rehash. "Do it the hard way - we may have any number of nil keys and #rehash deals with them"!
----- Method: WeakKeyDictionary>>keysDo: (in category 'enumerating') -----
keysDo: aBlock
"Evaluate aBlock for each of the receiver's keys."
self associationsDo: [:association |
association key ifNotNil:[aBlock value: association key]].!
----- Method: WeakKeyDictionary>>rehash (in category 'private') -----
rehash
"Rehash the receiver. Reimplemented to allow for multiple nil keys"
| oldArray assoc newIndex |
oldArray _ array.
array _ Array new: oldArray size.
tally _ 0.
1 to: array size do:[:i|
assoc _ oldArray at: i.
assoc ifNotNil:[
newIndex _ self scanForNil: assoc key.
self atNewIndex: newIndex put: assoc.
].
].!
----- Method: WeakKeyDictionary>>scanFor: (in category 'private') -----
scanFor: anObject
"ar 10/21/2000: The method has been copied to this location to indicate that whenever #scanFor: changes #scanForNil: must be changed in the receiver as well."
"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements."
| element start finish |
finish _ array size.
start _ (anObject hash \\ finish) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == nil or: [element key = anObject])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == nil or: [element key = anObject])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
----- Method: WeakKeyDictionary>>scanForNil: (in category 'private') -----
scanForNil: anObject
"Private. Scan the key array for the first slot containing nil (indicating an empty slot). Answer the index of that slot."
| start finish |
start _ (anObject hash \\ array size) + 1.
finish _ array size.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | (array at: index) == nil ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | (array at: index) == nil ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
Dictionary subclass: #WeakValueDictionary
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Weak'!
!WeakValueDictionary commentStamp: '<historical>' prior: 0!
I am a dictionary holding only weakly on my values. Clients may expect to get a nil value for any object they request.!
----- Method: WeakValueDictionary>>add: (in category 'adding') -----
add: anAssociation
self at: anAssociation key put: anAssociation value.
^ anAssociation!
----- Method: WeakValueDictionary>>at:put: (in category 'accessing') -----
at: key put: anObject
"Set the value at key to be anObject. If key is not found, create a new
entry for key and set is value to anObject. Answer anObject."
| index element |
index _ self findElementOrNil: key.
element _ array at: index.
element == nil
ifTrue: [self atNewIndex: index put: (WeakValueAssociation key: key value: anObject)]
ifFalse: [element value: anObject].
^ anObject!
Set subclass: #IdentitySet
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!IdentitySet commentStamp: 'sw 1/14/2003 22:35' prior: 0!
The same as a Set, except that items are compared using #== instead of #=.
Almost any class named IdentityFoo is the same as Foo except for the way items are compared. In Foo, #= is used, while in IdentityFoo, #== is used. That is, identity collections will treat items as the same only if they have the same identity.
For example, note that copies of a string are equal:
('abc' copy) = ('abc' copy)
but they are not identitcal:
('abc' copy) == ('abc' copy)
A regular Set will only include equal objects once:
| aSet |
aSet := Set new.
aSet add: 'abc' copy.
aSet add: 'abc' copy.
aSet
An IdentitySet will include multiple equal objects if they are not identical:
| aSet |
aSet := IdentitySet new.
aSet add: 'abc' copy.
aSet add: 'abc' copy.
aSet
!
----- Method: IdentitySet>>asIdentitySet (in category 'converting') -----
asIdentitySet
^self!
----- Method: IdentitySet>>scanFor: (in category 'private') -----
scanFor: anObject
"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements."
| finish hash start element |
finish _ array size.
finish > 4096
ifTrue: [hash _ anObject identityHash * (finish // 4096)]
ifFalse: [hash _ anObject identityHash].
start _ (hash \\ finish) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == nil or: [element == anObject])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == nil or: [element == anObject])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
Set subclass: #KeyedSet
instanceVariableNames: 'keyBlock'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!KeyedSet commentStamp: '<historical>' prior: 0!
Like Set except a key of every element is used for hashing and searching instead of the element itself. keyBlock gets the key of an element.!
KeyedSet subclass: #KeyedIdentitySet
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
----- Method: KeyedIdentitySet>>scanFor: (in category 'private') -----
scanFor: anObject
"Same as super except change = to ==, and hash to identityHash"
| element start finish |
finish _ array size.
start _ (anObject identityHash \\ finish) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == nil or: [(keyBlock value: element) == anObject])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == nil or: [(keyBlock value: element) == anObject])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
----- Method: KeyedSet class>>keyBlock: (in category 'instance creation') -----
keyBlock: oneArgBlock
"Create a new KeySet whose way to access an element's key is by executing oneArgBlock on the element"
^ self new keyBlock: oneArgBlock!
----- Method: KeyedSet>>add: (in category 'adding') -----
add: newObject
"Include newObject as one of the receiver's elements, but only if
not already present. Answer newObject."
| index |
newObject ifNil: [self error: 'Sets cannot meaningfully contain nil as an element'].
index _ self findElementOrNil: (keyBlock value: newObject).
(array at: index) ifNotNil: [^ self errorKeyAlreadyExists: (array at: index)].
self atNewIndex: index put: newObject.
^ newObject!
----- Method: KeyedSet>>addAll: (in category 'adding') -----
addAll: aCollection
"Include all the elements of aCollection as the receiver's elements"
(aCollection respondsTo: #associationsDo:)
ifTrue: [aCollection associationsDo: [:ass | self add: ass]]
ifFalse: [aCollection do: [:each | self add: each]].
^ aCollection!
----- Method: KeyedSet>>at: (in category 'accessing') -----
at: key
"Answer the value associated with the key."
^ self at: key ifAbsent: [self errorKeyNotFound]!
----- Method: KeyedSet>>at:ifAbsent: (in category 'accessing') -----
at: key ifAbsent: aBlock
"Answer the value associated with the key or, if key isn't found,
answer the result of evaluating aBlock."
| obj |
obj _ array at: (self findElementOrNil: key).
obj ifNil: [^ aBlock value].
^ obj!
----- Method: KeyedSet>>at:ifAbsentPut: (in category 'accessing') -----
at: key ifAbsentPut: aBlock
"Answer the value associated with the key or, if key isn't found,
add the result of evaluating aBlock to self"
^ self at: key ifAbsent: [self add: aBlock value]!
----- Method: KeyedSet>>at:ifPresent: (in category 'accessing') -----
at: key ifPresent: aBlock
"Lookup the given key in the receiver. If it is present, answer the value of evaluating the given block with the value associated with the key. Otherwise, answer nil."
| v |
v _ self at: key ifAbsent: [^ nil].
^ aBlock value: v
!
----- Method: KeyedSet>>copy (in category 'copying') -----
copy
^super copy postCopyBlocks!
----- Method: KeyedSet>>errorKeyNotFound (in category 'private') -----
errorKeyNotFound
self error: 'key not found'!
----- Method: KeyedSet>>fixCollisionsFrom: (in category 'private') -----
fixCollisionsFrom: index
"The element at index has been removed and replaced by nil.
This method moves forward from there, relocating any entries
that had been placed below due to collisions with this one"
| length oldIndex newIndex element |
oldIndex _ index.
length _ array size.
[oldIndex = length
ifTrue: [oldIndex _ 1]
ifFalse: [oldIndex _ oldIndex + 1].
(element _ self keyAt: oldIndex) == nil]
whileFalse:
[newIndex _ self findElementOrNil: (keyBlock value: element).
oldIndex = newIndex ifFalse: [self swap: oldIndex with: newIndex]]!
----- Method: KeyedSet>>includes: (in category 'testing') -----
includes: anObject
^ (array at: (self findElementOrNil: (keyBlock value: anObject))) ~~ nil!
----- Method: KeyedSet>>includesKey: (in category 'testing') -----
includesKey: key
^ (array at: (self findElementOrNil: key)) ~~ nil!
----- Method: KeyedSet>>init: (in category 'private') -----
init: n
super init: n.
keyBlock _ [:element | element key].
!
----- Method: KeyedSet>>keyBlock: (in category 'initialize') -----
keyBlock: oneArgBlock
"When evaluated return the key of the argument which will be an element of the set"
keyBlock _ oneArgBlock!
----- Method: KeyedSet>>keys (in category 'accessing') -----
keys
| keys |
keys _ Set new.
self keysDo: [:key | keys add: key].
^ keys!
----- Method: KeyedSet>>keysDo: (in category 'accessing') -----
keysDo: block
self do: [:item | block value: (keyBlock value: item)]!
----- Method: KeyedSet>>keysSorted (in category 'accessing') -----
keysSorted
| keys |
keys _ SortedCollection new.
self do: [:item | keys add: (keyBlock value: item)].
^ keys!
----- Method: KeyedSet>>like:ifAbsent: (in category 'accessing') -----
like: anObject ifAbsent: aBlock
"Answer an object in the receiver that is equal to anObject,
or evaluate the block if not found. Relies heavily on hash properties"
^(array at: (self scanFor: (keyBlock value: anObject)))
ifNil: [ aBlock value ]
ifNotNil: [ :element | element enclosedSetElement ]!
----- Method: KeyedSet>>member: (in category 'adding') -----
member: newObject
"Include newObject as one of the receiver's elements, if already exists just return it"
| index |
newObject ifNil: [self error: 'Sets cannot meaningfully contain nil as an element'].
index _ self findElementOrNil: (keyBlock value: newObject).
(array at: index) ifNotNil: [^ array at: index].
self atNewIndex: index put: newObject.
^ newObject!
----- Method: KeyedSet>>noCheckAdd: (in category 'private') -----
noCheckAdd: anObject
array at: (self findElementOrNil: (keyBlock value: anObject)) put: anObject.
tally _ tally + 1!
----- Method: KeyedSet>>postCopyBlocks (in category 'copying') -----
postCopyBlocks
keyBlock _ keyBlock copy.
"Fix temps in case we're referring to outside stuff"
keyBlock fixTemps.!
----- Method: KeyedSet>>rehash (in category 'private') -----
rehash
| newSelf |
newSelf _ self species new: self size.
newSelf keyBlock: keyBlock.
self do: [:each | newSelf noCheckAdd: each].
array _ newSelf array!
----- Method: KeyedSet>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: aBlock
| index |
index _ self findElementOrNil: (keyBlock value: oldObject).
(array at: index) == nil ifTrue: [ ^ aBlock value ].
array at: index put: nil.
tally _ tally - 1.
self fixCollisionsFrom: index.
^ oldObject!
----- Method: KeyedSet>>removeKey: (in category 'removing') -----
removeKey: key
^ self removeKey: key ifAbsent: [self errorKeyNotFound]!
----- Method: KeyedSet>>removeKey:ifAbsent: (in category 'removing') -----
removeKey: key ifAbsent: aBlock
| index obj |
index _ self findElementOrNil: key.
(obj _ array at: index) == nil ifTrue: [ ^ aBlock value ].
array at: index put: nil.
tally _ tally - 1.
self fixCollisionsFrom: index.
^ obj!
----- Method: KeyedSet>>scanFor: (in category 'private') -----
scanFor: anObject
"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements."
| element start finish |
finish _ array size.
start _ (anObject hash \\ finish) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == nil or: [(keyBlock value: element) = anObject])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == nil or: [(keyBlock value: element) = anObject])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
Set subclass: #PluggableSet
instanceVariableNames: 'hashBlock equalBlock'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Unordered'!
!PluggableSet commentStamp: '<historical>' prior: 0!
PluggableSets allow the redefinition of hashing and equality by clients. This is in particular useful if the clients know about specific properties of the objects stored in the set which in turn can heavily improve the performance of sets and dictionaries.
Instance variables:
hashBlock <BlockContext> A one argument block used for hashing the elements.
equalBlock <BlockContext> A two argument block used for comparing the elements.
Example: Adding 1000 integer points in the range (0 at 0) to: (100 at 100) to a set.
| rnd set max pt |
set _ Set new: 1000.
rnd _ Random new.
max _ 100.
Time millisecondsToRun:[
1 to: 1000 do:[:i|
pt _ (rnd next * max) truncated @ (rnd next * max) truncated.
set add: pt.
].
].
The above is way slow since the default hashing function of points leads to an awful lot of collisions in the set. And now the same, with a somewhat different hash function:
| rnd set max pt |
set _ PluggableSet new: 1000.
set hashBlock:[:item| (item x bitShift: 16) + item y].
rnd _ Random new.
max _ 100.
Time millisecondsToRun:[
1 to: 1000 do:[:i|
pt _ (rnd next * max) truncated @ (rnd next * max) truncated.
set add: pt.
].
].
!
----- Method: PluggableSet class>>integerSet (in category 'instance creation') -----
integerSet
^self new hashBlock: [:integer | integer hash \\ 1064164 * 1009]!
----- Method: PluggableSet>>copy (in category 'copying') -----
copy
^super copy postCopyBlocks!
----- Method: PluggableSet>>equalBlock (in category 'accessing') -----
equalBlock
"Return the block used for comparing the elements in the receiver."
^equalBlock!
----- Method: PluggableSet>>equalBlock: (in category 'accessing') -----
equalBlock: aBlock
"Set a new equality block. The block must accept two arguments and return true if the argumets are considered equal, false otherwise"
equalBlock _ aBlock.!
----- Method: PluggableSet>>hashBlock (in category 'accessing') -----
hashBlock
"Return the block used for hashing the elements in the receiver."
^hashBlock!
----- Method: PluggableSet>>hashBlock: (in category 'accessing') -----
hashBlock: aBlock
"Set a new hash block. The block must accept one argument and return the hash value of the given argument."
hashBlock _ aBlock.!
----- Method: PluggableSet>>postCopyBlocks (in category 'copying') -----
postCopyBlocks
hashBlock _ hashBlock copy.
equalBlock _ equalBlock copy.
"Fix temps in case we're referring to outside stuff"
hashBlock ifNotNil: [hashBlock fixTemps].
equalBlock ifNotNil: [equalBlock fixTemps]!
----- Method: PluggableSet>>scanFor: (in category 'private') -----
scanFor: anObject
"Scan the key array for the first slot containing either a nil
(indicating
an empty slot) or an element that matches anObject. Answer the index
of that slot or zero if no slot is found. This method will be
overridden
in various subclasses that have different interpretations for matching
elements."
| element start finish |
finish _ array size.
start _ (hashBlock ifNil: [anObject hash]
ifNotNil: [hashBlock value: anObject])
\\ finish + 1.
"Search from (hash mod size) to the end."
start to: finish do: [:index | ((element _ array at: index) == nil or:
[equalBlock ifNil: [element = anObject]
ifNotNil: [equalBlock value: element value: anObject]])
ifTrue: [^ index]].
"Search from 1 to where we started."
1 to: start - 1 do: [:index | ((element _ array at: index) == nil or:
[equalBlock ifNil: [element = anObject]
ifNotNil: [equalBlock value: element value: anObject]])
ifTrue: [^ index]].
^ 0"No match AND no empty slot"!
----- Method: Set class>>new (in category 'instance creation') -----
new
^ self new: 4!
----- Method: Set class>>new: (in category 'instance creation') -----
new: nElements
"Create a Set large enough to hold nElements without growing"
^ super basicNew init: (self sizeFor: nElements)!
----- Method: Set class>>newFrom: (in category 'instance creation') -----
newFrom: aCollection
"Answer an instance of me containing the same elements as aCollection."
| newCollection |
newCollection _ self new: aCollection size.
newCollection addAll: aCollection.
^ newCollection
"
Set newFrom: {1. 2. 3}
{1. 2. 3} as: Set
"!
----- Method: Set class>>quickRehashAllSets (in category 'initialization') -----
quickRehashAllSets "Set rehashAllSets"
| insts |
self withAllSubclassesDo:
[:c |
insts _ c allInstances.
(insts isEmpty or: [c = MethodDictionary]) ifFalse:
['Rehashing instances of ' , c name
displayProgressAt: Sensor cursorPoint
from: 1 to: insts size
during: [:bar | 1 to: insts size do: [:x | bar value: x. (insts at: x) rehash]]
]
]!
----- Method: Set class>>rehashAllSets (in category 'initialization') -----
rehashAllSets "Set rehashAllSets"
| insts |
self withAllSubclassesDo:
[:c |
insts _ c allInstances.
insts isEmpty ifFalse:
['Rehashing instances of ' , c name
displayProgressAt: Sensor cursorPoint
from: 1 to: insts size
during: [:bar | 1 to: insts size do: [:x | bar value: x. (insts at: x) rehash]]
]
]!
----- Method: Set class>>sizeFor: (in category 'instance creation') -----
sizeFor: nElements
"Large enough size to hold nElements with some slop (see fullCheck)"
nElements <= 0 ifTrue: [^ 1].
^ nElements+1*4//3!
----- Method: Set>>= (in category 'comparing') -----
= aSet
self == aSet ifTrue: [^ true]. "stop recursion"
(aSet isKindOf: Set) ifFalse: [^ false].
self size = aSet size ifFalse: [^ false].
self do: [:each | (aSet includes: each) ifFalse: [^ false]].
^ true!
----- Method: Set>>add: (in category 'adding') -----
add: newObject
"Include newObject as one of the receiver's elements, but only if
not already present. Answer newObject."
| index |
newObject ifNil: [self error: 'Sets cannot meaningfully contain nil as an element'].
index _ self findElementOrNil: newObject.
(array at: index) ifNil: [self atNewIndex: index put: newObject].
^ newObject!
----- Method: Set>>add:withOccurrences: (in category 'adding') -----
add: newObject withOccurrences: anInteger
^ self add: newObject!
----- Method: Set>>array (in category 'private') -----
array
^ array!
----- Method: Set>>asSet (in category 'converting') -----
asSet
^self!
----- Method: Set>>atNewIndex:put: (in category 'private') -----
atNewIndex: index put: anObject
array at: index put: anObject.
tally _ tally + 1.
self fullCheck!
----- Method: Set>>atRandom: (in category 'accessing') -----
atRandom: aGenerator
"Answer a random element of the receiver. Uses aGenerator which
should be kept by the user in a variable and used every time. Use
this instead of #atRandom for better uniformity of random numbers
because only you use the generator. Causes an error if self has no
elements."
| ind |
self emptyCheck.
ind _ aGenerator nextInt: array size.
ind to: array size do:[:i|
(array at: i) == nil ifFalse:[^array at: i]].
1 to: ind do:[:i|
(array at: i) == nil ifFalse:[^array at: i]].
self errorEmptyCollection.!
----- Method: Set>>capacity (in category 'accessing') -----
capacity
"Answer the current capacity of the receiver."
^ array size!
----- Method: Set>>collect: (in category 'enumerating') -----
collect: aBlock
"Evaluate aBlock with each of the receiver's elements as the argument.
Collect the resulting values into a collection like the receiver. Answer
the new collection."
| newSet |
newSet _ Set new: self size.
array do: [:each | each ifNotNil: [newSet add: (aBlock value: each)]].
^ newSet!
----- Method: Set>>comeFullyUpOnReload: (in category 'objects from disk') -----
comeFullyUpOnReload: smartRefStream
"Symbols have new hashes in this image."
self rehash.
"^ self"
!
----- Method: Set>>copy (in category 'copying') -----
copy
^ self shallowCopy withArray: array shallowCopy!
----- Method: Set>>copyWithout: (in category 'copying') -----
copyWithout: oldElement
"Answer a copy of the receiver that does not contain any
elements equal to oldElement."
^ self copy
remove: oldElement ifAbsent: [];
yourself!
----- Method: Set>>do: (in category 'enumerating') -----
do: aBlock
tally = 0 ifTrue: [^ self].
1 to: array size do:
[:index |
| each |
(each _ array at: index) ifNotNil: [aBlock value: each]]!
----- Method: Set>>doWithIndex: (in category 'enumerating') -----
doWithIndex: aBlock2
"Support Set enumeration with a counter, even though not ordered"
| index |
index _ 0.
self do: [:item | aBlock2 value: item value: (index _ index+1)]!
----- Method: Set>>findElementOrNil: (in category 'private') -----
findElementOrNil: anObject
"Answer the index of a first slot containing either a nil (indicating an empty slot) or an element that matches the given object. Answer the index of that slot or zero. Fail if neither a match nor an empty slot is found."
| index |
index _ self scanFor: anObject.
index > 0 ifTrue: [^index].
"Bad scene. Neither have we found a matching element
nor even an empty slot. No hashed set is ever supposed to get
completely full."
self error: 'There is no free space in this set!!'.!
----- Method: Set>>fixCollisionsFrom: (in category 'private') -----
fixCollisionsFrom: index
"The element at index has been removed and replaced by nil.
This method moves forward from there, relocating any entries
that had been placed below due to collisions with this one"
| length oldIndex newIndex element |
oldIndex _ index.
length _ array size.
[oldIndex = length
ifTrue: [oldIndex _ 1]
ifFalse: [oldIndex _ oldIndex + 1].
(element _ self keyAt: oldIndex) == nil]
whileFalse:
[newIndex _ self findElementOrNil: element.
oldIndex = newIndex ifFalse: [self swap: oldIndex with: newIndex]]!
----- Method: Set>>fullCheck (in category 'private') -----
fullCheck
"Keep array at least 1/4 free for decent hash behavior"
array size - tally < (array size // 4 max: 1)
ifTrue: [self grow]!
----- Method: Set>>grow (in category 'private') -----
grow
"Grow the elements array and reinsert the old elements"
| oldElements |
oldElements _ array.
array _ Array new: array size + self growSize.
tally _ 0.
oldElements do:
[:each | each == nil ifFalse: [self noCheckAdd: each]]!
----- Method: Set>>growSize (in category 'private') -----
growSize
^ array size max: 2!
----- Method: Set>>includes: (in category 'testing') -----
includes: anObject
^ (array at: (self findElementOrNil: anObject)) ~~ nil!
----- Method: Set>>init: (in category 'private') -----
init: n
"Initialize array to an array size of n"
array _ Array new: n.
tally _ 0!
----- Method: Set>>keyAt: (in category 'private') -----
keyAt: index
"May be overridden by subclasses so that fixCollisions will work"
^ array at: index!
----- Method: Set>>like: (in category 'accessing') -----
like: anObject
"Answer an object in the receiver that is equal to anObject,
nil if no such object is found. Relies heavily on hash properties"
| index |
^(index _ self scanFor: anObject) = 0
ifFalse: [array at: index]!
----- Method: Set>>like:ifAbsent: (in category 'accessing') -----
like: anObject ifAbsent: aBlock
"Answer an object in the receiver that is equal to anObject,
or evaluate the block if not found. Relies heavily on hash properties"
^(array at: (self scanFor: anObject))
ifNil: [ aBlock value ]
ifNotNil: [ :element | element enclosedSetElement ]!
----- Method: Set>>noCheckAdd: (in category 'private') -----
noCheckAdd: anObject
array at: (self findElementOrNil: anObject) put: anObject.
tally _ tally + 1!
----- Method: Set>>occurrencesOf: (in category 'testing') -----
occurrencesOf: anObject
^ (self includes: anObject) ifTrue: [1] ifFalse: [0]!
----- Method: Set>>rehash (in category 'private') -----
rehash
| newSelf |
newSelf _ self species new: self size.
self do: [:each | newSelf noCheckAdd: each].
array _ newSelf array!
----- Method: Set>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: aBlock
| index |
index _ self findElementOrNil: oldObject.
(array at: index) == nil ifTrue: [ ^ aBlock value ].
array at: index put: nil.
tally _ tally - 1.
self fixCollisionsFrom: index.
^ oldObject!
----- Method: Set>>scanFor: (in category 'private') -----
scanFor: anObject
"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements."
| element start finish |
finish _ array size.
start _ (anObject hash \\ finish) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == nil or: [element = anObject])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == nil or: [element = anObject])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
----- Method: Set>>size (in category 'accessing') -----
size
^ tally!
----- Method: Set>>someElement (in category 'accessing') -----
someElement
"Deprecated. Use anyOne."
^ self anyOne!
----- Method: Set>>swap:with: (in category 'private') -----
swap: oneIndex with: otherIndex
"May be overridden by subclasses so that fixCollisions will work"
array swap: oneIndex with: otherIndex
!
----- Method: Set>>union: (in category 'enumerating') -----
union: aCollection
"Answer the set theoretic union of the receiver and aCollection, using the receiver's notion of equality and not side effecting the receiver at all."
^ self copy addAll: aCollection; yourself
!
----- Method: Set>>withArray: (in category 'private') -----
withArray: anArray
"private -- for use only in copy"
array _ anArray!
Set subclass: #WeakSet
instanceVariableNames: 'flag'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Weak'!
----- Method: WeakSet>>add: (in category 'adding') -----
add: newObject
"Include newObject as one of the receiver's elements, but only if
not already present. Answer newObject"
| index |
newObject ifNil: [self error: 'Sets cannot meaningfully contain nil as an element'].
index _ self findElementOrNil: newObject.
((array at: index) == flag or: [(array at: index) isNil])
ifTrue: [self atNewIndex: index put: newObject].
^newObject!
----- Method: WeakSet>>collect: (in category 'enumerating') -----
collect: aBlock
| each newSet |
newSet _ self species new: self size.
tally = 0 ifTrue: [^newSet ].
1 to: array size do:
[:index |
((each _ array at: index) == nil or: [each == flag])
ifFalse: [newSet add: (aBlock value: each)]
].
^newSet!
----- Method: WeakSet>>do: (in category 'enumerating') -----
do: aBlock
| each |
tally = 0 ifTrue: [^self].
1 to: array size do:
[:index |
((each _ array at: index) == nil or: [each == flag])
ifFalse: [aBlock value: each]
]!
----- Method: WeakSet>>do:after: (in category 'public') -----
do: aBlock after: anElement
| each startIndex |
tally = 0 ifTrue: [^self].
startIndex _ anElement ifNil: [1] ifNotNil:
[self findElementOrNil: anElement].
startIndex + 1 to: array size do:
[:index |
((each _ array at: index) == nil or: [each == flag])
ifFalse: [aBlock value: each]
]!
----- Method: WeakSet>>fixCollisionsFrom: (in category 'private') -----
fixCollisionsFrom: index
"The element at index has been removed and replaced by nil.
This method moves forward from there, relocating any entries
that had been placed below due to collisions with this one"
| length oldIndex newIndex element |
oldIndex _ index.
length _ array size.
[oldIndex = length
ifTrue: [oldIndex _ 1]
ifFalse: [oldIndex _ oldIndex + 1].
(element _ self keyAt: oldIndex) == flag]
whileFalse:
[newIndex _ self findElementOrNil: element.
oldIndex = newIndex ifFalse: [self swap: oldIndex with: newIndex]]!
----- Method: WeakSet>>grow (in category 'private') -----
grow
"Grow the elements array and reinsert the old elements"
self growTo: array size + self growSize!
----- Method: WeakSet>>growTo: (in category 'private') -----
growTo: anInteger
"Grow the elements array and reinsert the old elements"
| oldElements |
oldElements _ array.
array _ WeakArray new: anInteger.
array atAllPut: flag.
tally _ 0.
oldElements do:
[:each | (each == flag or: [each == nil]) ifFalse: [self noCheckAdd: each]]!
----- Method: WeakSet>>includes: (in category 'testing') -----
includes: anObject
^(array at: (self findElementOrNil: anObject)) ~~ flag!
----- Method: WeakSet>>init: (in category 'private') -----
init: n
"Initialize array to an array size of n"
flag _ Object new.
array _ WeakArray new: n.
array atAllPut: flag.
tally _ 0!
----- Method: WeakSet>>like: (in category 'accessing') -----
like: anObject
"Answer an object in the receiver that is equal to anObject,
nil if no such object is found. Relies heavily on hash properties"
| index element |
^(index _ self scanFor: anObject) = 0
ifFalse: [(element _ array at: index) == flag ifFalse: [element]]!
----- Method: WeakSet>>like:ifAbsent: (in category 'accessing') -----
like: anObject ifAbsent: aBlock
"Answer an object in the receiver that is equal to anObject,
or evaluate the block if not found. Relies heavily on hash properties"
| element |
((element := array at: (self scanFor: anObject)) == flag or: [ element == nil ])
ifTrue: [ ^aBlock value ]
ifFalse: [ ^element enclosedSetElement ]!
----- Method: WeakSet>>printElementsOn: (in category 'public') -----
printElementsOn: aStream
| oldPos |
aStream nextPut: $(.
oldPos _ aStream position.
self do: [:element | aStream print: element; space].
aStream position > oldPos ifTrue: [aStream skip: -1 "remove the extra space"].
aStream nextPut: $)!
----- Method: WeakSet>>rehash (in category 'private') -----
rehash
self growTo: array size!
----- Method: WeakSet>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: aBlock
| index |
index _ self findElementOrNil: oldObject.
(array at: index) == flag ifTrue: [ ^ aBlock value ].
array at: index put: flag.
tally _ tally - 1.
self fixCollisionsFrom: index.
^oldObject!
----- Method: WeakSet>>scanFor: (in category 'private') -----
scanFor: anObject
"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements"
| element start finish |
finish _ array size.
start _ (anObject hash \\ finish) + 1.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == flag or: [element = anObject])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == flag or: [element = anObject])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
----- Method: WeakSet>>scanForLoadedSymbol: (in category 'private') -----
scanForLoadedSymbol: anObject
"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements"
| element start finish |
start _ (anObject hash \\ array size) + 1.
finish _ array size.
"Search from (hash mod size) to the end."
start to: finish do:
[:index | ((element _ array at: index) == flag or: [element asString = anObject asString])
ifTrue: [^ index ]].
"Search from 1 to where we started."
1 to: start-1 do:
[:index | ((element _ array at: index) == flag or: [element asString = anObject asString])
ifTrue: [^ index ]].
^ 0 "No match AND no empty slot"!
----- Method: WeakSet>>size (in category 'accessing') -----
size
"Careful!! Answer the maximum amount
of elements in the receiver, not the
exact amount"
^tally!
----- Method: WeakSet>>slowSize (in category 'public') -----
slowSize
"Careful!! Answer the maximum amount
of elements in the receiver, not the
exact amount"
tally _ array inject: 0 into:
[:total :each | (each == nil or: [each == flag])
ifTrue: [total] ifFalse: [total + 1]].
^tally!
Collection subclass: #SkipList
instanceVariableNames: 'sortBlock pointers numElements level splice'
classVariableNames: 'Rand'
poolDictionaries: ''
category: 'Collections-SkipLists'!
!SkipList commentStamp: 'KLC 2/26/2004 12:04' prior: 0!
>From "Skip Lists: A Probabilistic Alternative to Balanced Trees" by William Pugh ( http://epaperpress.com/sortsearch/download/skiplist.pdf ):
"Skip lists are a data structure that can be used in place of balanced trees. Skip lists use probabilistic balancing rather than strictly enforcing balancing and as a result the algorithms for insertion and deletion in skip lists are much simpler and significantly faster than equivalent algorithms for balanced trees."
Notes:
The elements of the skip list must implement #< or you must provide a sort block.
!
SkipList subclass: #IdentitySkipList
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-SkipLists'!
!IdentitySkipList commentStamp: '<historical>' prior: 0!
Like a SkipList, except that elements are compared with #== instead of #= .
See the comment of IdentitySet for more information.
!
----- Method: IdentitySkipList>>is:equalTo: (in category 'element comparison') -----
is: element1 equalTo: element2
^ element1 == element2!
----- Method: SkipList class>>maxLevel: (in category 'instance creation') -----
maxLevel: maxLevel
"
SkipList maxLevel: 5
"
^ super new initialize: maxLevel!
----- Method: SkipList class>>maxLevel:sortBlock: (in category 'instance creation') -----
maxLevel: anInteger sortBlock: aBlock
^ (self maxLevel: anInteger) sortBlock: aBlock!
----- Method: SkipList class>>new (in category 'instance creation') -----
new
"
SkipList new
"
^ super new initialize: 10!
----- Method: SkipList class>>new: (in category 'instance creation') -----
new: anInteger
^ self maxLevel: (anInteger log: 2) ceiling!
----- Method: SkipList class>>new:sortBlock: (in category 'instance creation') -----
new: anInteger sortBlock: aBlock
^ (self new: anInteger) sortBlock: aBlock!
----- Method: SkipList class>>newFrom: (in category 'instance creation') -----
newFrom: aCollection
| skipList |
skipList _ self new: aCollection size.
skipList addAll: aCollection.
^ skipList!
----- Method: SkipList class>>sortBlock: (in category 'instance creation') -----
sortBlock: aBlock
^ self new sortBlock: aBlock!
----- Method: SkipList>>add: (in category 'adding') -----
add: element
self add: element ifPresent: nil.
^ element!
----- Method: SkipList>>add:ifPresent: (in category 'adding') -----
add: element ifPresent: aBlock
| node lvl s |
node _ self search: element updating: splice.
node ifNotNil: [aBlock ifNotNil: [^ aBlock value: node]].
lvl _ self randomLevel.
node _ SkipListNode on: element level: lvl.
level + 1 to: lvl do: [:i | splice at: i put: self].
1 to: lvl do: [:i |
s _ splice at: i.
node atForward: i put: (s forward: i).
s atForward: i put: node].
numElements _ numElements + 1.
splice atAllPut: nil.
^ element
!
----- Method: SkipList>>atForward:put: (in category 'private') -----
atForward: i put: node
level _ node
ifNil: [pointers findLast: [:n | n notNil]]
ifNotNil: [level max: i].
^ pointers at: i put: node!
----- Method: SkipList>>do: (in category 'enumerating') -----
do: aBlock
self nodesDo: [:node | aBlock value: node object]!
----- Method: SkipList>>forward: (in category 'private') -----
forward: i
^ pointers at: i!
----- Method: SkipList>>includes: (in category 'testing') -----
includes: element
^ (self search: element updating: nil) notNil!
----- Method: SkipList>>initialize: (in category 'initialization') -----
initialize: maxLevel
pointers _ Array new: maxLevel.
splice _ Array new: maxLevel.
numElements _ 0.
level _ 0.
Rand ifNil: [Rand _ Random new]!
----- Method: SkipList>>is:before: (in category 'private') -----
is: node before: element
| object |
node ifNil: [^ false].
object _ node object.
^ sortBlock
ifNil: [object < element]
ifNotNil: [(self is: object equalTo: element) ifTrue: [^ false].
sortBlock value: object value: element]!
----- Method: SkipList>>is:equalTo: (in category 'element comparison') -----
is: element1 equalTo: element2
^ element1 = element2!
----- Method: SkipList>>is:theNodeFor: (in category 'private') -----
is: node theNodeFor: element
node ifNil: [^ false].
node == self ifTrue: [^ false].
^ self is: node object equalTo: element!
----- Method: SkipList>>isEmpty (in category 'testing') -----
isEmpty
^ numElements = 0!
----- Method: SkipList>>level (in category 'accessing') -----
level
^ level!
----- Method: SkipList>>maxLevel (in category 'accessing') -----
maxLevel
^ pointers size!
----- Method: SkipList>>maxLevel: (in category 'accessing') -----
maxLevel: n
| newLevel oldPointers |
newLevel _ n max: level.
oldPointers _ pointers.
pointers _ Array new: newLevel.
splice _ Array new: newLevel.
1 to: level do: [:i | pointers at: i put: (oldPointers at: i)]
!
----- Method: SkipList>>next (in category 'private') -----
next
^ pointers first!
----- Method: SkipList>>nodesDo: (in category 'node enumeration') -----
nodesDo: aBlock
| node |
node _ pointers first.
[node notNil]
whileTrue:
[aBlock value: node.
node _ node next]!
----- Method: SkipList>>randomLevel (in category 'private') -----
randomLevel
| p answer max |
p _ 0.5.
answer _ 1.
max _ self maxLevel.
[Rand next < p and: [answer < max]]
whileTrue: [answer _ answer + 1].
^ answer!
----- Method: SkipList>>remove: (in category 'removing') -----
remove: element
^ self remove: element ifAbsent: [self errorNotFound: element]!
----- Method: SkipList>>remove:ifAbsent: (in category 'removing') -----
remove: element ifAbsent: aBlock
| node i s |
node _ self search: element updating: splice.
node ifNil: [^ aBlock value].
i _ 1.
[s _ splice at: i.
i <= level and: [(s forward: i) == node]]
whileTrue:
[s atForward: i put: (node forward: i).
i _ i + 1].
numElements _ numElements - 1.
splice atAllPut: nil.
^ node object
!
----- Method: SkipList>>removeAll (in category 'removing') -----
removeAll
pointers atAllPut: nil.
splice atAllPut: nil.
numElements _ 0.
level _ 0.!
----- Method: SkipList>>search:updating: (in category 'private') -----
search: element updating: array
| node forward |
node _ self.
level to: 1 by: -1 do: [:i |
[forward _ node forward: i.
self is: forward before: element] whileTrue: [node _ forward].
"At this point: node < element <= forward"
array ifNotNil: [array at: i put: node]].
node _ node next.
^ (self is: node theNodeFor: element) ifTrue: [node]!
----- Method: SkipList>>size (in category 'accessing') -----
size
^ numElements!
----- Method: SkipList>>sortBlock (in category 'accessing') -----
sortBlock
^ sortBlock!
----- Method: SkipList>>sortBlock: (in category 'accessing') -----
sortBlock: aBlock
sortBlock _ aBlock!
Collection subclass: #WeakRegistry
instanceVariableNames: 'valueDictionary accessLock'
classVariableNames: 'Default'
poolDictionaries: ''
category: 'Collections-Weak'!
!WeakRegistry commentStamp: '<historical>' prior: 0!
I am a registry for objects needing finalization. When an object is added the object as well as its executor is stored. When the object is garbage collected, the executor can take the appropriate action for any resources associated with the object.
See also:
Object executor
Object actAsExecutor
Object finalize
!
----- Method: WeakRegistry class>>default (in category 'accessing') -----
default
^Default ifNil:[Default := self new]!
----- Method: WeakRegistry class>>new (in category 'instance creation') -----
new
^self new: 5!
----- Method: WeakRegistry class>>new: (in category 'instance creation') -----
new: n
| registry |
registry := super new initialize: n.
WeakArray addWeakDependent: registry.
^registry!
----- Method: WeakRegistry>>add: (in category 'adding') -----
add: anObject
"Add anObject to the receiver. Store the object as well as the associated executor."
| executor |
executor := anObject executor.
self protected:[
valueDictionary at: anObject put: executor.
].
^anObject!
----- Method: WeakRegistry>>add:executor: (in category 'adding') -----
add: anObject executor: anExecutor
"Add anObject to the receiver. Store the object as well as the associated executor."
self protected:[
valueDictionary at: anObject put: anExecutor.
].
^anObject!
----- Method: WeakRegistry>>do: (in category 'enumerating') -----
do: aBlock
^self protected:[
valueDictionary keysDo: aBlock.
].
!
----- Method: WeakRegistry>>finalizeValues (in category 'finalization') -----
finalizeValues
"Some of our elements may have gone away. Look for those and activate the associated executors."
| finiObjects |
finiObjects := nil.
"First collect the objects."
self protected:[
valueDictionary associationsDo:[:assoc|
assoc key isNil ifTrue:[
finiObjects isNil
ifTrue:[finiObjects := OrderedCollection with: assoc value]
ifFalse:[finiObjects add: assoc value]]
].
finiObjects isNil ifFalse:[valueDictionary finalizeValues: finiObjects asArray].
].
"Then do the finalization"
finiObjects isNil ifTrue:[^self].
finiObjects do:[:each| each finalize].
!
----- Method: WeakRegistry>>initialize: (in category 'initialize') -----
initialize: n
valueDictionary := WeakKeyDictionary new: n.
accessLock := Semaphore forMutualExclusion.!
----- Method: WeakRegistry>>keys (in category 'accessing') -----
keys
^self protected:[
Array streamContents:[:s| valueDictionary keysDo:[:key| s nextPut: key]]].!
----- Method: WeakRegistry>>printElementsOn: (in category 'printing') -----
printElementsOn: aStream
aStream nextPut: $(.
accessLock
ifNil: [self do: [:element | aStream print: element; space]]
ifNotNil: [aStream nextPutAll: '<this WeakRegistry is locked>; space'].
self isEmpty ifFalse: [aStream skip: -1].
aStream nextPut: $)!
----- Method: WeakRegistry>>protected: (in category 'private') -----
protected: aBlock
"Execute aBlock protected by the accessLock"
^accessLock isNil
ifTrue:[aBlock value]
ifFalse:[accessLock critical: aBlock ifError:[:msg :rcvr| rcvr error: msg]]!
----- Method: WeakRegistry>>remove:ifAbsent: (in category 'removing') -----
remove: oldObject ifAbsent: exceptionBlock
"Remove oldObject as one of the receiver's elements."
| removedObject |
oldObject isNil ifTrue:[^oldObject].
self protected:[
removedObject := valueDictionary removeKey: oldObject ifAbsent:[nil].
].
^removedObject isNil
ifTrue:[exceptionBlock value]
ifFalse:[removedObject].
!
----- Method: WeakRegistry>>size (in category 'accessing') -----
size
^ self protected: [valueDictionary size]!
----- Method: WeakRegistry>>species (in category 'private') -----
species
^Set!
Object subclass: #Link
instanceVariableNames: 'nextLink'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Support'!
!Link commentStamp: '<historical>' prior: 0!
An instance of me is a simple record of a pointer to another Link. I am an abstract class; my concrete subclasses, for example, Process, can be stored in a LinkedList structure.!
----- Method: Link class>>nextLink: (in category 'instance creation') -----
nextLink: aLink
"Answer an instance of me referring to the argument, aLink."
^self new nextLink: aLink; yourself!
----- Method: Link>>nextLink (in category 'accessing') -----
nextLink
"Answer the link to which the receiver points."
^nextLink!
----- Method: Link>>nextLink: (in category 'accessing') -----
nextLink: aLink
"Store the argument, aLink, as the link to which the receiver refers.
Answer aLink."
^nextLink _ aLink!
Object subclass: #MimeConverter
instanceVariableNames: 'dataStream mimeStream'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
MimeConverter subclass: #Base64MimeConverter
instanceVariableNames: 'data'
classVariableNames: 'FromCharTable ToCharTable'
poolDictionaries: ''
category: 'Collections-Streams'!
!Base64MimeConverter commentStamp: '<historical>' prior: 0!
This class encodes and decodes data in Base64 format. This is MIME encoding. We translate a whole stream at once, taking a Stream as input and giving one as output. Returns a whole stream for the caller to use.
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
Outbound: bytes are broken into 6 bit chunks, and the 0-63 value is converted to a character. 3 data bytes go into 4 characters.
Inbound: Characters are translated in to 0-63 values and shifted into 8 bit bytes.
(See: N. Borenstein, Bellcore, N. Freed, Innosoft, Network Working Group, Request for Comments: RFC 1521, September 1993, MIME (Multipurpose Internet Mail Extensions) Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies. Sec 6.2)
By Ted Kaehler, based on Tim Olson's Base64Filter.!
----- Method: Base64MimeConverter class>>decodeInteger: (in category 'as yet unclassified') -----
decodeInteger: mimeString
| bytes sum |
"Decode the MIME string into an integer of any length"
bytes _ (Base64MimeConverter mimeDecodeToBytes:
(ReadStream on: mimeString)) contents.
sum _ 0.
bytes reverseDo: [:by | sum _ sum * 256 + by].
^ sum!
----- Method: Base64MimeConverter class>>encodeInteger: (in category 'as yet unclassified') -----
encodeInteger: int
| strm |
"Encode an integer of any length and return the MIME string"
strm _ ReadWriteStream on: (ByteArray new: int digitLength).
1 to: int digitLength do: [:ii | strm nextPut: (int digitAt: ii)].
strm reset.
^ ((self mimeEncode: strm) contents) copyUpTo: $= "remove padding"!
----- Method: Base64MimeConverter class>>initialize (in category 'as yet unclassified') -----
initialize
FromCharTable _ Array new: 256. "nils"
ToCharTable _ Array new: 64.
($A asciiValue to: $Z asciiValue) doWithIndex: [:val :ind |
FromCharTable at: val+1 put: ind-1.
ToCharTable at: ind put: val asCharacter].
($a asciiValue to: $z asciiValue) doWithIndex: [:val :ind |
FromCharTable at: val+1 put: ind+25.
ToCharTable at: ind+26 put: val asCharacter].
($0 asciiValue to: $9 asciiValue) doWithIndex: [:val :ind |
FromCharTable at: val+1 put: ind+25+26.
ToCharTable at: ind+26+26 put: val asCharacter].
FromCharTable at: $+ asciiValue + 1 put: 62.
ToCharTable at: 63 put: $+.
FromCharTable at: $/ asciiValue + 1 put: 63.
ToCharTable at: 64 put: $/.
!
----- Method: Base64MimeConverter class>>mimeDecodeToBytes: (in category 'as yet unclassified') -----
mimeDecodeToBytes: aStream
"Return a RWBinaryOrTextStream of the original ByteArray. aStream has only 65 innocuous character values. aStream is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output."
| me |
aStream position: 0.
me _ self new mimeStream: aStream.
me dataStream: (RWBinaryOrTextStream on: (ByteArray new: aStream size * 3 // 4)).
me mimeDecodeToByteArray.
me dataStream position: 0.
^ me dataStream!
----- Method: Base64MimeConverter class>>mimeDecodeToChars: (in category 'as yet unclassified') -----
mimeDecodeToChars: aStream
"Return a ReadWriteStream of the original String. aStream has only 65 innocuous character values. It is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output."
| me |
aStream position: 0.
me _ self new mimeStream: aStream.
me dataStream: (ReadWriteStream on: (String new: aStream size * 3 // 4)).
me mimeDecode.
me dataStream position: 0.
^ me dataStream!
----- Method: Base64MimeConverter class>>mimeEncode: (in category 'as yet unclassified') -----
mimeEncode: aStream
"Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output."
| me |
aStream position: 0.
me _ self new dataStream: aStream.
me mimeStream: (ReadWriteStream on: (String new: aStream size + 20 * 4 // 3)).
me mimeEncode.
me mimeStream position: 0.
^ me mimeStream!
----- Method: Base64MimeConverter>>mimeDecode (in category 'conversion') -----
mimeDecode
"Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Reutrn a whole stream for the user to read."
| nibA nibB nibC nibD |
[mimeStream atEnd] whileFalse: [
(nibA _ self nextValue) ifNil: [^ dataStream].
(nibB _ self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)) asCharacter.
nibB _ nibB bitAnd: 16rF.
(nibC _ self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)) asCharacter.
nibC _ nibC bitAnd: 16r3.
(nibD _ self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibC bitShift: 6) + nibD) asCharacter.
].
^ dataStream!
----- Method: Base64MimeConverter>>mimeDecodeToByteArray (in category 'conversion') -----
mimeDecodeToByteArray
"Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Reutrn a whole stream for the user to read."
| nibA nibB nibC nibD |
[mimeStream atEnd] whileFalse: [
(nibA _ self nextValue) ifNil: [^ dataStream].
(nibB _ self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)).
nibB _ nibB bitAnd: 16rF.
(nibC _ self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)).
nibC _ nibC bitAnd: 16r3.
(nibD _ self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibC bitShift: 6) + nibD).
].
^ dataStream!
----- Method: Base64MimeConverter>>mimeEncode (in category 'conversion') -----
mimeEncode
"Convert from data to 6 bit characters."
| phase1 phase2 raw nib lineLength |
phase1 _ phase2 _ false.
lineLength := 0.
[dataStream atEnd] whileFalse: [
lineLength >= 70 ifTrue: [ mimeStream cr. lineLength := 0. ].
data _ raw _ dataStream next asInteger.
nib _ (data bitAnd: 16rFC) bitShift: -2.
mimeStream nextPut: (ToCharTable at: nib+1).
(raw _ dataStream next) ifNil: [raw _ 0. phase1 _ true].
data _ ((data bitAnd: 3) bitShift: 8) + raw asInteger.
nib _ (data bitAnd: 16r3F0) bitShift: -4.
mimeStream nextPut: (ToCharTable at: nib+1).
(raw _ dataStream next) ifNil: [raw _ 0. phase2 _ true].
data _ ((data bitAnd: 16rF) bitShift: 8) + (raw asInteger).
nib _ (data bitAnd: 16rFC0) bitShift: -6.
mimeStream nextPut: (ToCharTable at: nib+1).
nib _ (data bitAnd: 16r3F).
mimeStream nextPut: (ToCharTable at: nib+1).
lineLength := lineLength + 4.].
phase1 ifTrue: [mimeStream skip: -2; nextPut: $=; nextPut: $=.
^ mimeStream].
phase2 ifTrue: [mimeStream skip: -1; nextPut: $=.
^ mimeStream].
!
----- Method: Base64MimeConverter>>nextValue (in category 'conversion') -----
nextValue
"The next six bits of data char from the mimeStream, or nil. Skip all other chars"
| raw num |
[raw _ mimeStream next.
raw ifNil: [^ nil]. "end of stream"
raw == $= ifTrue: [^ nil].
num _ FromCharTable at: raw asciiValue + 1.
num ifNotNil: [^ num].
"else ignore space, return, tab, ..."
true] whileTrue.!
----- Method: MimeConverter class>>forEncoding: (in category 'convenience') -----
forEncoding: encodingString
"Answer a converter class for the given encoding or nil if unknown"
encodingString ifNil: [^nil].
^ encodingString asLowercase caseOf:
{ ['base64'] -> [Base64MimeConverter].
['quoted-printable'] -> [QuotedPrintableMimeConverter]}
otherwise: [].
!
----- Method: MimeConverter class>>mimeDecode:as: (in category 'convenience') -----
mimeDecode: aStringOrStream as: contentsClass
^ contentsClass streamContents: [:out |
self mimeDecode: aStringOrStream to: out]!
----- Method: MimeConverter class>>mimeDecode:to: (in category 'convenience') -----
mimeDecode: aStringOrStream to: outStream
self new
mimeStream: (aStringOrStream isStream
ifTrue: [aStringOrStream]
ifFalse: [ReadStream on: aStringOrStream]);
dataStream: outStream;
mimeDecode!
----- Method: MimeConverter class>>mimeEncode: (in category 'convenience') -----
mimeEncode: aCollectionOrStream
^ String streamContents: [:out |
self mimeEncode: aCollectionOrStream to: out]!
----- Method: MimeConverter class>>mimeEncode:to: (in category 'convenience') -----
mimeEncode: aCollectionOrStream to: outStream
self new
dataStream: (aCollectionOrStream isStream
ifTrue: [aCollectionOrStream]
ifFalse: [ReadStream on: aCollectionOrStream]);
mimeStream: outStream;
mimeEncode!
----- Method: MimeConverter>>dataStream (in category 'accessing') -----
dataStream
^dataStream!
----- Method: MimeConverter>>dataStream: (in category 'accessing') -----
dataStream: anObject
dataStream _ anObject!
----- Method: MimeConverter>>mimeDecode (in category 'conversion') -----
mimeDecode
"Do conversion reading from mimeStream writing to dataStream"
self subclassResponsibility!
----- Method: MimeConverter>>mimeEncode (in category 'conversion') -----
mimeEncode
"Do conversion reading from dataStream writing to mimeStream"
self subclassResponsibility!
----- Method: MimeConverter>>mimeStream (in category 'accessing') -----
mimeStream
^mimeStream!
----- Method: MimeConverter>>mimeStream: (in category 'accessing') -----
mimeStream: anObject
mimeStream _ anObject!
MimeConverter subclass: #QuotedPrintableMimeConverter
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!QuotedPrintableMimeConverter commentStamp: '<historical>' prior: 0!
I do quoted printable MIME decoding as specified in RFC 2045 "MIME Part One: Format of Internet Message Bodies".
Short version of RFC2045, Sect. 6.7:
(1) Any octet, except a CR or LF that is part of a CRLF line break of the canonical (standard) form of the data being encoded, may be represented by an "=" followed by a two digit hexadecimal representation of the octet's value. [...]
(2) Octets with decimal values of 33 through 60 inclusive, and 62 through 126, inclusive, MAY be represented as the US-ASCII characters which correspond to those octets [...].
(3) Octets with values of 9 and 32 MAY be represented as US-ASCII TAB (HT) and SPACE characters,
respectively, but MUST NOT be so represented at the end of an encoded line. [...]
(4) A line break in a text body, represented as a CRLF sequence in the text canonical form, must be represented by a (RFC 822) line break, which is also a CRLF sequence, in the Quoted-Printable encoding. [...]
(5) The Quoted-Printable encoding REQUIRES that encoded lines be no more than 76 characters long. If longer lines are to be encoded with the Quoted-Printable encoding, "soft" line breaks
must be used. An equal sign as the last character on a encoded line indicates such a non-significant ("soft") line break in the encoded text.
--bf 11/27/1998 16:50!
----- Method: QuotedPrintableMimeConverter>>mimeDecode (in category 'conversion') -----
mimeDecode
"Do conversion reading from mimeStream writing to dataStream"
| line s c1 v1 c2 v2 |
[(line _ mimeStream nextLine) isNil] whileFalse: [
line _ line withoutTrailingBlanks.
line size = 0
ifTrue: [dataStream cr]
ifFalse: [
s _ ReadStream on: line.
[dataStream nextPutAll: (s upTo: $=).
s atEnd] whileFalse: [
c1 _ s next. v1 _ c1 digitValue.
((v1 between: 0 and: 15) and: [s atEnd not])
ifFalse: [dataStream nextPut: $=; nextPut: c1]
ifTrue: [c2 _ s next. v2 _ c2 digitValue.
(v2 between: 0 and: 15)
ifFalse: [dataStream nextPut: $=; nextPut: c1; nextPut: c2]
ifTrue: [dataStream nextPut: (Character value: v1 * 16 + v2)]]].
line last = $= ifFalse: [dataStream cr]]].
^ dataStream!
QuotedPrintableMimeConverter subclass: #RFC2047MimeConverter
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!RFC2047MimeConverter commentStamp: '<historical>' prior: 0!
I do quoted printable MIME decoding as specified in RFC 2047 ""MIME Part Three: Message Header Extensions for Non-ASCII Text". See String>>decodeMimeHeader!
----- Method: RFC2047MimeConverter>>encodeChar:to: (in category 'private-encoding') -----
encodeChar: aChar to: aStream
aChar = Character space
ifTrue: [^ aStream nextPut: $_].
((aChar asciiValue between: 32 and: 127) and: [('?=_' includes: aChar) not])
ifTrue: [^ aStream nextPut: aChar].
aStream nextPut: $=;
nextPut: (Character digitValue: aChar asciiValue // 16);
nextPut: (Character digitValue: aChar asciiValue \\ 16)
!
----- Method: RFC2047MimeConverter>>encodeWord: (in category 'private-encoding') -----
encodeWord: aString
(aString allSatisfy: [:c | c asciiValue < 128])
ifTrue: [^ aString].
^ String streamContents: [:stream |
stream nextPutAll: '=?iso-8859-1?Q?'.
aString do: [:c | self encodeChar: c to: stream].
stream nextPutAll: '?=']!
----- Method: RFC2047MimeConverter>>isStructuredField: (in category 'private-encoding') -----
isStructuredField: aString
| fName |
fName _ aString copyUpTo: $:.
('Resent' sameAs: (fName copyUpTo: $-))
ifTrue: [fName _ fName copyFrom: 8 to: fName size].
^#('Sender' 'From' 'Reply-To' 'To' 'cc' 'bcc') anySatisfy: [:each | fName sameAs: each]!
----- Method: RFC2047MimeConverter>>mimeDecode (in category 'conversion') -----
mimeDecode
"Do conversion reading from mimeStream writing to dataStream. See String>>decodeMimeHeader"
| c |
[mimeStream atEnd] whileFalse: [
c _ mimeStream next.
c = $=
ifTrue: [c _ Character value: mimeStream next digitValue * 16
+ mimeStream next digitValue]
ifFalse: [c = $_ ifTrue: [c _ $ ]].
dataStream nextPut: c].
^ dataStream!
----- Method: RFC2047MimeConverter>>mimeEncode (in category 'conversion') -----
mimeEncode
"Do conversion reading from dataStream writing to mimeStream. Break long lines and escape non-7bit chars."
| word pos wasGood isGood max |
true ifTrue: [mimeStream nextPutAll: dataStream upToEnd].
pos _ 0.
max _ 72.
wasGood _ true.
[dataStream atEnd] whileFalse: [
word _ self readWord.
isGood _ word allSatisfy: [:c | c asciiValue < 128].
wasGood & isGood ifTrue: [
pos + word size < max
ifTrue: [dataStream nextPutAll: word.
pos _ pos + word size]
ifFalse: []
]
].
^ mimeStream!
----- Method: RFC2047MimeConverter>>readWord (in category 'private-encoding') -----
readWord
| strm |
strm _ WriteStream on: (String new: 20)
dataStream skipSeparators.
[dataStream atEnd] whileFalse:
[ | c |
c _ dataStream next.
strm nextPut: c.
c isSeparator ifTrue: [^ strm contents]].
^ strm contents!
Object subclass: #SharedQueue
instanceVariableNames: 'contentsArray readPosition writePosition accessProtect readSynch'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Sequenceable'!
!SharedQueue commentStamp: '<historical>' prior: 0!
I provide synchronized communication of arbitrary objects between Processes. An object is sent by sending the message nextPut: and received by sending the message next. If no object has been sent when a next message is sent, the Process requesting the object will be suspended until one is sent.!
----- Method: SharedQueue class>>new (in category 'instance creation') -----
new
"Answer a new instance of SharedQueue that has 10 elements."
^self new: 10!
----- Method: SharedQueue class>>new: (in category 'instance creation') -----
new: anInteger
^super new init: anInteger!
----- Method: SharedQueue>>flush (in category 'accessing') -----
flush
"Throw out all pending contents"
accessProtect critical: [
"nil out flushed slots --bf 02/11/2006"
contentsArray from: readPosition to: writePosition-1 put: nil.
readPosition _ 1.
writePosition _ 1.
"Reset the read synchronization semaphore"
readSynch initSignals].!
----- Method: SharedQueue>>flushAllSuchThat: (in category 'accessing') -----
flushAllSuchThat: aBlock
"Remove from the queue all objects that satisfy aBlock, and answer them"
| removed value newReadPos |
removed := OrderedCollection new: self size.
accessProtect critical: [
newReadPos _ writePosition.
writePosition-1 to: readPosition by: -1 do:
[:i | value _ contentsArray at: i.
contentsArray at: i put: nil.
(aBlock value: value) ifTrue: [
removed addFirst: value.
"We take an element out of the queue, and therefore, we need to decrement
the readSynch signals"
readSynch wait.
] ifFalse: [
newReadPos _ newReadPos - 1.
contentsArray at: newReadPos put: value]].
readPosition _ newReadPos].
^removed
!
----- Method: SharedQueue>>init: (in category 'private') -----
init: size
contentsArray _ Array new: size.
readPosition _ 1.
writePosition _ 1.
accessProtect _ Semaphore forMutualExclusion.
readSynch _ Semaphore new!
----- Method: SharedQueue>>isEmpty (in category 'testing') -----
isEmpty
"Answer whether any objects have been sent through the receiver and
not yet received by anyone."
^readPosition = writePosition!
----- Method: SharedQueue>>makeRoomAtEnd (in category 'private') -----
makeRoomAtEnd
| contentsSize |
readPosition = 1
ifTrue: [contentsArray _ contentsArray , (Array new: 10)]
ifFalse:
[contentsSize _ writePosition - readPosition.
"BLT direction ok for this. Lots faster!!!!!!!!!!!! SqR!!!! 4/10/2000 10:47"
contentsArray
replaceFrom: 1
to: contentsSize
with: contentsArray
startingAt: readPosition.
"nil out remainder --bf 10/25/2005"
contentsArray
from: contentsSize+1
to: contentsArray size
put: nil.
readPosition _ 1.
writePosition _ contentsSize + 1]!
----- Method: SharedQueue>>next (in category 'accessing') -----
next
"Answer the object that was sent through the receiver first and has not
yet been received by anyone. If no object has been sent, suspend the
requesting process until one is."
| value |
readSynch wait.
accessProtect
critical: [readPosition = writePosition
ifTrue:
[self error: 'Error in SharedQueue synchronization'.
value _ nil]
ifFalse:
[value _ contentsArray at: readPosition.
contentsArray at: readPosition put: nil.
readPosition _ readPosition + 1]].
^value!
----- Method: SharedQueue>>nextOrNil (in category 'accessing') -----
nextOrNil
"Answer the object that was sent through the receiver first and has not
yet been received by anyone. If no object has been sent, answer <nil>."
| value |
accessProtect critical: [
readPosition >= writePosition ifTrue: [
value := nil
] ifFalse: [
value := contentsArray at: readPosition.
contentsArray at: readPosition put: nil.
readPosition := readPosition + 1.
readSynch wait.
].
].
^value!
----- Method: SharedQueue>>nextOrNilSuchThat: (in category 'accessing') -----
nextOrNilSuchThat: aBlock
"Answer the next object that satisfies aBlock, or nil otherwise.
Leave other enqueued objects in place."
| i each |
accessProtect critical: [
i := readPosition.
[i < writePosition] whileTrue: [
each := contentsArray at: i.
(aBlock value: each) ifTrue: [
[i > readPosition] whileTrue: [
contentsArray at: i put: (contentsArray at: i-1).
i := i - 1].
contentsArray at: readPosition put: nil.
readPosition := readPosition + 1.
readSynch wait.
^each].
i := i + 1.
].
].
^nil
"===
| q c v |
q := SharedQueue new.
1 to: 10 do: [ :i | q nextPut: i].
c := OrderedCollection new.
[
v := q nextOrNilSuchThat: [ :e | e odd].
v notNil
] whileTrue: [
c add: {v. q size}
].
{c. q}
==="!
----- Method: SharedQueue>>nextPut: (in category 'accessing') -----
nextPut: value
"Send value through the receiver. If a Process has been suspended
waiting to receive a value through the receiver, allow it to proceed."
accessProtect
critical: [writePosition > contentsArray size
ifTrue: [self makeRoomAtEnd].
contentsArray at: writePosition put: value.
writePosition _ writePosition + 1].
readSynch signal.
^value!
----- Method: SharedQueue>>peek (in category 'accessing') -----
peek
"Answer the object that was sent through the receiver first and has not
yet been received by anyone but do not remove it from the receiver. If
no object has been sent, return nil"
| value |
accessProtect
critical: [readPosition >= writePosition
ifTrue: [readPosition _ 1.
writePosition _ 1.
value _ nil]
ifFalse: [value _ contentsArray at: readPosition]].
^value!
----- Method: SharedQueue>>size (in category 'accessing') -----
size
"Answer the number of objects that have been sent through the
receiver and not yet received by anyone."
^writePosition - readPosition!
Object subclass: #SkipListNode
instanceVariableNames: 'pointers object'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-SkipLists'!
----- Method: SkipListNode class>>new: (in category 'instance creation') -----
new: maxLevel
^ super new initialize: maxLevel!
----- Method: SkipListNode class>>on:level: (in category 'instance creation') -----
on: element level: maxLevel
^ (self new: maxLevel)
object: element!
----- Method: SkipListNode class>>tailOfLevel: (in category 'instance creation') -----
tailOfLevel: n
^ self on: nil level: n!
----- Method: SkipListNode>>atForward:put: (in category 'accessing') -----
atForward: i put: node
^ pointers at: i put: node!
----- Method: SkipListNode>>forward: (in category 'accessing') -----
forward: i
^ pointers at: i!
----- Method: SkipListNode>>initialize: (in category 'initialization') -----
initialize: maxLevel
pointers _ Array new: maxLevel!
----- Method: SkipListNode>>level (in category 'accessing') -----
level
^ pointers size!
----- Method: SkipListNode>>next (in category 'accessing') -----
next
^ pointers first!
----- Method: SkipListNode>>object (in category 'accessing') -----
object
^ object!
----- Method: SkipListNode>>object: (in category 'private') -----
object: anObject
object _ anObject!
----- Method: SkipListNode>>printOn: (in category 'printing') -----
printOn: aStream
| first |
aStream
nextPut: $[;
nextPutAll: object printString;
nextPutAll: ']-->('.
first _ true.
pointers do: [:node |
first ifTrue: [first _ false] ifFalse: [aStream space].
aStream nextPutAll: (node ifNil: ['*'] ifNotNil: [node object printString])].
aStream nextPut: $)
!
Object subclass: #Stream
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!Stream commentStamp: '<historical>' prior: 0!
I am an abstract class that represents an accessor for a sequence of objects. This sequence is referred to as my "contents".!
Stream subclass: #AttributedTextStream
instanceVariableNames: 'characters attributeRuns attributeValues currentAttributes currentRun'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!AttributedTextStream commentStamp: '<historical>' prior: 0!
a stream on Text's which keeps track of the last attribute put; new characters are added with those attributes.
instance vars:
characters - a WriteStream of the characters in the stream
attributeRuns - a RunArray with the attributes for the stream
currentAttributes - the attributes to be used for new text
attributesChanged - whether the attributes have changed since the last addition!
----- Method: AttributedTextStream class>>new (in category 'instance creation') -----
new
"For this class we override Stream class>>new since this
class actually is created using #new, even though it is a Stream."
^self basicNew initialize!
----- Method: AttributedTextStream>>contents (in category 'retrieving the text') -----
contents
| ans |
currentRun > 0 ifTrue:[
attributeValues nextPut: currentAttributes.
attributeRuns nextPut: currentRun.
currentRun _ 0].
ans _ Text new: characters size.
"this is declared private, but it's exactly what I need, and it's declared as exactly what I want it to do...."
ans setString: characters contents setRuns:
(RunArray runs: attributeRuns contents values: attributeValues contents).
^ans!
----- Method: AttributedTextStream>>currentAttributes (in category 'access') -----
currentAttributes
"return the current attributes"
^currentAttributes!
----- Method: AttributedTextStream>>currentAttributes: (in category 'access') -----
currentAttributes: newAttributes
"set the current attributes"
(currentRun > 0 and:[currentAttributes ~= newAttributes]) ifTrue:[
attributeRuns nextPut: currentRun.
attributeValues nextPut: currentAttributes.
currentRun _ 0.
].
currentAttributes _ newAttributes.
!
----- Method: AttributedTextStream>>initialize (in category 'private-initialization') -----
initialize
characters _ WriteStream on: String new.
currentAttributes _ OrderedCollection new.
currentRun _ 0.
attributeValues _ WriteStream on: (Array new: 50).
attributeRuns _ WriteStream on: (Array new: 50). !
----- Method: AttributedTextStream>>nextPut: (in category 'stream protocol') -----
nextPut: aChar
currentRun _ currentRun + 1.
characters nextPut: aChar!
----- Method: AttributedTextStream>>nextPutAll: (in category 'stream protocol') -----
nextPutAll: aString
"add an entire string with the same attributes"
currentRun _ currentRun + aString size.
characters nextPutAll: aString.!
----- Method: AttributedTextStream>>size (in category 'access') -----
size
"number of characters in the stream so far"
^characters size!
Stream subclass: #PositionableStream
instanceVariableNames: 'collection position readLimit'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!PositionableStream commentStamp: '<historical>' prior: 0!
I represent an accessor for a sequence of objects (a collection) that are externally named by indices so that the point of access can be repositioned. I am abstract in that I do not implement the messages next and nextPut: which are inherited from my superclass Stream.!
----- Method: PositionableStream class>>on: (in category 'instance creation') -----
on: aCollection
"Answer an instance of me, streaming over the elements of aCollection."
^self basicNew on: aCollection!
----- Method: PositionableStream class>>on:from:to: (in category 'instance creation') -----
on: aCollection from: firstIndex to: lastIndex
"Answer an instance of me, streaming over the elements of aCollection
starting with the element at firstIndex and ending with the one at
lastIndex."
^self basicNew on: (aCollection copyFrom: firstIndex to: lastIndex)!
----- Method: PositionableStream>>asBinaryOrTextStream (in category 'converting') -----
asBinaryOrTextStream
"Convert to a stream that can switch between bytes and characters"
^ (RWBinaryOrTextStream with: self contentsOfEntireFile) reset!
----- Method: PositionableStream>>asZLibReadStream (in category 'converting') -----
asZLibReadStream
^ZLibReadStream on: collection from: position+1 to: readLimit!
----- Method: PositionableStream>>atEnd (in category 'testing') -----
atEnd
"Primitive. Answer whether the receiver can access any more objects.
Optional. See Object documentation whatIsAPrimitive."
<primitive: 67>
^position >= readLimit!
----- Method: PositionableStream>>back (in category 'accessing') -----
back
"Go back one element and return it. Use indirect messages in case I am a StandardFileStream"
self position = 0 ifTrue: [self errorCantGoBack].
self position = 1 ifTrue: [self position: 0. ^ nil].
self skip: -2.
^ self next
!
----- Method: PositionableStream>>backChunk (in category 'fileIn/Out') -----
backChunk
"Answer the contents of the receiver back to the previous terminator character. Doubled terminators indicate an embedded terminator character."
| terminator out ch |
terminator _ $!!.
out _ WriteStream on: (String new: 1000).
[(ch _ self back) == nil] whileFalse: [
(ch == terminator) ifTrue: [
self peekBack == terminator ifTrue: [
self back. "skip doubled terminator"
] ifFalse: [
^ out contents reversed "we're done!!"
].
].
out nextPut: ch.
].
^ out contents reversed!
----- Method: PositionableStream>>backUpTo: (in category 'positioning') -----
backUpTo: subCollection
"Back up the position to he subCollection. Position must be somewhere within the stream initially. Leave it just after it. Return true if succeeded. No wildcards, and case does matter."
"Example:
| strm | strm _ ReadStream on: 'zabc abdc'.
strm setToEnd; backUpTo: 'abc'; position
"
| pattern startMatch |
pattern _ ReadStream on: subCollection reversed.
startMatch _ nil.
[pattern atEnd] whileFalse:
[self position = 0 ifTrue: [^ false].
self skip: -1.
(self next) = (pattern next)
ifTrue: [pattern position = 1 ifTrue: [startMatch _ self position]]
ifFalse: [pattern position: 0.
startMatch ifNotNil: [
self position: startMatch-1.
startMatch _ nil]].
self skip: -1].
self position: startMatch.
^ true
!
----- Method: PositionableStream>>basicNextChunk (in category 'fileIn/Out') -----
basicNextChunk
"Answer the contents of the receiver, up to the next terminator character. Doubled terminators indicate an embedded terminator character."
| terminator out ch |
terminator _ $!!.
out _ WriteStream on: (String new: 1000).
self skipSeparators.
[(ch _ self next) == nil] whileFalse: [
(ch == terminator) ifTrue: [
self peek == terminator ifTrue: [
self next. "skip doubled terminator"
] ifFalse: [
^ out contents "terminator is not doubled; we're done!!"
].
].
out nextPut: ch.
].
^ out contents!
----- Method: PositionableStream>>boolean (in category 'data get/put') -----
boolean
"Answer the next boolean value from this (binary) stream."
^ self next ~= 0
!
----- Method: PositionableStream>>boolean: (in category 'data get/put') -----
boolean: aBoolean
"Store the given boolean value on this (binary) stream."
self nextPut: (aBoolean ifTrue: [1] ifFalse: [0]).
!
----- Method: PositionableStream>>checkForPreamble: (in category 'fileIn/Out') -----
checkForPreamble: chunk
((chunk beginsWith: '"Change Set:') and: [ChangeSet current preambleString == nil])
ifTrue: [ChangeSet current preambleString: chunk].
((chunk beginsWith: '"Postscript:') and: [ChangeSet current postscriptString == nil])
ifTrue: [ChangeSet current postscriptString: chunk].
!
----- Method: PositionableStream>>command: (in category 'fileIn/Out') -----
command: aString
"Overridden by HtmlFileStream to append commands directly without translation. 4/5/96 tk"
"We ignore any HTML commands. Do nothing"!
----- Method: PositionableStream>>contents (in category 'accessing') -----
contents
"Answer with a copy of my collection from 1 to readLimit."
^collection copyFrom: 1 to: readLimit!
----- Method: PositionableStream>>contentsOfEntireFile (in category 'accessing') -----
contentsOfEntireFile
"For non-file streams"
^ self contents!
----- Method: PositionableStream>>copyMethodChunkFrom: (in category 'fileIn/Out') -----
copyMethodChunkFrom: aStream
"Copy the next chunk from aStream (must be different from the receiver)."
| chunk |
chunk _ aStream nextChunkText.
chunk runs values size = 1 "Optimize for unembellished text"
ifTrue: [self nextChunkPut: chunk asString]
ifFalse: [self nextChunkPutWithStyle: chunk]!
----- Method: PositionableStream>>copyMethodChunkFrom:at: (in category 'fileIn/Out') -----
copyMethodChunkFrom: aStream at: pos
"Copy the next chunk from aStream (must be different from the receiver)."
| chunk |
aStream position: pos.
chunk _ aStream nextChunkText.
chunk runs values size = 1 "Optimize for unembellished text"
ifTrue: [self nextChunkPut: chunk asString]
ifFalse: [self nextChunkPutWithStyle: chunk]!
----- Method: PositionableStream>>copyPreamble:from:at: (in category 'filein/out') -----
copyPreamble: preamble from: aStream at: pos
"Look for a changeStamp for this method by peeking backward.
Write a method preamble, with that stamp if found."
| terminator methodPos p last50 stamp i |
terminator _ $!!.
"Look back to find stamp in old preamble, such as...
Polygon methodsFor: 'private' stamp: 'di 6/25/97 21:42' prior: 34957598!! "
aStream position: pos.
methodPos _ aStream position.
(aStream isMemberOf: MultiByteFileStream) ifTrue: [
aStream position: (p _ 0 max: methodPos-100).
last50 _ aStream basicNext: methodPos - p.
] ifFalse: [
aStream position: (p _ 0 max: methodPos-50).
last50 _ aStream next: methodPos - p.
].
stamp _ String new.
(i _ last50 findLastOccuranceOfString: 'stamp:' startingAt: 1) > 0 ifTrue:
[stamp _ (last50 copyFrom: i+8 to: last50 size) copyUpTo: $'].
"Write the new preamble, with old stamp if any."
self cr; nextPut: terminator.
self nextChunkPut: (String streamContents:
[:strm |
strm nextPutAll: preamble.
stamp size > 0 ifTrue:
[strm nextPutAll: ' stamp: '; print: stamp]]).
self cr!
----- Method: PositionableStream>>decodeString:andRuns: (in category 'fileIn/Out') -----
decodeString: string andRuns: runsRaw
| strm runLength runValues newString index |
strm _ ReadStream on: runsRaw from: 1 to: runsRaw size.
(strm peekFor: $( ) ifFalse: [^ nil].
runLength _ OrderedCollection new.
[strm skipSeparators.
strm peekFor: $)] whileFalse:
[runLength add: (Number readFrom: strm)].
runValues _ OrderedCollection new.
[strm atEnd not] whileTrue:
[runValues add: (Number readFrom: strm).
strm next.].
newString _ WideString new: string size.
index _ 1.
runLength with: runValues do: [:length :leadingChar |
index to: index + length - 1 do: [:pos |
newString at: pos put: (Character leadingChar: leadingChar code: (string at: pos) charCode).
].
index _ index + length.
].
^ newString.
!
----- Method: PositionableStream>>decodeStyle:version: (in category 'fileIn/Out') -----
decodeStyle: runsObjData version: styleVersion
"Decode the runs array from the ReferenceStream it is stored in."
"Verify that the class mentioned have the same inst vars as we have now"
| structureInfo |
styleVersion = RemoteString currentTextAttVersion ifTrue: [
"Matches our classes, no need for checking"
^ (ReferenceStream on: runsObjData) next].
structureInfo _ RemoteString structureAt: styleVersion. "or nil"
"See SmartRefStream instVarInfo: for dfn"
^ SmartRefStream read: runsObjData withClasses: structureInfo!
----- Method: PositionableStream>>fileIn (in category 'fileIn/Out') -----
fileIn
"This is special for reading expressions from text that has been formatted
with exclamation delimitors. The expressions are read and passed to the
Compiler. Answer the result of compilation."
| msg |
msg := self name = 'a stream' translated
ifTrue:
['']
ifFalse:
[self name].
^ self fileInAnnouncing: 'Reading ' translated, msg!
----- Method: PositionableStream>>fileInAnnouncing: (in category 'fileIn/Out') -----
fileInAnnouncing: announcement
"This is special for reading expressions from text that has been formatted
with exclamation delimitors. The expressions are read and passed to the
Compiler. Answer the result of compilation. Put up a progress report with
the given announcement as the title."
| val chunk classes reader |
classes := Set new.
classes add: UndefinedObject; add: ObjectScanner.
announcement
displayProgressAt: Sensor cursorPoint
from: 0
to: self size
during:
[:bar |
[self atEnd] whileFalse:
[bar value: self position.
self skipSeparators.
[val := (self peekFor: $!!)
ifTrue: [
reader := Compiler evaluate: self nextChunk logged: false.
(reader isMemberOf: ClassCategoryReader) ifTrue: [classes add: reader theClass].
reader scanFrom: self]
ifFalse:
[chunk := self nextChunk.
self checkForPreamble: chunk.
Compiler evaluate: chunk logged: true]]
on: InMidstOfFileinNotification
do: [:ex | ex resume: true].
self skipStyleChunk].
self close].
"Note: The main purpose of this banner is to flush the changes file."
SmalltalkImage current logChange: '----End fileIn of ' , self name , '----'.
classes do: [:c | c forgetDoIts].
^val!
----- Method: PositionableStream>>fileInFor:announcing: (in category 'fileIn/Out') -----
fileInFor: client announcing: announcement
"This is special for reading expressions from text that has been formatted
with exclamation delimitors. The expressions are read and passed to the
Compiler. Answer the result of compilation. Put up a progress report with
the given announcement as the title.
Does NOT handle preambles or postscripts specially."
| val chunk |
announcement displayProgressAt: Sensor cursorPoint
from: 0 to: self size
during:
[:bar |
[self atEnd]
whileFalse:
[bar value: self position.
self skipSeparators.
[ val _ (self peekFor: $!!) ifTrue: [
(Compiler evaluate: self nextChunk for: client logged: false) scanFrom: self
] ifFalse: [
chunk _ self nextChunk.
self checkForPreamble: chunk.
Compiler evaluate: chunk for: client logged: true ].
] on: InMidstOfFileinNotification
do: [ :ex | ex resume: true].
self atEnd ifFalse: [ self skipStyleChunk ]].
self close].
"Note: The main purpose of this banner is to flush the changes file."
SmalltalkImage current logChange: '----End fileIn of ' , self name , '----'.
Smalltalk forgetDoIts.
^ val!
----- Method: PositionableStream>>fileInSilentlyAnnouncing: (in category 'fileIn/Out') -----
fileInSilentlyAnnouncing: announcement
"This is special for reading expressions from text that has been formatted
with exclamation delimitors. The expressions are read and passed to the
Compiler. Answer the result of compilation. Put up a progress report with
the given announcement as the title."
| val chunk |
[self atEnd] whileFalse:
[self skipSeparators.
[val := (self peekFor: $!!)
ifTrue: [(Compiler evaluate: self nextChunk logged: false) scanFrom: self]
ifFalse:
[chunk := self nextChunk.
self checkForPreamble: chunk.
Compiler evaluate: chunk logged: true]]
on: InMidstOfFileinNotification
do: [:ex | ex resume: true].
self skipStyleChunk].
self close.
"Note: The main purpose of this banner is to flush the changes file."
SmalltalkImage current logChange: '----End fileIn of ' , self name , '----'.
self flag: #ThisMethodShouldNotBeThere. "sd"
SystemNavigation new allBehaviorsDo:
[:cl |
cl
removeSelectorSimply: #DoIt;
removeSelectorSimply: #DoItIn:].
^val!
----- Method: PositionableStream>>header (in category 'fileIn/Out') -----
header
"If the stream requires a standard header, override this message. See HtmlFileStream"!
----- Method: PositionableStream>>int16 (in category 'data get/put') -----
int16
"Answer the next signed, 16-bit integer from this (binary) stream."
| n |
n _ self next.
n _ (n bitShift: 8) + (self next).
n >= 16r8000 ifTrue: [n _ n - 16r10000].
^ n
!
----- Method: PositionableStream>>int16: (in category 'data get/put') -----
int16: anInteger
"Store the given signed, 16-bit integer on this (binary) stream."
| n |
(anInteger < -16r8000) | (anInteger >= 16r8000)
ifTrue: [self error: 'outside 16-bit integer range'].
anInteger < 0
ifTrue: [n _ 16r10000 + anInteger]
ifFalse: [n _ anInteger].
self nextPut: (n digitAt: 2).
self nextPut: (n digitAt: 1).
!
----- Method: PositionableStream>>int32 (in category 'data get/put') -----
int32
"Answer the next signed, 32-bit integer from this (binary) stream."
"Details: As a fast check for negative number, check the high bit of the first digit"
| n firstDigit |
n _ firstDigit _ self next.
n _ (n bitShift: 8) + self next.
n _ (n bitShift: 8) + self next.
n _ (n bitShift: 8) + self next.
firstDigit >= 128 ifTrue: [n _ -16r100000000 + n]. "decode negative 32-bit integer"
^ n
!
----- Method: PositionableStream>>int32: (in category 'data get/put') -----
int32: anInteger
"Store the given signed, 32-bit integer on this (binary) stream."
| n |
(anInteger < -16r80000000) | (anInteger >= 16r80000000)
ifTrue: [self error: 'outside 32-bit integer range'].
anInteger < 0
ifTrue: [n _ 16r100000000 + anInteger]
ifFalse: [n _ anInteger].
self nextPut: (n digitAt: 4).
self nextPut: (n digitAt: 3).
self nextPut: (n digitAt: 2).
self nextPut: (n digitAt: 1).
!
----- Method: PositionableStream>>isBinary (in category 'testing') -----
isBinary
"Return true if the receiver is a binary byte stream"
^collection class == ByteArray!
----- Method: PositionableStream>>isEmpty (in category 'testing') -----
isEmpty
"Answer whether the receiver's contents has no elements."
^position = 0!
----- Method: PositionableStream>>last (in category 'accessing') -----
last
"Return the final element in the receiver"
^ collection at: position!
----- Method: PositionableStream>>match: (in category 'positioning') -----
match: subCollection
"Set the access position of the receiver to be past the next occurrence of the subCollection. Answer whether subCollection is found. No wildcards, and case does matter."
| pattern startMatch |
pattern _ ReadStream on: subCollection.
startMatch _ nil.
[pattern atEnd] whileFalse:
[self atEnd ifTrue: [^ false].
(self next) = (pattern next)
ifTrue: [pattern position = 1 ifTrue: [startMatch _ self position]]
ifFalse: [pattern position: 0.
startMatch ifNotNil: [
self position: startMatch.
startMatch _ nil]]].
^ true
!
----- Method: PositionableStream>>next: (in category 'accessing') -----
next: anInteger
"Answer the next anInteger elements of my collection. Must override
because default uses self contents species, which might involve a large
collection."
| newArray |
newArray _ collection species new: anInteger.
1 to: anInteger do: [:index | newArray at: index put: self next].
^newArray!
----- Method: PositionableStream>>next:into: (in category 'accessing') -----
next: n into: aCollection
"Read n objects into the given collection.
Return aCollection or a partial copy if less than
n elements have been read."
^self next: n into: aCollection startingAt: 1!
----- Method: PositionableStream>>next:into:startingAt: (in category 'accessing') -----
next: n into: aCollection startingAt: startIndex
"Read n objects into the given collection.
Return aCollection or a partial copy if less than
n elements have been read."
| obj |
0 to: n-1 do:[:i|
(obj _ self next) == nil ifTrue:[^aCollection copyFrom: 1 to: startIndex+i-1].
aCollection at: startIndex+i put: obj].
^aCollection!
----- Method: PositionableStream>>next:putAll: (in category 'accessing') -----
next: anInteger putAll: aCollection
"Store the next anInteger elements from the given collection."
^self next: anInteger putAll: aCollection startingAt: 1!
----- Method: PositionableStream>>next:putAll:startingAt: (in category 'accessing') -----
next: anInteger putAll: aCollection startingAt: startIndex
"Store the next anInteger elements from the given collection."
(startIndex = 1 and:[anInteger = aCollection size])
ifTrue:[^self nextPutAll: aCollection].
^self nextPutAll: (aCollection copyFrom: startIndex to: startIndex+anInteger-1)!
----- Method: PositionableStream>>nextChunk (in category 'fileIn/Out') -----
nextChunk
"Answer the contents of the receiver, up to the next terminator character. Doubled terminators indicate an embedded terminator character."
| terminator out ch |
terminator _ $!!.
out _ WriteStream on: (String new: 1000).
self skipSeparators.
[(ch _ self next) == nil] whileFalse: [
(ch == terminator) ifTrue: [
self peek == terminator ifTrue: [
self next. "skip doubled terminator"
] ifFalse: [
^ self parseLangTagFor: out contents "terminator is not doubled; we're done!!"
].
].
out nextPut: ch.
].
^ self parseLangTagFor: out contents.
!
----- Method: PositionableStream>>nextChunkText (in category 'fileIn/Out') -----
nextChunkText
"Deliver the next chunk as a Text. Decode the following ]style[ chunk if present. Position at start of next real chunk."
| string runsRaw strm runs peek pos |
"Read the plain text"
string _ self nextChunk.
"Test for ]style[ tag"
pos _ self position.
peek _ self skipSeparatorsAndPeekNext.
peek = $] ifFalse: [self position: pos. ^ string asText]. "no tag"
(self upTo: $[) = ']style' ifFalse: [self position: pos. ^ string asText]. "different tag"
"Read and decode the style chunk"
runsRaw _ self basicNextChunk. "style encoding"
strm _ ReadStream on: runsRaw from: 1 to: runsRaw size.
runs _ RunArray scanFrom: strm.
^ Text basicNew setString: string setRunsChecking: runs.
!
----- Method: PositionableStream>>nextDelimited: (in category 'accessing') -----
nextDelimited: terminator
"Answer the contents of the receiver, up to the next terminator character. Doubled terminators indicate an embedded terminator character. For example: 'this '' was a quote'. Start postioned before the initial terminator."
| out ch |
out _ WriteStream on: (String new: 1000).
self atEnd ifTrue: [^ ''].
self next == terminator ifFalse: [self skip: -1]. "absorb initial terminator"
[(ch _ self next) == nil] whileFalse: [
(ch == terminator) ifTrue: [
self peek == terminator ifTrue: [
self next. "skip doubled terminator"
] ifFalse: [
^ out contents "terminator is not doubled; we're done!!"
].
].
out nextPut: ch.
].
^ out contents!
----- Method: PositionableStream>>nextInt32 (in category 'nonhomogeneous accessing') -----
nextInt32
"Read a 32-bit signed integer from the next 4 bytes"
| s |
s _ 0.
1 to: 4 do: [:i | s _ (s bitShift: 8) + self next].
(s bitAnd: 16r80000000) = 0
ifTrue: [^ s]
ifFalse: [^ -1 - s bitInvert32]!
----- Method: PositionableStream>>nextInt32Put: (in category 'nonhomogeneous accessing') -----
nextInt32Put: int32
"Write a signed integer to the next 4 bytes"
| pos |
pos _ int32 < 0
ifTrue: [(0-int32) bitInvert32 + 1]
ifFalse: [int32].
1 to: 4 do: [:i | self nextPut: (pos digitAt: 5-i)].
^ int32!
----- Method: PositionableStream>>nextInto: (in category 'accessing') -----
nextInto: aCollection
"Read the next elements of the receiver into aCollection.
Return aCollection or a partial copy if less than aCollection
size elements have been read."
^self next: aCollection size into: aCollection startingAt: 1.!
----- Method: PositionableStream>>nextInto:startingAt: (in category 'accessing') -----
nextInto: aCollection startingAt: startIndex
"Read the next elements of the receiver into aCollection.
Return aCollection or a partial copy if less than aCollection
size elements have been read."
^self next: (aCollection size - startIndex+1) into: aCollection startingAt: startIndex.!
----- Method: PositionableStream>>nextLine (in category 'accessing') -----
nextLine
"Answer next line (may be empty), or nil if at end"
self atEnd ifTrue: [^nil].
^self upTo: Character cr!
----- Method: PositionableStream>>nextLittleEndianNumber: (in category 'nonhomogeneous accessing') -----
nextLittleEndianNumber: n
"Answer the next n bytes as a positive Integer or LargePositiveInteger, where the bytes are ordered from least significant to most significant."
| bytes s |
bytes _ self next: n.
s _ 0.
n to: 1 by: -1 do: [:i | s _ (s bitShift: 8) bitOr: (bytes at: i)].
^ s
!
----- Method: PositionableStream>>nextLittleEndianNumber:put: (in category 'nonhomogeneous accessing') -----
nextLittleEndianNumber: n put: value
"Answer the next n bytes as a positive Integer or LargePositiveInteger, where the bytes are ordered from least significant to most significant."
| bytes |
bytes _ ByteArray new: n.
1 to: n do: [: i | bytes at: i put: (value digitAt: i)].
self nextPutAll: bytes!
----- Method: PositionableStream>>nextNumber: (in category 'nonhomogeneous accessing') -----
nextNumber: n
"Answer the next n bytes as a positive Integer or LargePositiveInteger."
| s |
s _ 0.
1 to: n do:
[:i | s _ (s bitShift: 8) bitOr: self next asInteger].
^ s normalize!
----- Method: PositionableStream>>nextNumber:put: (in category 'nonhomogeneous accessing') -----
nextNumber: n put: v
"Append to the receiver the argument, v, which is a positive
SmallInteger or a LargePositiveInteger, as the next n bytes.
Possibly pad with leading zeros."
1 to: n do: [:i | self nextPut: (v digitAt: n+1-i)].
^ v
!
----- Method: PositionableStream>>nextString (in category 'nonhomogeneous accessing') -----
nextString
"Read a string from the receiver. The first byte is the length of the string, unless it is greater than 192, in which case the first four bytes encode the length. I expect to be in ascii mode when called (caller puts back to binary)."
| length aByteArray |
"read the length in binary mode"
self binary.
length _ self next. "first byte."
length >= 192 ifTrue: [length _ length - 192.
1 to: 3 do: [:ii | length _ length * 256 + self next]].
aByteArray _ ByteArray new: length.
self nextInto: aByteArray.
^aByteArray asString.
!
----- Method: PositionableStream>>nextStringOld (in category 'nonhomogeneous accessing') -----
nextStringOld
"Read a string from the receiver. The first byte is the length of the
string, unless it is greater than 192, in which case the first *two* bytes
encode the length. Max size 16K. "
| aString length |
length _ self next. "first byte."
length >= 192 ifTrue: [length _ (length - 192) * 256 + self next].
aString _ String new: length.
1 to: length do: [:ii | aString at: ii put: self next asCharacter].
^aString!
----- Method: PositionableStream>>nextStringPut: (in category 'nonhomogeneous accessing') -----
nextStringPut: s
"Append the string, s, to the receiver. Only used by DataStream. Max size of 64*256*256*256."
| length |
(length _ s size) < 192
ifTrue: [self nextPut: length]
ifFalse:
[self nextPut: (length digitAt: 4)+192.
self nextPut: (length digitAt: 3).
self nextPut: (length digitAt: 2).
self nextPut: (length digitAt: 1)].
self nextPutAll: s asByteArray.
^s!
----- Method: PositionableStream>>nextWord (in category 'nonhomogeneous accessing') -----
nextWord
"Answer the next two bytes from the receiver as an Integer."
| high low |
high _ self next.
high==nil ifTrue: [^false].
low _ self next.
low==nil ifTrue: [^false].
^(high asInteger bitShift: 8) + low asInteger!
----- Method: PositionableStream>>nextWordPut: (in category 'nonhomogeneous accessing') -----
nextWordPut: aWord
"Append to the receiver an Integer as the next two bytes."
self nextPut: ((aWord bitShift: -8) bitAnd: 255).
self nextPut: (aWord bitAnd: 255).
^aWord!
----- Method: PositionableStream>>nextWordsInto: (in category 'accessing') -----
nextWordsInto: aBitmap
"Fill the word based buffer from my collection.
Stored on stream as Big Endian. Optimized for speed.
Read in BigEndian, then restoreEndianness."
| blt pos source byteSize |
collection class isBytes
ifFalse: [^ self next: aBitmap size into: aBitmap startingAt: 1].
byteSize := aBitmap byteSize.
"is the test on collection basicSize \\ 4 necessary?"
((self position bitAnd: 3) = 0 and: [ (collection basicSize bitAnd: 3) = 0])
ifTrue: [source := collection.
pos := self position.
self skip: byteSize]
ifFalse: ["forced to copy it into a buffer"
source := self next: byteSize.
pos := 0].
"Now use BitBlt to copy the bytes to the bitmap."
blt := (BitBlt current
toForm: (Form new hackBits: aBitmap))
sourceForm: (Form new hackBits: source).
blt combinationRule: Form over. "store"
blt sourceX: 0;
sourceY: pos // 4;
height: byteSize // 4;
width: 4.
blt destX: 0;
destY: 0.
blt copyBits.
"And do whatever the bitmap needs to do to convert from big-endian order."
aBitmap restoreEndianness.
^ aBitmap "May be WordArray, ColorArray, etc"
!
----- Method: PositionableStream>>on: (in category 'private') -----
on: aCollection
collection _ aCollection.
readLimit _ aCollection size.
position _ 0.
self reset!
----- Method: PositionableStream>>originalContents (in category 'accessing') -----
originalContents
"Answer the receiver's actual contents collection, NOT a copy. 1/29/96 sw"
^ collection!
----- Method: PositionableStream>>padTo:put: (in category 'positioning') -----
padTo: nBytes put: aCharacter
"Pad using the argument, aCharacter, to the next boundary of nBytes characters."
| rem |
rem _ nBytes - (self position \\ nBytes).
rem = nBytes ifTrue: [^ 0].
self next: rem put: aCharacter.!
----- Method: PositionableStream>>padToNextLongPut: (in category 'positioning') -----
padToNextLongPut: char
"Make position be on long word boundary, writing the padding
character, char, if necessary."
[self position \\ 4 = 0]
whileFalse: [self nextPut: char]!
----- Method: PositionableStream>>parseLangTagFor: (in category 'fileIn/Out') -----
parseLangTagFor: aString
| string peek runsRaw pos |
string _ aString.
"Test for ]lang[ tag"
pos _ self position.
peek _ self skipSeparatorsAndPeekNext.
peek = $] ifFalse: [self position: pos. ^ string]. "no tag"
(self upTo: $[) = ']lang' ifTrue: [
runsRaw _ self basicNextChunk.
string _ self decodeString: aString andRuns: runsRaw
] ifFalse: [
self position: pos
].
^ string.
!
----- Method: PositionableStream>>peek (in category 'accessing') -----
peek
"Answer what would be returned if the message next were sent to the
receiver. If the receiver is at the end, answer nil."
| nextObject |
self atEnd ifTrue: [^nil].
nextObject _ self next.
position _ position - 1.
^nextObject!
----- Method: PositionableStream>>peekBack (in category 'accessing') -----
peekBack
"Return the element at the previous position, without changing position. Use indirect messages in case self is a StandardFileStream."
| element |
element _ self back.
self skip: 1.
^ element!
----- Method: PositionableStream>>peekFor: (in category 'accessing') -----
peekFor: anObject
"Answer false and do not move over the next element if it is not equal to
the argument, anObject, or if the receiver is at the end. Answer true
and increment the position for accessing elements, if the next element is
equal to anObject."
| nextObject |
self atEnd ifTrue: [^false].
nextObject _ self next.
"peek for matching element"
anObject = nextObject ifTrue: [^true].
"gobble it if found"
position _ position - 1.
^false!
----- Method: PositionableStream>>position (in category 'positioning') -----
position
"Answer the current position of accessing the sequence of objects."
^position!
----- Method: PositionableStream>>position: (in category 'positioning') -----
position: anInteger
"Set the current position for accessing the objects to be anInteger, as long
as anInteger is within the bounds of the receiver's contents. If it is not,
create an error notification."
anInteger >= 0 & (anInteger <= readLimit)
ifTrue: [position _ anInteger]
ifFalse: [self positionError]!
----- Method: PositionableStream>>positionError (in category 'private') -----
positionError
"Since I am not necessarily writable, it is up to my subclasses to override
position: if expanding the collection is preferrable to giving this error."
self error: 'Attempt to set the position of a PositionableStream out of bounds'!
----- Method: PositionableStream>>positionOfSubCollection: (in category 'positioning') -----
positionOfSubCollection: subCollection
"Return a position such that that element at the new position equals the first element of sub, and the next elements equal the rest of the elements of sub. Begin the search at the current position.
If no such match is found, answer 0."
^self positionOfSubCollection: subCollection ifAbsent: [0]!
----- Method: PositionableStream>>positionOfSubCollection:ifAbsent: (in category 'positioning') -----
positionOfSubCollection: subCollection ifAbsent: exceptionBlock
"Return a position such that that element at the new position equals the first element of sub, and the next elements equal the rest of the elements of sub. Begin the search at the current position.
If no such match is found, answer the result of evaluating argument, exceptionBlock."
| pattern startPosition currentPosition |
pattern _ ReadStream on: subCollection.
startPosition := self position.
[pattern atEnd] whileFalse:
[self atEnd ifTrue: [^exceptionBlock value].
self next = pattern next
ifFalse: [pattern reset]].
currentPosition := self position.
self position: startPosition.
^pattern atEnd
ifTrue: [currentPosition + 1 - subCollection size]
ifFalse: [exceptionBlock value]!
----- Method: PositionableStream>>pushBack: (in category 'positioning') -----
pushBack: aString
"Compatibility with SocketStreams"
self skip: aString size negated!
----- Method: PositionableStream>>reset (in category 'positioning') -----
reset
"Set the receiver's position to the beginning of the sequence of objects."
position _ 0!
----- Method: PositionableStream>>resetContents (in category 'positioning') -----
resetContents
"Set the position and limits to 0."
position _ 0.
readLimit _ 0!
----- Method: PositionableStream>>setFrom:to: (in category 'private') -----
setFrom: newStart to: newStop
position _ newStart - 1.
readLimit _ newStop!
----- Method: PositionableStream>>setToEnd (in category 'positioning') -----
setToEnd
"Set the position of the receiver to the end of the sequence of objects."
position _ readLimit!
----- Method: PositionableStream>>skip: (in category 'positioning') -----
skip: anInteger
"Set the receiver's position to be the current position+anInteger. A
subclass might choose to be more helpful and select the minimum of the
receiver's size and position+anInteger, or the maximum of 1 and
position+anInteger for the repositioning."
self position: position + anInteger!
----- Method: PositionableStream>>skipSeparators (in category 'fileIn/Out') -----
skipSeparators
[self atEnd]
whileFalse:
[self next isSeparator ifFalse: [^ self position: self position-1]]!
----- Method: PositionableStream>>skipSeparatorsAndPeekNext (in category 'fileIn/Out') -----
skipSeparatorsAndPeekNext
"A special function to make nextChunk fast"
| peek |
[self atEnd]
whileFalse:
[(peek _ self next) isSeparator
ifFalse: [self position: self position-1. ^ peek]]!
----- Method: PositionableStream>>skipStyleChunk (in category 'fileIn/Out') -----
skipStyleChunk
"Get to the start of the next chunk that is not a style for the previous chunk"
| pos |
pos _ self position.
self skipSeparators.
self peek == $]
ifTrue: [(self upTo: $[) = ']text' "old -- no longer needed"
"now positioned past the open bracket"
ifFalse: [self nextChunk]] "absorb ]style[ and its whole chunk"
ifFalse: [self position: pos] "leave untouched"
!
----- Method: PositionableStream>>skipTo: (in category 'positioning') -----
skipTo: anObject
"Set the access position of the receiver to be past the next occurrence of
anObject. Answer whether anObject is found."
[self atEnd]
whileFalse: [self next = anObject ifTrue: [^true]].
^false!
----- Method: PositionableStream>>string (in category 'data get/put') -----
string
"Answer the next string from this (binary) stream."
| size |
size _ self uint16.
^ (self next: size) asString
!
----- Method: PositionableStream>>string: (in category 'data get/put') -----
string: aString
"Store the given string on this (binary) stream. The string must contain 65535 or fewer characters."
aString size > 16rFFFF ifTrue: [self error: 'string too long for this format'].
self uint16: aString size.
self nextPutAll: aString asByteArray.
!
----- Method: PositionableStream>>trailer (in category 'fileIn/Out') -----
trailer
"If the stream requires a standard trailer, override this message. See HtmlFileStream"!
----- Method: PositionableStream>>uint16 (in category 'data get/put') -----
uint16
"Answer the next unsigned, 16-bit integer from this (binary) stream."
| n |
n _ self next.
n _ (n bitShift: 8) + (self next).
^ n
!
----- Method: PositionableStream>>uint16: (in category 'data get/put') -----
uint16: anInteger
"Store the given unsigned, 16-bit integer on this (binary) stream."
(anInteger < 0) | (anInteger >= 16r10000)
ifTrue: [self error: 'outside unsigned 16-bit integer range'].
self nextPut: (anInteger digitAt: 2).
self nextPut: (anInteger digitAt: 1).
!
----- Method: PositionableStream>>uint24 (in category 'data get/put') -----
uint24
"Answer the next unsigned, 24-bit integer from this (binary) stream."
| n |
n _ self next.
n _ (n bitShift: 8) + self next.
n _ (n bitShift: 8) + self next.
^ n
!
----- Method: PositionableStream>>uint24: (in category 'data get/put') -----
uint24: anInteger
"Store the given unsigned, 24-bit integer on this (binary) stream."
(anInteger < 0) | (anInteger >= 16r1000000)
ifTrue: [self error: 'outside unsigned 24-bit integer range'].
self nextPut: (anInteger digitAt: 3).
self nextPut: (anInteger digitAt: 2).
self nextPut: (anInteger digitAt: 1).
!
----- Method: PositionableStream>>uint32 (in category 'data get/put') -----
uint32
"Answer the next unsigned, 32-bit integer from this (binary) stream."
| n |
n _ self next.
n _ (n bitShift: 8) + self next.
n _ (n bitShift: 8) + self next.
n _ (n bitShift: 8) + self next.
^ n
!
----- Method: PositionableStream>>uint32: (in category 'data get/put') -----
uint32: anInteger
"Store the given unsigned, 32-bit integer on this (binary) stream."
(anInteger < 0) | (anInteger >= 16r100000000)
ifTrue: [self error: 'outside unsigned 32-bit integer range'].
self nextPut: (anInteger digitAt: 4).
self nextPut: (anInteger digitAt: 3).
self nextPut: (anInteger digitAt: 2).
self nextPut: (anInteger digitAt: 1).
!
----- Method: PositionableStream>>unCommand (in category 'fileIn/Out') -----
unCommand
"If this read stream is at a <, then skip up to just after the next >. For removing html commands."
| char |
[self peek = $<] whileTrue: ["begin a block"
[self atEnd == false and: [self next ~= $>]] whileTrue.
"absorb characters"
].
!
----- Method: PositionableStream>>untilEndWithFork:displayingProgress: (in category 'positioning') -----
untilEndWithFork: aBlock displayingProgress: aString
| sem done result |
sem := Semaphore new.
done := false.
[[result := aBlock value]
ensure: [done := true.
sem signal]] fork.
self
untilEnd: [done
ifTrue: [^ result].
(Delay forSeconds: 0.2) wait]
displayingProgress: aString.
sem wait.
^ result!
----- Method: PositionableStream>>upTo: (in category 'accessing') -----
upTo: anObject
"Answer a subcollection from the current access position to the
occurrence (if any, but not inclusive) of anObject in the receiver. If
anObject is not in the collection, answer the entire rest of the receiver."
| newStream element |
newStream _ WriteStream on: (collection species new: 100).
[self atEnd or: [(element _ self next) = anObject]]
whileFalse: [newStream nextPut: element].
^newStream contents!
----- Method: PositionableStream>>upToAll: (in category 'accessing') -----
upToAll: aCollection
"Answer a subcollection from the current access position to the occurrence (if any, but not inclusive) of aCollection. If aCollection is not in the stream, answer the entire rest of the stream."
| startPos endMatch result |
startPos _ self position.
(self match: aCollection)
ifTrue: [endMatch _ self position.
self position: startPos.
result _ self next: endMatch - startPos - aCollection size.
self position: endMatch.
^ result]
ifFalse: [self position: startPos.
^ self upToEnd]!
----- Method: PositionableStream>>upToEnd (in category 'accessing') -----
upToEnd
"Answer a subcollection from the current access position through the last element of the receiver."
| newStream |
newStream _ WriteStream on: (collection species new: 100).
[self atEnd] whileFalse: [ newStream nextPut: self next ].
^ newStream contents!
----- Method: PositionableStream>>verbatim: (in category 'fileIn/Out') -----
verbatim: aString
"Do not attempt to translate the characters. Use to override nextPutAll:"
^ self nextPutAll: aString!
PositionableStream subclass: #ReadStream
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!ReadStream commentStamp: '<historical>' prior: 0!
I represent an accessor for a sequence of objects that can only read objects from the sequence.!
----- Method: ReadStream class>>on:from:to: (in category 'instance creation') -----
on: aCollection from: firstIndex to: lastIndex
"Answer with a new instance streaming over a copy of aCollection from
firstIndex to lastIndex."
^self basicNew
on: aCollection
from: firstIndex
to: lastIndex!
----- Method: ReadStream>>ascii (in category 'accessing') -----
ascii!
----- Method: ReadStream>>binary (in category 'accessing') -----
binary!
----- Method: ReadStream>>localName (in category 'file stream compatibility') -----
localName
^'ReadStream'!
----- Method: ReadStream>>next (in category 'accessing') -----
next
"Primitive. Answer the next object in the Stream represented by the
receiver. Fail if the collection of this stream is not an Array or a String.
Fail if the stream is positioned at its end, or if the position is out of
bounds in the collection. Optional. See Object documentation
whatIsAPrimitive."
<primitive: 65>
position >= readLimit
ifTrue: [^nil]
ifFalse: [^collection at: (position _ position + 1)]!
----- Method: ReadStream>>next: (in category 'accessing') -----
next: anInteger
"Answer the next anInteger elements of my collection. overriden for efficiency"
| ans endPosition |
endPosition _ position + anInteger min: readLimit.
ans _ collection copyFrom: position+1 to: endPosition.
position _ endPosition.
^ans
!
----- Method: ReadStream>>next:into:startingAt: (in category 'accessing') -----
next: n into: aCollection startingAt: startIndex
"Read n objects into the given collection.
Return aCollection or a partial copy if less than
n elements have been read."
| max |
max _ (readLimit - position) min: n.
aCollection
replaceFrom: startIndex
to: startIndex+max-1
with: collection
startingAt: position+1.
position _ position + max.
max = n
ifTrue:[^aCollection]
ifFalse:[^aCollection copyFrom: 1 to: startIndex+max-1]!
----- Method: ReadStream>>nextPut: (in category 'accessing') -----
nextPut: anObject
self shouldNotImplement!
----- Method: ReadStream>>on:from:to: (in category 'private') -----
on: aCollection from: firstIndex to: lastIndex
| len |
collection _ aCollection.
readLimit _ lastIndex > (len _ collection size)
ifTrue: [len]
ifFalse: [lastIndex].
position _ firstIndex <= 1
ifTrue: [0]
ifFalse: [firstIndex - 1]!
----- Method: ReadStream>>openReadOnly (in category 'file stream compatibility') -----
openReadOnly!
----- Method: ReadStream>>readOnly (in category 'file stream compatibility') -----
readOnly!
----- Method: ReadStream>>readStream (in category 'accessing') -----
readStream
"polymorphic with SequenceableCollection. Return self"
^ self!
----- Method: ReadStream>>size (in category 'accessing') -----
size
"Compatibility with other streams (e.g., FileStream)"
^readLimit!
----- Method: ReadStream>>upTo: (in category 'accessing') -----
upTo: anObject
"fast version using indexOf:"
| start end |
start _ position+1.
end _ collection indexOf: anObject startingAt: start ifAbsent: [ 0 ].
"not present--return rest of the collection"
end = 0 ifTrue: [ ^self upToEnd ].
"skip to the end and return the data passed over"
position _ end.
^collection copyFrom: start to: (end-1)!
----- Method: ReadStream>>upToEnd (in category 'accessing') -----
upToEnd
| start |
start _ position+1.
position _ collection size.
^collection copyFrom: start to: position!
PositionableStream subclass: #WriteStream
instanceVariableNames: 'writeLimit'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!WriteStream commentStamp: '<historical>' prior: 0!
I represent an accessor for a sequence of objects that can only store objects in the sequence.!
WriteStream subclass: #LimitedWriteStream
instanceVariableNames: 'limit limitBlock'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!LimitedWriteStream commentStamp: '<historical>' prior: 0!
A LimitedWriteStream is a specialized WriteStream that has a maximum size of the collection it streams over. When this limit is reached a special limitBlock is executed. This can for example be used to "bail out" of lengthy streaming operations before they have finished. For a simple example take a look at the universal Object printString.
The message SequenceableCollection class streamContents:limitedTo: creates a LimitedWriteStream. In this case it prevents very large (or possibly recursive) object structures to "overdo" their textual representation. !
----- Method: LimitedWriteStream>>nextPut: (in category 'accessing') -----
nextPut: anObject
"Ensure that the limit is not exceeded"
position >= limit ifTrue: [limitBlock value]
ifFalse: [super nextPut: anObject].
!
----- Method: LimitedWriteStream>>nextPutAll: (in category 'as yet unclassified') -----
nextPutAll: aCollection
| newEnd |
collection class == aCollection class ifFalse:
[^ super nextPutAll: aCollection ].
newEnd _ position + aCollection size.
newEnd > limit ifTrue: [
super nextPutAll: (aCollection copyFrom: 1 to: (limit - position max: 0)).
^ limitBlock value.
].
newEnd > writeLimit ifTrue: [
self growTo: newEnd + 10
].
collection replaceFrom: position+1 to: newEnd with: aCollection startingAt: 1.
position _ newEnd.!
----- Method: LimitedWriteStream>>pastEndPut: (in category 'as yet unclassified') -----
pastEndPut: anObject
collection size >= limit ifTrue: [limitBlock value]. "Exceptional return"
^ super pastEndPut: anObject!
----- Method: LimitedWriteStream>>setLimit:limitBlock: (in category 'as yet unclassified') -----
setLimit: sizeLimit limitBlock: aBlock
"Limit the numer of elements this stream will write..."
limit _ sizeLimit.
"Execute this (typically ^ contents) when that limit is exceded"
limitBlock _ aBlock!
WriteStream subclass: #ReadWriteStream
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!ReadWriteStream commentStamp: '<historical>' prior: 0!
I represent an accessor for a sequence of objects. My instances can both read and store objects.!
ReadWriteStream subclass: #RWBinaryOrTextStream
instanceVariableNames: 'isBinary'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!RWBinaryOrTextStream commentStamp: '<historical>' prior: 0!
A simulation of a FileStream, but living totally in memory. Hold the contents of a file or web page from the network. Can then fileIn like a normal FileStream.
Need to be able to switch between binary and text, as a FileStream does, without recopying the whole collection. Convert to binary upon input and output. Always keep as text internally.!
----- Method: RWBinaryOrTextStream>>asBinaryOrTextStream (in category 'as yet unclassified') -----
asBinaryOrTextStream
^ self!
----- Method: RWBinaryOrTextStream>>ascii (in category 'as yet unclassified') -----
ascii
isBinary _ false!
----- Method: RWBinaryOrTextStream>>binary (in category 'as yet unclassified') -----
binary
isBinary _ true!
----- Method: RWBinaryOrTextStream>>contents (in category 'as yet unclassified') -----
contents
"Answer with a copy of my collection from 1 to readLimit."
| newArray |
isBinary ifFalse: [^ super contents]. "String"
readLimit _ readLimit max: position.
newArray _ ByteArray new: readLimit.
^ newArray replaceFrom: 1
to: readLimit
with: collection
startingAt: 1.!
----- Method: RWBinaryOrTextStream>>contentsOfEntireFile (in category 'as yet unclassified') -----
contentsOfEntireFile
"For compatibility with file streams."
^ self contents!
----- Method: RWBinaryOrTextStream>>isBinary (in category 'as yet unclassified') -----
isBinary
^ isBinary!
----- Method: RWBinaryOrTextStream>>next (in category 'as yet unclassified') -----
next
| byte |
^ isBinary
ifTrue: [byte _ super next.
byte ifNil: [nil] ifNotNil: [byte asciiValue]]
ifFalse: [super next].
!
----- Method: RWBinaryOrTextStream>>next: (in category 'as yet unclassified') -----
next: anInteger
"Answer the next anInteger elements of my collection. Must override to get class right."
| newArray |
newArray _ (isBinary ifTrue: [ByteArray] ifFalse: [ByteString]) new: anInteger.
^ self nextInto: newArray!
----- Method: RWBinaryOrTextStream>>next:into:startingAt: (in category 'as yet unclassified') -----
next: n into: aCollection startingAt: startIndex
"Read n objects into the given collection.
Return aCollection or a partial copy if less than n elements have been read."
"Overriden for efficiency"
| max |
max _ (readLimit - position) min: n.
aCollection
replaceFrom: startIndex
to: startIndex+max-1
with: collection
startingAt: position+1.
position _ position + max.
max = n
ifTrue:[^aCollection]
ifFalse:[^aCollection copyFrom: 1 to: startIndex+max-1]!
----- Method: RWBinaryOrTextStream>>next:putAll:startingAt: (in category 'writing') -----
next: anInteger putAll: aCollection startingAt: startIndex
^super next: anInteger putAll: aCollection asString startingAt: startIndex!
----- Method: RWBinaryOrTextStream>>nextPut: (in category 'as yet unclassified') -----
nextPut: charOrByte
super nextPut: charOrByte asCharacter!
----- Method: RWBinaryOrTextStream>>nextPutAll: (in category 'writing') -----
nextPutAll: aCollection
^super nextPutAll: aCollection asString!
----- Method: RWBinaryOrTextStream>>padToEndWith: (in category 'as yet unclassified') -----
padToEndWith: aChar
"We don't have pages, so we are at the end, and don't need to pad."!
----- Method: RWBinaryOrTextStream>>reset (in category 'as yet unclassified') -----
reset
"Set the receiver's position to the beginning of the sequence of objects."
super reset.
isBinary ifNil: [isBinary _ false].
collection class == ByteArray ifTrue: ["Store as String and convert as needed."
collection _ collection asString.
isBinary _ true].
!
----- Method: RWBinaryOrTextStream>>setFileTypeToObject (in category 'as yet unclassified') -----
setFileTypeToObject
"do nothing. We don't have a file type"!
----- Method: RWBinaryOrTextStream>>text (in category 'as yet unclassified') -----
text
isBinary _ false!
----- Method: RWBinaryOrTextStream>>upToEnd (in category 'as yet unclassified') -----
upToEnd
"Must override to get class right."
| newArray |
newArray _ (isBinary ifTrue: [ByteArray] ifFalse: [ByteString]) new: self size - self position.
^ self nextInto: newArray!
----- Method: ReadWriteStream>>= (in category 'testing') -----
= other
(self class == ReadWriteStream and: [other class == ReadWriteStream]) ifFalse: [
^ super = other]. "does an identity test. Don't read contents of FileStream"
^ self position = other position and: [self contents = other contents]!
----- Method: ReadWriteStream>>asUnZippedStream (in category 'converting') -----
asUnZippedStream
| isGZip outputStream first strm archive which |
"Decompress this file if needed, and return a stream. No file is written. File extension may be .gz or anything else. Also works on archives (.zip, .gZip)."
strm _ self binary.
strm isZipArchive ifTrue: [
archive _ ZipArchive new readFrom: strm.
which _ archive members detect: [:any | any fileName asLowercase endsWith: '.ttf']
ifNone: [nil].
which ifNil: [archive close.
^ self error: 'Can''t find .ttf file in archive'].
strm _ which contentStream.
archive close].
first _ strm next.
isGZip _ (strm next * 256 + first) = (GZipConstants gzipMagic).
strm skip: -2.
isGZip
ifTrue: [outputStream _ (MultiByteBinaryOrTextStream with:
(GZipReadStream on: strm) upToEnd) reset.
strm close]
ifFalse: [outputStream _ strm].
^ outputStream!
----- Method: ReadWriteStream>>close (in category 'file status') -----
close
"Presumably sets the status of the receiver to be closed. This message does
nothing at this level, but is included for FileStream compatibility."
^self!
----- Method: ReadWriteStream>>closed (in category 'file status') -----
closed
"If you have close (for FileStream compatibility), you must respond to closed. The result in nonsense here. TK 29 May 96"
^ false!
----- Method: ReadWriteStream>>contents (in category 'accessing') -----
contents
"Answer with a copy of my collection from 1 to readLimit."
readLimit _ readLimit max: position.
^collection copyFrom: 1 to: readLimit!
----- Method: ReadWriteStream>>fileInObjectAndCode (in category 'fileIn/Out') -----
fileInObjectAndCode
"This file may contain:
1) a fileIn of code
2) just an object in SmartReferenceStream format
3) both code and an object.
File it in and return the object. Note that self must be a FileStream or RWBinaryOrTextStream. Maybe ReadWriteStream incorporate RWBinaryOrTextStream?"
| refStream object |
self text.
self peek asciiValue = 4
ifTrue: [ "pure object file"
refStream _ SmartRefStream on: self.
object _ refStream nextAndClose]
ifFalse: [ "objects mixed with a fileIn"
self fileIn. "reads code and objects, then closes the file"
object _ SmartRefStream scannedObject]. "set by side effect of one of the chunks"
SmartRefStream scannedObject: nil. "clear scannedObject"
^ object!
----- Method: ReadWriteStream>>fileNameEndsWith: (in category 'fileIn/Out') -----
fileNameEndsWith: aString
"See comment in FileStream fileNameEndsWith:"
^false!
----- Method: ReadWriteStream>>fileOutChangeSet:andObject: (in category 'fileIn/Out') -----
fileOutChangeSet: aChangeSetOrNil andObject: theObject
^ self fileOutChangeSet: aChangeSetOrNil andObject: theObject withVersionNotification: false.
!
----- Method: ReadWriteStream>>fileOutChangeSet:andObject:withVersionNotification: (in category 'fileIn/Out') -----
fileOutChangeSet: aChangeSetOrNil andObject: theObject withVersionNotification: withNotification
"Write a file that has both the source code for the named class and an object as bits. Any instance-specific object will get its class written automatically."
"An experimental version to fileout a changeSet first so that a project can contain its own classes"
self setFileTypeToObject.
"Type and Creator not to be text, so can attach correctly to an email msg"
self header; timeStamp.
(withNotification or: [aChangeSetOrNil notNil]) ifTrue: [
withNotification ifTrue: [
self fileOutVersionCheckNotification.
].
aChangeSetOrNil ifNotNil: [
aChangeSetOrNil fileOutPreambleOn: self.
aChangeSetOrNil fileOutOn: self.
aChangeSetOrNil fileOutPostscriptOn: self.
].
].
self trailer. "Does nothing for normal files. HTML streams will have trouble with object data"
"Append the object's raw data"
(SmartRefStream on: self)
nextPut: theObject; "and all subobjects"
close. "also closes me"
!
----- Method: ReadWriteStream>>fileOutChanges (in category 'fileIn/Out') -----
fileOutChanges
"Append to the receiver a description of all class changes."
Cursor write showWhile:
[self header; timeStamp.
ChangeSet current fileOutOn: self.
self trailer; close]!
----- Method: ReadWriteStream>>fileOutClass:andObject: (in category 'fileIn/Out') -----
fileOutClass: extraClass andObject: theObject
"Write a file that has both the source code for the named class and an object as bits. Any instance-specific object will get its class written automatically."
| class srefStream |
self setFileTypeToObject.
"Type and Creator not to be text, so can attach correctly to an email msg"
self text.
self header; timeStamp.
extraClass ifNotNil: [
class _ extraClass. "A specific class the user wants written"
class sharedPools size > 0 ifTrue:
[class shouldFileOutPools
ifTrue: [class fileOutSharedPoolsOn: self]].
class fileOutOn: self moveSource: false toFile: 0].
self trailer. "Does nothing for normal files. HTML streams will have trouble with object data"
self binary.
"Append the object's raw data"
srefStream _ SmartRefStream on: self.
srefStream nextPut: theObject. "and all subobjects"
srefStream close. "also closes me"
!
----- Method: ReadWriteStream>>fileOutClass:andObject:blocking: (in category 'fileIn/Out') -----
fileOutClass: extraClass andObject: theObject blocking: anIdentDict
"Write a file that has both the source code for the named class and an object as bits. Any instance-specific object will get its class written automatically. Accept a list of objects to map to nil or some other object (blockers). In addition to teh choices in each class's objectToStoreOnDataStream"
| class srefStream |
self setFileTypeToObject.
"Type and Creator not to be text, so can attach correctly to an email msg"
self header; timeStamp.
extraClass ifNotNil: [
class _ extraClass. "A specific class the user wants written"
class sharedPools size > 0 ifTrue:
[class shouldFileOutPools
ifTrue: [class fileOutSharedPoolsOn: self]].
class fileOutOn: self moveSource: false toFile: 0].
self trailer. "Does nothing for normal files. HTML streams will have trouble with object data"
"Append the object's raw data"
srefStream _ SmartRefStream on: self.
srefStream blockers: anIdentDict.
srefStream nextPut: theObject. "and all subobjects"
srefStream close. "also closes me"
!
----- Method: ReadWriteStream>>fileOutVersionCheckNotification (in category 'fileIn/Out') -----
fileOutVersionCheckNotification
"Put a version-check bumper onto the project stream."
self nextChunkPut: ' | cont | (Smalltalk includesKey: #MorphExtensionPlus) ifFalse: [self inform: ''This project cannot be loaded into an older system.\Please use an OLPC Etoys compatible image.'' translated withCRs.
cont _ thisContext.
[cont notNil] whileTrue: [
cont selector == #handleEvent: ifTrue: [cont return: nil].
cont _ cont sender.
]]'; cr.
self nextChunkPut: ' | cont | (Smalltalk includesKey: #CalendarMorph) ifFalse:
[(self confirm: ''This project was created from a more recent\version of Etoys, and may not load or\work properly in an older system.\Ideally use Etoys 5.0 or newer\proceed anyway?'' translated withCRs) ifFalse:
[cont _ thisContext.
[cont notNil] whileTrue: [
cont selector == #handleEvent: ifTrue: [cont return: nil].
cont _ cont sender.
]]]'; cr.
!
----- Method: ReadWriteStream>>hash (in category 'testing') -----
hash
self class == ReadWriteStream ifFalse: [^ super hash].
^ (self position + readLimit + 53) hash!
----- Method: ReadWriteStream>>isZipArchive (in category 'testing') -----
isZipArchive
"Determine if this appears to be a valid Zip archive"
| sig |
self binary.
sig _ self next: 4.
self position: self position - 4. "rewind"
^ZipArchive validSignatures includes: sig!
----- Method: ReadWriteStream>>name (in category 'accessing') -----
name
^ 'a stream' translated "for fileIn compatibility"!
----- Method: ReadWriteStream>>next (in category 'accessing') -----
next
"Primitive. Return the next object in the Stream represented by the
receiver. Fail if the collection of this stream is not an Array or a String.
Fail if the stream is positioned at its end, or if the position is out of
bounds in the collection. Optional. See Object documentation
whatIsAPrimitive."
<primitive: 65>
"treat me as a FIFO"
position >= readLimit
ifTrue: [^nil]
ifFalse: [^collection at: (position _ position + 1)]!
----- Method: ReadWriteStream>>next: (in category 'accessing') -----
next: anInteger
"Answer the next anInteger elements of my collection. overriden for efficiency"
| ans endPosition |
readLimit := readLimit max: position.
endPosition _ position + anInteger min: readLimit.
ans _ collection copyFrom: position+1 to: endPosition.
position _ endPosition.
^ans
!
----- Method: ReadWriteStream>>readStream (in category 'converting') -----
readStream
"polymorphic with SequenceableCollection. Return self"
^ self!
ReadWriteStream subclass: #Transcripter
instanceVariableNames: 'frame para'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
!Transcripter commentStamp: '<historical>' prior: 0!
Transcripter is a dog-simple scrolling stream with display. It is intended to operate with no support from MVC or color in a minimal, or headless version of Squeak. No attention has been paid to appearance or performance.!
----- Method: Transcripter class>>emergencyEvaluator (in category 'utilities') -----
emergencyEvaluator
(Transcripter newInFrame: (0 at 0 corner: 320 at 200))
show: 'Type ''revert'' to revert your last method change.
Type ''exit'' to exit the emergency evaluator.';
readEvalPrint!
----- Method: Transcripter class>>newInFrame: (in category 'instance creation') -----
newInFrame: frame
"
(Transcripter newInFrame: (0 at 0 extent: 100 at 200))
nextPutAll: 'Hello there'; endEntry;
cr; print: 355.0/113; endEntry;
readEvalPrint.
"
| transcript |
transcript _ self on: (String new: 100).
transcript initInFrame: frame.
^ transcript clear!
----- Method: Transcripter class>>startTranscriptProcess (in category 'instance creation') -----
startTranscriptProcess "Transcripter startTranscriptProcess"
| activeProcess |
Transcript _ self newInFrame: Display boundingBox.
activeProcess _ [Transcript readEvalPrint.
Smalltalk processShutDownList: true; quitPrimitive]
newProcess
priority: Processor userSchedulingPriority.
activeProcess resume.
Processor terminateActive
!
----- Method: Transcripter>>black (in category 'private') -----
black
Display depth = 1 ifTrue: [^ Bitmap with: 16rFFFFFFFF "Works without color support"].
^ Color black!
----- Method: Transcripter>>clear (in category 'accessing') -----
clear
Display fill: (frame insetBy: -2) fillColor: self black;
fill: frame fillColor: self white.
self on: (String new: 100); endEntry!
----- Method: Transcripter>>confirm: (in category 'command line') -----
confirm: queryString
| choice |
[true]
whileTrue:
[choice _ self request: queryString , '
Please type yes or no followed by return'.
choice first asUppercase = $Y ifTrue: [^ true].
choice first asUppercase = $N ifTrue: [^ false]]!
----- Method: Transcripter>>endEntry (in category 'accessing') -----
endEntry
| c d cb |
c _ self contents.
Display extent ~= DisplayScreen actualScreenSize ifTrue:
["Handle case of user resizing physical window"
DisplayScreen startUp.
frame _ frame intersect: Display boundingBox.
^ self clear; show: c].
para setWithText: c asText
style: TextStyle default
compositionRectangle: ((frame insetBy: 4) withHeight: 9999)
clippingRectangle: frame
foreColor: self black backColor: self white.
d _ para compositionRectangle bottom - frame bottom.
d > 0 ifTrue:
["Scroll up to keep all contents visible"
cb _ para characterBlockAtPoint: para compositionRectangle topLeft
+ (0@(d+para lineGrid)).
self on: (c copyFrom: cb stringIndex to: c size).
readLimit_ position_ collection size.
^ self endEntry].
para display!
----- Method: Transcripter>>initInFrame: (in category 'initialization') -----
initInFrame: rect
frame _ rect insetBy: 2. "Leave room for border"
para _ Paragraph withText: self contents asText
style: TextStyle default
compositionRectangle: ((frame insetBy: 4) withHeight: 9999)
clippingRectangle: frame
foreColor: self black backColor: self white!
----- Method: Transcripter>>readEvalPrint (in category 'command line') -----
readEvalPrint
| line okToRevert |
okToRevert _ true.
[#('quit' 'exit' 'done' ) includes: (line _ self request: '>')]
whileFalse:
[line = 'revert'
ifTrue: [okToRevert
ifTrue: [Utilities revertLastMethodSubmission.
self cr; show: 'reverted: ' , Utilities mostRecentlySubmittedMessage.
okToRevert _ false]
ifFalse: [self cr; show: 'Only one level of revert currently supported']]
ifFalse: [self cr; show: ([Compiler evaluate: line] ifError: [:err :ex | err])]]!
----- Method: Transcripter>>request: (in category 'command line') -----
request: prompt
| startPos char contents |
self cr; show: prompt.
startPos _ position.
[[Sensor keyboardPressed] whileFalse.
(char _ Sensor keyboard) = Character cr]
whileFalse:
[char = Character backspace
ifTrue: [readLimit _ position _ (position - 1 max: startPos)]
ifFalse: [self nextPut: char].
self endEntry].
contents _ self contents.
^ contents copyFrom: startPos + 1 to: contents size!
----- Method: Transcripter>>show: (in category 'accessing') -----
show: anObject
self nextPutAll: anObject asString; endEntry!
----- Method: Transcripter>>white (in category 'private') -----
white
Display depth = 1 ifTrue: [^ Bitmap with: 0 "Works without color support"].
^ Color white!
WriteStream subclass: #TextStream
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'!
----- Method: TextStream>>applyAttribute:beginningAt: (in category 'as yet unclassified') -----
applyAttribute: att beginningAt: startPos
collection addAttribute: att from: startPos to: self position!
----- Method: TextStream>>nextPutAll: (in category 'as yet unclassified') -----
nextPutAll: aCollection
"Optimized access to get around Text at:Put: overhead"
| n |
n _ aCollection size.
position + n > writeLimit
ifTrue:
[self growTo: position + n + 10].
collection
replaceFrom: position+1
to: position + n
with: aCollection
startingAt: 1.
position _ position + n!
----- Method: TextStream>>withAttribute:do: (in category 'as yet unclassified') -----
withAttribute: att do: strmBlock
| pos1 val |
pos1 _ self position.
val _ strmBlock value.
collection addAttribute: att from: pos1+1 to: self position.
^ val!
----- Method: TextStream>>withAttributes:do: (in category 'as yet unclassified') -----
withAttributes: attributes do: streamBlock
| pos1 val |
pos1 _ self position.
val _ streamBlock value.
attributes do: [:attribute |
collection
addAttribute: attribute
from: pos1 + 1
to: self position].
^ val!
WriteStream subclass: #TranscriptStream
instanceVariableNames: ''
classVariableNames: 'AccessSema'
poolDictionaries: ''
category: 'Collections-Streams'!
!TranscriptStream commentStamp: '<historical>' prior: 0!
This class is a much simpler implementation of Transcript protocol that supports multiple views and very simple conversion to morphic. Because it inherits from Stream, it is automatically compatible with code that is designe to write to streams.!
----- Method: TranscriptStream class>>initialize (in category 'class initialization') -----
initialize
self registerInFlapsRegistry. !
----- Method: TranscriptStream class>>new (in category 'as yet unclassified') -----
new
^ self on: (String new: 1000)
"
INSTALLING:
TextCollector allInstances do:
[:t | t breakDependents.
t become: TranscriptStream new].
TESTING: (Execute this text in a workspace)
Do this first...
tt _ TranscriptStream new.
tt openLabel: 'Transcript test 1'.
Then this will open a second view -- ooooh...
tt openLabel: 'Transcript test 2'.
And finally make them do something...
tt clear.
[Sensor anyButtonPressed] whileFalse:
[1 to: 20 do: [:i | tt print: (2 raisedTo: i-1); cr; endEntry]].
"!
----- Method: TranscriptStream class>>newTranscript: (in category 'as yet unclassified') -----
newTranscript: aTextCollector
"Store aTextCollector as the value of the system global Transcript."
Smalltalk at: #Transcript put: aTextCollector!
----- Method: TranscriptStream class>>openMorphicTranscript (in category 'as yet unclassified') -----
openMorphicTranscript
"Have the current project's transcript open up as a morph"
^ Transcript openAsMorph!
----- Method: TranscriptStream class>>registerInFlapsRegistry (in category 'class initialization') -----
registerInFlapsRegistry
"Register the receiver in the system's flaps registry"
self environment
at: #Flaps
ifPresent: [:cl | cl registerQuad: {#TranscriptStream. #openMorphicTranscript. 'Transcript' translatedNoop. 'A Transcript is a window usable for logging and debugging; browse references to #Transcript for examples of how to write to it.' translatedNoop}
forFlapNamed: 'Tools']
!
----- Method: TranscriptStream class>>unload (in category 'class initialization') -----
unload
"Unload the receiver from global registries"
self environment at: #Flaps ifPresent: [:cl |
cl unregisterQuadsWithReceiver: self] !
----- Method: TranscriptStream class>>windowColorSpecification (in category 'window color') -----
windowColorSpecification
"Answer a WindowColorSpec object that declares my preference"
^ WindowColorSpec classSymbol: self name wording: 'Transcript' translatedNoop brightColor: #lightOrange pastelColor: #paleOrange helpMessage: 'The system transcript' translatedNoop!
----- Method: TranscriptStream>>bs (in category 'stream extensions') -----
bs
self position > 0 ifTrue: [^ self skip: -1].
self changed: #bs!
----- Method: TranscriptStream>>characterLimit (in category 'access') -----
characterLimit
"Tell the views how much to retain on screen"
^ 20000!
----- Method: TranscriptStream>>clear (in category 'stream extensions') -----
clear
"Clear all characters and redisplay the view"
self changed: #clearText.
self reset!
----- Method: TranscriptStream>>closeAllViews (in category 'initialization') -----
closeAllViews
"Transcript closeAllViews"
self dependents do:
[:d |
(d isKindOf: PluggableTextView)
ifTrue: [d topView controller closeAndUnscheduleNoTerminate].
(d isSystemWindow) ifTrue: [d delete]]!
----- Method: TranscriptStream>>codePaneMenu:shifted: (in category 'model protocol') -----
codePaneMenu: aMenu shifted: shifted
"Note that unless we override perform:orSendTo:, PluggableTextController will respond to all menu items"
^ StringHolder basicNew codePaneMenu: aMenu shifted: shifted
!
----- Method: TranscriptStream>>countOpenTranscripts (in category 'private') -----
countOpenTranscripts
"Transcript countOpenTranscripts"
^ (self dependents select: [:e | e isTextView]) size
!
----- Method: TranscriptStream>>endEntry (in category 'stream extensions') -----
endEntry
"Display all the characters since the last endEntry, and reset the stream"
self semaphore critical:[
self changed: #appendEntry.
self reset.
].!
----- Method: TranscriptStream>>flush (in category 'stream extensions') -----
flush
self endEntry!
----- Method: TranscriptStream>>open (in category 'initialization') -----
open
| openCount |
openCount _ 0.
self dependents do:
[:d | ((d isKindOf: PluggableTextView) or:
[d isKindOf: PluggableTextMorph]) ifTrue: [openCount _ openCount + 1]].
openCount = 0
ifTrue: [self openLabel: 'Transcript']
ifFalse: [self openLabel: 'Transcript #' , (openCount+1) printString]!
----- Method: TranscriptStream>>openAsMorph (in category 'initialization') -----
openAsMorph
"Answer a morph viewing this transcriptStream"
^ (self openAsMorphLabel: 'Transcript') applyModelExtent!
----- Method: TranscriptStream>>openAsMorphLabel: (in category 'initialization') -----
openAsMorphLabel: labelString
"Build a morph viewing this transcriptStream"
| window |
window _ (SystemWindow labelled: labelString) model: self.
window addMorph: (PluggableTextMorph on: self text: nil accept: nil
readSelection: nil menu: #codePaneMenu:shifted:)
frame: (0 at 0 corner: 1 at 1).
^ window!
----- Method: TranscriptStream>>openLabel: (in category 'initialization') -----
openLabel: aString
"Open a window on this transcriptStream"
| topView codeView |
Smalltalk isMorphic ifTrue: [^ (self openAsMorphLabel: aString) openInWorld].
topView _ (StandardSystemView new) model: self.
topView borderWidth: 1.
topView label: aString.
topView minimumSize: 100 @ 50.
codeView _ PluggableTextView on: self text: nil accept: nil
readSelection: nil menu: #codePaneMenu:shifted:.
codeView window: (0 at 0 extent: 200 at 200).
topView addSubView: codeView.
topView controller open!
----- Method: TranscriptStream>>pastEndPut: (in category 'stream extensions') -----
pastEndPut: anObject
"If the stream reaches its limit, just output the contents and reset."
self endEntry.
^ self nextPut: anObject!
----- Method: TranscriptStream>>perform:orSendTo: (in category 'model protocol') -----
perform: selector orSendTo: otherTarget
"Selector was just chosen from a menu by a user. If can respond, then
perform it on myself. If not, send it to otherTarget, presumably the
editPane from which the menu was invoked."
(self respondsTo: selector)
ifTrue: [^ self perform: selector]
ifFalse: [^ otherTarget perform: selector]!
----- Method: TranscriptStream>>release (in category 'model protocol') -----
release
self dependents do:
[:view | (view isMorph and: [view isInWorld not])
ifTrue: [self removeDependent: view]]!
----- Method: TranscriptStream>>semaphore (in category 'private') -----
semaphore
^AccessSema ifNil:[AccessSema _ Semaphore forMutualExclusion]!
----- Method: TranscriptStream>>show: (in category 'stream extensions') -----
show: anObject "TextCollector compatibility"
self nextPutAll: anObject asString; endEntry!
----- Method: TranscriptStream>>step (in category 'model protocol') -----
step
"Objects that may be models of SystemWindows need to respond to this, albeit vacuously"!
----- Method: WriteStream class>>on:from:to: (in category 'instance creation') -----
on: aCollection from: firstIndex to: lastIndex
"Answer an instance of me on a copy of the argument, aCollection,
determined by the indices firstIndex and lastIndex. Position the instance
at the beginning of the collection."
^self basicNew
on: aCollection
from: firstIndex
to: lastIndex!
----- Method: WriteStream class>>with: (in category 'instance creation') -----
with: aCollection
"Answer an instance of me on the argument, aCollection, positioned to
store objects at the end of aCollection."
^self basicNew with: aCollection!
----- Method: WriteStream class>>with:from:to: (in category 'instance creation') -----
with: aCollection from: firstIndex to: lastIndex
"Answer an instance of me on the subcollection of the argument,
aCollection, determined by the indices firstIndex and lastIndex. Position
the instance to store at the end of the subcollection."
^self basicNew with: (aCollection copyFrom: firstIndex to: lastIndex)!
----- Method: WriteStream>>braceArray (in category 'private') -----
braceArray
"This method is used in compilation of brace constructs.
It MUST NOT be deleted or altered."
^ collection!
----- Method: WriteStream>>braceArray: (in category 'private') -----
braceArray: anArray
"This method is used in compilation of brace constructs.
It MUST NOT be deleted or altered."
collection _ anArray.
position _ 0.
readLimit _ 0.
writeLimit _ anArray size.!
----- Method: WriteStream>>contents (in category 'accessing') -----
contents
readLimit _ readLimit max: position.
^collection copyFrom: 1 to: position!
----- Method: WriteStream>>cr (in category 'character writing') -----
cr
"Append a return character to the receiver."
self nextPut: Character cr!
----- Method: WriteStream>>crtab (in category 'character writing') -----
crtab
"Append a return character, followed by a single tab character, to the
receiver."
self nextPut: Character cr.
self nextPut: Character tab!
----- Method: WriteStream>>crtab: (in category 'character writing') -----
crtab: anInteger
"Append a return character, followed by anInteger tab characters, to the
receiver."
self nextPut: Character cr.
anInteger timesRepeat: [self nextPut: Character tab]!
----- Method: WriteStream>>ensureASpace (in category 'character writing') -----
ensureASpace
"Append a space character to the receiver IFF there is not one on the end."
(position > 0 and: [(collection at: position) = Character space]) ifTrue: [^self].
self nextPut: Character space!
----- Method: WriteStream>>ensureNoSpace (in category 'character writing') -----
ensureNoSpace
"If there is not one on the end, remove it."
(position > 0 and: [(collection at: position) = Character space])
ifTrue: [self skip: -1].!
----- Method: WriteStream>>flush (in category 'file open/close') -----
flush!
----- Method: WriteStream>>growTo: (in category 'private') -----
growTo: anInteger
" anInteger is the required minimal new size of the collection "
| oldSize grownCollection newSize |
oldSize _ collection size.
newSize := anInteger + (oldSize // 4 max: 20).
grownCollection _ collection class new: newSize.
collection _ grownCollection replaceFrom: 1 to: oldSize with: collection startingAt: 1.
writeLimit _ collection size.
!
----- Method: WriteStream>>next (in category 'accessing') -----
next
self shouldNotImplement!
----- Method: WriteStream>>next:putAll:startingAt: (in category 'accessing') -----
next: anInteger putAll: aCollection startingAt: startIndex
"Store the next anInteger elements from the given collection."
| newEnd numPut |
collection class == aCollection class ifFalse:
[^ super next: anInteger putAll: aCollection startingAt: startIndex ].
numPut _ anInteger min: (aCollection size - startIndex + 1).
newEnd _ position + numPut.
newEnd > writeLimit ifTrue:
[^ super next: anInteger putAll: aCollection startingAt: startIndex "Trigger normal pastEndPut: logic"].
collection replaceFrom: position+1 to: newEnd with: aCollection startingAt: startIndex.
position _ newEnd.
!
----- Method: WriteStream>>nextChunkPut: (in category 'fileIn/Out') -----
nextChunkPut: aString
"Append the argument, aString, to the receiver, doubling embedded terminators."
| i remainder terminator |
terminator _ $!!.
remainder _ aString.
[(i _ remainder indexOf: terminator) = 0] whileFalse:
[self nextPutAll: (remainder copyFrom: 1 to: i).
self nextPut: terminator. "double imbedded terminators"
remainder _ remainder copyFrom: i+1 to: remainder size].
self nextPutAll: remainder.
aString includesUnifiedCharacter ifTrue: [
self nextPut: terminator.
self nextPutAll: ']lang['.
aString writeLeadingCharRunsOn: self.
].
self nextPut: terminator.
!
----- Method: WriteStream>>nextChunkPutWithStyle: (in category 'fileIn/Out') -----
nextChunkPutWithStyle: aStringOrText
"Append the argument, aText, to the receiver, doubling embedded terminators. Put out one chunk for the string and one for the style runs. Mark the style with ]style[."
aStringOrText isString ifTrue: [^ self nextChunkPut: aStringOrText].
aStringOrText runs coalesce.
aStringOrText unembellished ifTrue: [^ self nextChunkPut: aStringOrText asString].
self nextChunkPut: aStringOrText asString.
self cr; nextPutAll: ']style['.
self nextChunkPut:
(String streamContents: [:strm |
aStringOrText runs writeScanOn: strm]).
!
----- Method: WriteStream>>nextPut: (in category 'accessing') -----
nextPut: anObject
"Primitive. Insert the argument at the next position in the Stream
represented by the receiver. Fail if the collection of this stream is not an
Array or a String. Fail if the stream is positioned at its end, or if the
position is out of bounds in the collection. Fail if the argument is not
of the right type for the collection. Optional. See Object documentation
whatIsAPrimitive."
<primitive: 66>
((collection class == ByteString) and: [
anObject isCharacter and:[anObject isOctetCharacter not]]) ifTrue: [
collection _ (WideString from: collection).
^self nextPut: anObject.
].
position >= writeLimit
ifTrue: [^ self pastEndPut: anObject]
ifFalse:
[position _ position + 1.
^collection at: position put: anObject]!
----- Method: WriteStream>>nextPutAll: (in category 'accessing') -----
nextPutAll: aCollection
| newEnd |
collection class == aCollection class ifFalse:
[^ super nextPutAll: aCollection ].
newEnd _ position + aCollection size.
newEnd > writeLimit ifTrue:
[self growTo: newEnd + 10].
collection replaceFrom: position+1 to: newEnd with: aCollection startingAt: 1.
position _ newEnd.!
----- Method: WriteStream>>nextPutKeyword:withArg: (in category 'character writing') -----
nextPutKeyword: keyword withArg: argValue
"Emit a keyword/value pair in the alternate syntax"
self nextPutAll: (keyword copyWithout: $:);
nextPut: $(;
store: argValue;
nextPut: $)!
----- Method: WriteStream>>on: (in category 'private') -----
on: aCollection
super on: aCollection.
readLimit _ 0.
writeLimit _ aCollection size!
----- Method: WriteStream>>on:from:to: (in category 'private') -----
on: aCollection from: firstIndex to: lastIndex
| len |
collection _ aCollection.
readLimit _
writeLimit _ lastIndex > (len _ collection size)
ifTrue: [len]
ifFalse: [lastIndex].
position _ firstIndex <= 1
ifTrue: [0]
ifFalse: [firstIndex - 1]!
----- Method: WriteStream>>pastEndPut: (in category 'private') -----
pastEndPut: anObject
"Grow the collection by creating a new bigger collection and then
copy over the contents from the old one. We grow by doubling the size
but the growth is kept between 20 and 1000000.
Finally we put <anObject> at the current write position."
| oldSize grownCollection |
oldSize _ collection size.
grownCollection _ collection class new: oldSize + ((oldSize max: 20) min: 1000000).
collection _ grownCollection replaceFrom: 1 to: oldSize with: collection startingAt: 1.
writeLimit _ collection size.
collection at: (position _ position + 1) put: anObject!
----- Method: WriteStream>>peekLast (in category 'character writing') -----
peekLast
"Return that item just put at the end of the stream"
^ position > 0
ifTrue: [collection at: position]
ifFalse: [nil]!
----- Method: WriteStream>>position: (in category 'positioning') -----
position: anInteger
"Refer to the comment in PositionableStream|position:."
readLimit _ readLimit max: position.
super position: anInteger!
----- Method: WriteStream>>reset (in category 'positioning') -----
reset
"Refer to the comment in PositionableStream|reset."
readLimit _ readLimit max: position.
position _ 0!
----- Method: WriteStream>>resetToStart (in category 'positioning') -----
resetToStart
readLimit _ position _ 0.!
----- Method: WriteStream>>setToEnd (in category 'positioning') -----
setToEnd
"Refer to the comment in PositionableStream|setToEnd."
readLimit _ readLimit max: position.
super setToEnd.!
----- Method: WriteStream>>size (in category 'accessing') -----
size
^readLimit _ readLimit max: position!
----- Method: WriteStream>>space (in category 'character writing') -----
space
"Append a space character to the receiver."
self nextPut: Character space!
----- Method: WriteStream>>space: (in category 'character writing') -----
space: anInteger
"Append anInteger space characters to the receiver."
anInteger timesRepeat: [self space]!
----- Method: WriteStream>>store: (in category 'printing') -----
store: anObject
"Have anObject print on the receiver for purposes of rereading."
anObject storeOn: self!
----- Method: WriteStream>>tab (in category 'character writing') -----
tab
"Append a tab character to the receiver."
self nextPut: Character tab!
----- Method: WriteStream>>tab: (in category 'character writing') -----
tab: anInteger
"Append anInteger tab characters to the receiver."
anInteger timesRepeat: [self tab]!
----- Method: WriteStream>>timeStamp (in category 'fileIn/Out') -----
timeStamp
"Append the current time to the receiver as a String."
self nextChunkPut: "double string quotes and !!s"
(String streamContents: [:s | SmalltalkImage current timeStamp: s]) printString.
self cr!
----- Method: WriteStream>>with: (in category 'private') -----
with: aCollection
super on: aCollection.
position _ readLimit _ writeLimit _ aCollection size!
----- Method: WriteStream>>withAttribute:do: (in category 'private') -----
withAttribute: att do: strmBlock
"No-op here is overriden in TextStream for font emphasis"
^ strmBlock value!
----- Method: WriteStream>>withAttributes:do: (in category 'private') -----
withAttributes: attributes do: strmBlock
"No-op here is overriden in TextStream for font emphasis"
^ strmBlock value!
----- Method: Stream class>>new (in category 'instance creation') -----
new
self error: 'Streams are created with on: and with:'!
----- Method: Stream>>atEnd (in category 'testing') -----
atEnd
"Answer whether the receiver can access any more objects."
self subclassResponsibility!
----- Method: Stream>>basicNext (in category 'accessing') -----
basicNext
^ self next.
!
----- Method: Stream>>basicNextPut: (in category 'accessing') -----
basicNextPut: anObject
^ self nextPut: anObject!
----- Method: Stream>>basicNextPutAll: (in category 'accessing') -----
basicNextPutAll: aCollection
^ self nextPutAll: aCollection.
!
----- Method: Stream>>binary (in category 'accessing') -----
binary!
----- Method: Stream>>close (in category 'file open/close') -----
close!
----- Method: Stream>>closed (in category 'testing') -----
closed
^ false!
----- Method: Stream>>contents (in category 'accessing') -----
contents
"Answer all of the contents of the receiver."
self subclassResponsibility!
----- Method: Stream>>dialect (in category 'alternate syntax') -----
dialect
^#ST80 "in case a regular stream is used to print parse nodes"!
----- Method: Stream>>do: (in category 'enumerating') -----
do: aBlock
"Evaluate aBlock for each of the objects accessible by receiver."
[self atEnd]
whileFalse: [aBlock value: self next]!
----- Method: Stream>>flush (in category 'accessing') -----
flush
"Do nothing by default"!
----- Method: Stream>>isStream (in category 'testing') -----
isStream
"Return true if the receiver responds to the stream protocol"
^true!
----- Method: Stream>>isTypeHTTP (in category 'testing') -----
isTypeHTTP
^false!
----- Method: Stream>>localName (in category 'accessing') -----
localName
^'a stream'!
----- Method: Stream>>next (in category 'accessing') -----
next
"Answer the next object accessible by the receiver."
self subclassResponsibility!
----- Method: Stream>>next: (in category 'accessing') -----
next: anInteger
"Answer the next anInteger number of objects accessible by the receiver."
| aCollection |
aCollection _ OrderedCollection new.
anInteger timesRepeat: [aCollection addLast: self next].
^aCollection!
----- Method: Stream>>next:put: (in category 'accessing') -----
next: anInteger put: anObject
"Make anObject be the next anInteger number of objects accessible by the
receiver. Answer anObject."
anInteger timesRepeat: [self nextPut: anObject].
^anObject!
----- Method: Stream>>nextMatchAll: (in category 'accessing') -----
nextMatchAll: aColl
"Answer true if next N objects are the ones in aColl,
else false. Advance stream of true, leave as was if false."
| save |
save _ self position.
aColl do: [:each |
(self next) = each ifFalse: [
self position: save.
^ false]
].
^ true!
----- Method: Stream>>nextMatchFor: (in category 'accessing') -----
nextMatchFor: anObject
"Gobble the next object and answer whether it is equal to the argument,
anObject."
^anObject = self next!
----- Method: Stream>>nextPut: (in category 'accessing') -----
nextPut: anObject
"Insert the argument, anObject, as the next object accessible by the
receiver. Answer anObject."
self subclassResponsibility!
----- Method: Stream>>nextPutAll: (in category 'accessing') -----
nextPutAll: aCollection
"Append the elements of aCollection to the sequence of objects accessible
by the receiver. Answer aCollection."
aCollection do: [:v | self nextPut: v].
^aCollection!
----- Method: Stream>>nextWordsPutAll: (in category 'testing') -----
nextWordsPutAll: aCollection
"Write the argument a word-like object in big endian format on the receiver.
May be used to write other than plain word-like objects (such as ColorArray)."
aCollection class isPointers | aCollection class isWords not
ifTrue: [^self error: aCollection class name,' is not word-like'].
1 to: aCollection basicSize do:[:i|
self nextNumber: 4 put: (aCollection basicAt: i).
].
^aCollection!
----- Method: Stream>>openReadOnly (in category 'accessing') -----
openReadOnly
^self!
----- Method: Stream>>print: (in category 'printing') -----
print: anObject
"Have anObject print itself on the receiver."
anObject printOn: self!
----- Method: Stream>>printHtml: (in category 'printing') -----
printHtml: anObject
anObject printHtmlOn: self!
----- Method: Stream>>printOn: (in category 'accessing') -----
printOn: stream
super printOn: stream.
stream space.
self contents printOn: stream.
!
----- Method: Stream>>readOnly (in category 'accessing') -----
readOnly
^self!
----- Method: Stream>>sleep (in category 'as yet unclassified') -----
sleep
"an FTP-based stream might close the connection here"!
----- Method: Stream>>upToEnd (in category 'accessing') -----
upToEnd
"answer the remaining elements in the string"
| elements |
elements _ OrderedCollection new.
[ self atEnd ] whileFalse: [
elements add: self next ].
^elements!
----- Method: Stream>>withStyleFor:do: (in category 'alternate syntax') -----
withStyleFor: elementType do: aBlock
^aBlock value "in case a regular stream is used to print parse nodes"
">>
(Compiler new compile: 'blah ^self' in: String notifying: nil ifFail: []) printString
<<"!
----- Method: Stream>>write: (in category 'filter streaming') -----
write:encodedObject
^encodedObject putOn:self.
!
Object subclass: #TextAttribute
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
!TextAttribute commentStamp: 'tk 7/22/2002 18:33' prior: 0!
Tells a piece of text to be a certain way.
Select text, press Command-6, choose a attribute. If selected text is of the form
Hi There<Smalltalk beep>
the part in angle brackets is saved for action, and the Hi There appears in the paragraph. If selection has no angle brackets, use the whole thing as both the text and the action.
TextDoIt -- eval as a Smalltalk expression (the part in angle brackets)
TextLink -- Show a method, class comment, class hierarchy, or class defintion.
<Point extent:>, <Point Comment>, <Point Hierarchy>, or <Point Defintion> are what you type.
TextURL -- Show the web page. <www.disney.com>
These attributes of text need to be stored on the disk in a regular file-out. It is done in this form: Hi There
in the text, and a Run containing dSmalltalk beep;;
Click here to see the extent:
in the text, and a Run containing method LPoint extent:;
See RunArray class scanFrom: where decoding is done.
!
TextAttribute subclass: #TextAction
instanceVariableNames: ''
classVariableNames: 'Purple'
poolDictionaries: ''
category: 'Collections-Text'!
TextAction subclass: #PluggableTextAttribute
instanceVariableNames: 'evalBlock'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
!PluggableTextAttribute commentStamp: '<historical>' prior: 0!
An attribute which evaluates an arbitrary block when it is selected.!
----- Method: PluggableTextAttribute class>>evalBlock: (in category 'instance creation') -----
evalBlock: aBlock
^super new evalBlock: aBlock!
----- Method: PluggableTextAttribute>>actOnClickFor: (in category 'clicking') -----
actOnClickFor: anObject
evalBlock ifNil: [ ^self ].
evalBlock numArgs = 0 ifTrue: [ evalBlock value. ^true ].
evalBlock numArgs = 1 ifTrue: [ evalBlock value: anObject. ^true ].
self error: 'evalBlock should have 0 or 1 arguments'!
----- Method: PluggableTextAttribute>>evalBlock: (in category 'initialization') -----
evalBlock: aBlock
evalBlock := aBlock!
----- Method: TextAction class>>initialize (in category 'as yet unclassified') -----
initialize "TextAction initialize"
Purple _ Color r: 0.4 g: 0 b: 1.0!
----- Method: TextAction>>analyze: (in category 'as yet unclassified') -----
analyze: aString
"Analyze the selected text to find both the parameter to store and the text to emphesize (may be different from original selection). Does not return self!!. May be of the form:
3+4
<3+4>
Click Here<3+4>
<3+4>Click Here
"
"Obtain the showing text and the instructions"
| b1 b2 trim param show |
b1 _ aString indexOf: $<.
b2 _ aString indexOf: $>.
(b1 < b2) & (b1 > 0) ifFalse: ["only one part"
param _ self validate: aString.
^ Array with: param with: (param size = 0 ifTrue: [nil] ifFalse: [param])].
"Two parts"
trim _ aString withBlanksTrimmed.
(trim at: 1) == $<
ifTrue: [(trim last) == $>
ifTrue: ["only instructions"
param _ self validate: (aString copyFrom: b1+1 to: b2-1).
show _ param size = 0 ifTrue: [nil] ifFalse: [param]]
ifFalse: ["at the front"
param _ self validate: (aString copyFrom: b1+1 to: b2-1).
show _ param size = 0 ifTrue: [nil]
ifFalse: [aString copyFrom: b2+1 to: aString size]]]
ifFalse: [(trim last) == $>
ifTrue: ["at the end"
param _ self validate: (aString copyFrom: b1+1 to: b2-1).
show _ param size = 0 ifTrue: [nil]
ifFalse: [aString copyFrom: 1 to: b1-1]]
ifFalse: ["Illegal -- <> has text on both sides"
show _ nil]].
^ Array with: param with: show
!
----- Method: TextAction>>couldDeriveFromPrettyPrinting (in category 'as yet unclassified') -----
couldDeriveFromPrettyPrinting
^ false!
----- Method: TextAction>>dominatedByCmd0 (in category 'as yet unclassified') -----
dominatedByCmd0
"Cmd-0 should turn off active text"
^ true!
----- Method: TextAction>>emphasizeScanner: (in category 'as yet unclassified') -----
emphasizeScanner: scanner
"Set the emphasis for text display"
scanner textColor: Purple!
----- Method: TextAction>>info (in category 'as yet unclassified') -----
info
^ 'no hidden info'!
----- Method: TextAction>>mayActOnClick (in category 'as yet unclassified') -----
mayActOnClick
^ true!
----- Method: TextAction>>validate: (in category 'as yet unclassified') -----
validate: aString
"any format is OK with me"
^ aString!
TextAction subclass: #TextDoIt
instanceVariableNames: 'evalString'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
----- Method: TextDoIt class>>evalString: (in category 'as yet unclassified') -----
evalString: str
^ self new evalString: str!
----- Method: TextDoIt class>>scanFrom: (in category 'as yet unclassified') -----
scanFrom: strm
"read a doit in the funny format used by Text styles on files. d10 factorial;; end with two semicolons"
| pos end doit |
pos _ strm position.
[strm skipTo: $;. strm peek == $;] whileFalse.
end _ strm position - 1.
strm position: pos.
doit _ strm next: end-pos.
strm skip: 2. ";;"
^ self evalString: doit!
----- Method: TextDoIt>>actOnClickFor: (in category 'as yet unclassified') -----
actOnClickFor: anObject
"Note: evalString gets evaluated IN THE CONTEXT OF anObject
-- meaning that self and all instVars are accessible"
Compiler evaluate: evalString for: anObject logged: false.
^ true !
----- Method: TextDoIt>>analyze: (in category 'as yet unclassified') -----
analyze: aString
| list |
list _ super analyze: aString.
evalString _ list at: 1.
^ list at: 2!
----- Method: TextDoIt>>evalString: (in category 'as yet unclassified') -----
evalString: str
evalString _ str !
----- Method: TextDoIt>>info (in category 'as yet unclassified') -----
info
^ evalString!
----- Method: TextDoIt>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
strm nextPut: $d; nextPutAll: evalString; nextPutAll: ';;'!
TextDoIt subclass: #TextPrintIt
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
----- Method: TextPrintIt>>actOnClickFor:in:at:editor: (in category 'as yet unclassified') -----
actOnClickFor: anObject in: aParagraph at: clickPoint editor: editor
"Note: evalString gets evaluated IN THE CONTEXT OF anObject
-- meaning that self and all instVars are accessible"
| result range index |
result _ Compiler evaluate: evalString for: anObject logged: false.
result _ ' ', result printString,' '.
"figure out where the attribute ends in aParagraph"
index _ (aParagraph characterBlockAtPoint: clickPoint) stringIndex.
range _ aParagraph text rangeOf: self startingAt: index.
editor selectFrom: range last+1 to: range last.
editor zapSelectionWith: result.
editor selectFrom: range last to: range last + result size.
^ true !
----- Method: TextPrintIt>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
strm nextPut: $P; nextPutAll: evalString; nextPutAll: ';;'!
TextAction subclass: #TextLink
instanceVariableNames: 'classAndMethod'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
----- Method: TextLink class>>scanFrom: (in category 'as yet unclassified') -----
scanFrom: strm
"read a link in the funny format used by Text styles on files. LPoint +;LPoint Comment;"
^ self new classAndMethod: (strm upTo: $;)!
----- Method: TextLink>>actOnClickFor: (in category 'as yet unclassified') -----
actOnClickFor: aMessageSet
"Add to the end of the list. 'aClass selector', 'aClass Comment', 'aClass Definition', 'aClass Hierarchy' are the formats allowed."
aMessageSet addItem: classAndMethod.
^ true!
----- Method: TextLink>>analyze: (in category 'as yet unclassified') -----
analyze: aString
| list |
list _ super analyze: aString.
classAndMethod _ list at: 1.
^ list at: 2!
----- Method: TextLink>>analyze:with: (in category 'as yet unclassified') -----
analyze: aString with: nonMethod
"Initalize this attribute holder with a piece text the user typed into a paragraph. Returns the text to emphesize (may be different from selection) Does not return self!!. nonMethod is what to show when clicked, i.e. the last part of specifier (Comment, Definition, or Hierarchy). May be of the form:
Point
<Point>
Click Here<Point>
<Point>Click Here
"
"Obtain the showing text and the instructions"
| b1 b2 trim |
b1 _ aString indexOf: $<.
b2 _ aString indexOf: $>.
(b1 < b2) & (b1 > 0) ifFalse: ["only one part"
classAndMethod _ self validate: aString, ' ', nonMethod.
^ classAndMethod ifNotNil: [aString]].
"Two parts"
trim _ aString withBlanksTrimmed.
(trim at: 1) == $<
ifTrue: [(trim last) == $>
ifTrue: ["only instructions"
classAndMethod _ self validate: (aString copyFrom: b1+1 to: b2-1), ' ', nonMethod.
^ classAndMethod ifNotNil: [classAndMethod]]
ifFalse: ["at the front"
classAndMethod _ self validate: (aString copyFrom: b1+1 to: b2-1), ' ', nonMethod.
^ classAndMethod ifNotNil: [aString copyFrom: b2+1 to: aString size]]]
ifFalse: [(trim last) == $>
ifTrue: ["at the end"
classAndMethod _ self validate: (aString copyFrom: b1+1 to: b2-1), ' ', nonMethod.
^ classAndMethod ifNotNil: [aString copyFrom: 1 to: b1-1]]
ifFalse: ["Illegal -- <> has text on both sides"
^ nil]]
!
----- Method: TextLink>>classAndMethod: (in category 'as yet unclassified') -----
classAndMethod: aString
classAndMethod _ aString!
----- Method: TextLink>>info (in category 'as yet unclassified') -----
info
^ classAndMethod!
----- Method: TextLink>>validate: (in category 'as yet unclassified') -----
validate: specString
"Can this string be decoded to be Class space Method (or Comment, Definition, Hierarchy)? If so, return it in valid format, else nil"
| list first mid last |
list _ specString findTokens: ' .|'.
last _ list last.
last first isUppercase ifTrue: [
(#('Comment' 'Definition' 'Hierarchy') includes: last) ifFalse: [^ nil].
"Check for 'Rectangle Comment Comment' and remove last one"
(list at: list size - 1) = last ifTrue: [list _ list allButLast]].
list size > 3 ifTrue: [^ nil].
list size < 2 ifTrue: [^ nil].
Symbol hasInterned: list first ifTrue: [:sym | first _ sym].
first ifNil: [^ nil].
Smalltalk at: first ifAbsent: [^ nil].
mid _ list size = 3
ifTrue: [(list at: 2) = 'class' ifTrue: ['class '] ifFalse: [^ nil]]
ifFalse: [''].
"OK if method name is not interned -- may not be defined yet"
^ first, ' ', mid, last!
----- Method: TextLink>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
strm nextPut: $L; nextPutAll: classAndMethod; nextPut: $;!
TextAction subclass: #TextURL
instanceVariableNames: 'url'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
TextURL subclass: #TextSqkPageLink
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
!TextSqkPageLink commentStamp: '<historical>' prior: 0!
I represent a link to either a SqueakPage in a BookMorph, or a regular url. See TextMorphEditor changeEmphasis:.
!
----- Method: TextSqkPageLink>>actOnClickFor: (in category 'as yet unclassified') -----
actOnClickFor: textMorph
"I represent a link to either a SqueakPage in a BookMorph, or a regular url"
| book |
((url endsWith: '.bo') or: [url endsWith: '.sp']) ifFalse: [
^ super actOnClickFor: textMorph].
book _ textMorph ownerThatIsA: BookMorph.
book ifNotNil: [book goToPageUrl: url].
"later handle case of page being in another book, not this one"
^ true!
----- Method: TextSqkPageLink>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
strm nextPut: $q; nextPutAll: url; nextPut: $;!
TextURL subclass: #TextSqkProjectLink
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
----- Method: TextSqkProjectLink>>actOnClickFor: (in category 'as yet unclassified') -----
actOnClickFor: textMorph
Project enterIfThereOrFind: url.
^ true!
----- Method: TextSqkProjectLink>>analyze: (in category 'as yet unclassified') -----
analyze: aString
^url _ aString!
----- Method: TextSqkProjectLink>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
strm nextPut: $p; nextPutAll: url; nextPut: $;!
----- Method: TextURL class>>scanFrom: (in category 'as yet unclassified') -----
scanFrom: strm
"read a link in the funny format used by Text styles on files. Rhttp://www.disney.com;"
^ self new url: (strm upTo: $;)!
----- Method: TextURL>>actOnClickFor: (in category 'as yet unclassified') -----
actOnClickFor: anObject
"Do what you can with this URL. Later a web browser."
| response m |
(url beginsWith: 'sqPr://') ifTrue: [
ProjectLoading thumbnailFromUrl: (url copyFrom: 8 to: url size).
^self "should not get here, but what the heck"
].
"if it's a web browser, tell it to jump"
anObject isWebBrowser
ifTrue: [anObject jumpToUrl: url. ^ true]
ifFalse: [((anObject respondsTo: #model) and: [anObject model isWebBrowser])
ifTrue: [anObject model jumpToUrl: url. ^ true]].
"if it's a morph, see if it is contained in a web browser"
(anObject isKindOf: Morph) ifTrue: [
m _ anObject.
[ m ~= nil ] whileTrue: [
(m isWebBrowser) ifTrue: [
m jumpToUrl: url.
^true ].
(m hasProperty: #webBrowserView) ifTrue: [
m model jumpToUrl: url.
^true ].
m _ m owner. ]
].
"no browser in sight. ask if we should start a new browser"
((self confirm: 'open a browser to view this URL?' translated) and: [WebBrowser default notNil]) ifTrue: [
WebBrowser default openOnUrl: url.
^ true ].
"couldn't display in a browser. Offer to put up just the source"
response _ (PopUpMenu labels: 'View web page as source
Cancel' translated)
startUpWithCaption: 'Couldn''t find a web browser. View
page as source?' translated.
response = 1 ifTrue: [HTTPSocket httpShowPage: url].
^ true!
----- Method: TextURL>>analyze: (in category 'as yet unclassified') -----
analyze: aString
| list |
list _ super analyze: aString.
url _ list at: 1.
^ list at: 2!
----- Method: TextURL>>info (in category 'as yet unclassified') -----
info
^ url!
----- Method: TextURL>>url: (in category 'as yet unclassified') -----
url: aString
url _ aString!
----- Method: TextURL>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
strm nextPut: $R; nextPutAll: url; nextPut: $;!
TextAttribute subclass: #TextAlignment
instanceVariableNames: 'alignment'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
----- Method: TextAlignment class>>alignmentSymbol: (in category 'as yet unclassified') -----
alignmentSymbol: alignmentCode
^#(leftFlush rightFlush centered justified) at: (alignmentCode + 1)!
----- Method: TextAlignment class>>centered (in category 'instance creation') -----
centered
^self new alignment: 2!
----- Method: TextAlignment class>>justified (in category 'instance creation') -----
justified
^self new alignment: 3!
----- Method: TextAlignment class>>leftFlush (in category 'instance creation') -----
leftFlush
^self new alignment: 0!
----- Method: TextAlignment class>>rightFlush (in category 'instance creation') -----
rightFlush
^self new alignment: 1!
----- Method: TextAlignment>>= (in category 'as yet unclassified') -----
= other
^ (other class == self class)
and: [other alignment = alignment]!
----- Method: TextAlignment>>alignment (in category 'as yet unclassified') -----
alignment
^alignment!
----- Method: TextAlignment>>alignment: (in category 'as yet unclassified') -----
alignment: aNumber
alignment _ aNumber.!
----- Method: TextAlignment>>asPangoValueFrom:to: (in category 'pango') -----
asPangoValueFrom: start to: end
^ Array with: #A with: start with: end with: alignment
!
----- Method: TextAlignment>>dominates: (in category 'as yet unclassified') -----
dominates: other
"There can be only one..."
^self class == other class!
----- Method: TextAlignment>>emphasizeScanner: (in category 'as yet unclassified') -----
emphasizeScanner: scanner
"Set the emphasist for text scanning"
scanner setAlignment: alignment.!
----- Method: TextAlignment>>hash (in category 'as yet unclassified') -----
hash
"#hash is re-implemented because #= is re-implemented"
^ alignment hash!
----- Method: TextAlignment>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
strm nextPut: $a.
alignment printOn: strm.!
----- Method: TextAttribute>>actOnClickFor: (in category 'as yet unclassified') -----
actOnClickFor: model
"Subclasses may override to provide, eg, hot-spot actions"
^ false!
----- Method: TextAttribute>>actOnClickFor:in: (in category 'as yet unclassified') -----
actOnClickFor: model in: aParagraph
^self actOnClickFor: model!
----- Method: TextAttribute>>actOnClickFor:in:at: (in category 'as yet unclassified') -----
actOnClickFor: model in: aParagraph at: clickPoint
^self actOnClickFor: model in: aParagraph!
----- Method: TextAttribute>>actOnClickFor:in:at:editor: (in category 'as yet unclassified') -----
actOnClickFor: model in: aParagraph at: clickPoint editor: editor
^self actOnClickFor: model in: aParagraph at: clickPoint!
----- Method: TextAttribute>>anchoredMorph (in category 'as yet unclassified') -----
anchoredMorph
"If one hides here, return it"
^nil!
----- Method: TextAttribute>>couldDeriveFromPrettyPrinting (in category 'as yet unclassified') -----
couldDeriveFromPrettyPrinting
"Answer whether the receiver is a kind of attribute that could have been generated by doing polychrome pretty-printing of a method without functional text attributes."
^ true!
----- Method: TextAttribute>>dominatedByCmd0 (in category 'as yet unclassified') -----
dominatedByCmd0
"Subclasses may override if cmd-0 should turn them off"
^ false!
----- Method: TextAttribute>>dominates: (in category 'as yet unclassified') -----
dominates: another
"Subclasses may override condense multiple attributes"
^ false!
----- Method: TextAttribute>>emphasisCode (in category 'as yet unclassified') -----
emphasisCode
"Subclasses may override to add bold, italic, etc"
^ 0!
----- Method: TextAttribute>>emphasizeScanner: (in category 'as yet unclassified') -----
emphasizeScanner: scanner
"Subclasses may override to set, eg, font, color, etc"!
----- Method: TextAttribute>>forFontInStyle:do: (in category 'as yet unclassified') -----
forFontInStyle: aTextStyle do: aBlock
"No action is the default. Overridden by font specs"!
----- Method: TextAttribute>>isKern (in category 'testing') -----
isKern
^false!
----- Method: TextAttribute>>mayActOnClick (in category 'as yet unclassified') -----
mayActOnClick
"Subclasses may override to provide, eg, hot-spot actions"
^ false!
----- Method: TextAttribute>>mayBeExtended (in category 'as yet unclassified') -----
mayBeExtended
"A quality that may be overridden by subclasses, such as TextAnchors, that really only apply to a single character"
^ true!
----- Method: TextAttribute>>oldEmphasisCode: (in category 'as yet unclassified') -----
oldEmphasisCode: default
"Allows running thorugh possibly multiple attributes
and getting the emphasis out of any that has an emphasis (font number)"
^ default!
----- Method: TextAttribute>>reset (in category 'as yet unclassified') -----
reset
"Allow subclasses to prepare themselves for merging attributes"!
----- Method: TextAttribute>>set (in category 'as yet unclassified') -----
set
"Respond true to include this attribute (as opposed to, eg, a bold
emphasizer that is clearing the property"
^ true!
TextAttribute subclass: #TextColor
instanceVariableNames: 'color'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
!TextColor commentStamp: '<historical>' prior: 0!
A TextColor encodes a text color change applicable over a given range of text.!
----- Method: TextColor class>>black (in category 'constants') -----
black
^ self new color: Color black!
----- Method: TextColor class>>blue (in category 'constants') -----
blue
^ self new color: Color blue!
----- Method: TextColor class>>color: (in category 'instance creation') -----
color: aColor
^ self new color: aColor!
----- Method: TextColor class>>cyan (in category 'constants') -----
cyan
^ self new color: Color cyan!
----- Method: TextColor class>>gray (in category 'constants') -----
gray
^ self new color: Color gray!
----- Method: TextColor class>>green (in category 'constants') -----
green
^ self new color: Color green!
----- Method: TextColor class>>magenta (in category 'constants') -----
magenta
^ self new color: Color magenta!
----- Method: TextColor class>>red (in category 'constants') -----
red
^ self new color: Color red!
----- Method: TextColor class>>scanFrom: (in category 'instance creation') -----
scanFrom: strm
"read a color in the funny format used by Text styles on files. c125000255 or cblue;"
| r g b |
strm peek isDigit
ifTrue:
[r _ (strm next: 3) asNumber.
g _ (strm next: 3) asNumber.
b _ (strm next: 3) asNumber.
^ self color: (Color r: r g: g b: b range: 255)].
"A name of a color"
^ self color: (Color perform: (strm upTo: $;) asSymbol)!
----- Method: TextColor class>>white (in category 'constants') -----
white
^ self new color: Color white!
----- Method: TextColor class>>yellow (in category 'constants') -----
yellow
^ self new color: Color yellow!
----- Method: TextColor>>= (in category 'comparing') -----
= other
^ (other class == self class)
and: [other color = color]!
----- Method: TextColor>>asPangoValueFrom:to: (in category 'pango') -----
asPangoValueFrom: start to: end
^ Array with: #C with: start with: end with: color pixelValue32.
!
----- Method: TextColor>>color (in category 'accessing') -----
color
^ color!
----- Method: TextColor>>color: (in category 'accessing') -----
color: aColor
color _ aColor!
----- Method: TextColor>>dominates: (in category 'scanning') -----
dominates: other
^ other class == self class!
----- Method: TextColor>>emphasizeScanner: (in category 'scanning') -----
emphasizeScanner: scanner
"Set the emphasis for text display"
scanner textColor: color!
----- Method: TextColor>>hash (in category 'comparing') -----
hash
^ color hash!
----- Method: TextColor>>printOn: (in category 'printing') -----
printOn: aStream
super printOn: aStream.
aStream nextPutAll: ' code: '; print: color!
----- Method: TextColor>>writeScanOn: (in category 'scanning') -----
writeScanOn: strm
"Two formats. c125000255 or cblue;"
| nn str |
strm nextPut: $c.
(nn _ color name) ifNotNil: [
(self class respondsTo: nn) ifTrue: [
^ strm nextPutAll: nn; nextPut: $;]].
(Array with: color red with: color green with: color blue) do: [:float |
str _ '000', (float * 255) asInteger printString.
strm nextPutAll: (str copyFrom: str size-2 to: str size)]!
TextAttribute subclass: #TextEmphasis
instanceVariableNames: 'emphasisCode setMode'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
!TextEmphasis commentStamp: '<historical>' prior: 0!
A TextEmphasis, encodes a characteristic applicable to all fonts. The encoding is as follows:
1 bold
2 itallic
4 underlined
8 narrow
16 struck out!
----- Method: TextEmphasis class>>bold (in category 'as yet unclassified') -----
bold
^ self new emphasisCode: 1!
----- Method: TextEmphasis class>>italic (in category 'as yet unclassified') -----
italic
^ self new emphasisCode: 2!
----- Method: TextEmphasis class>>narrow (in category 'as yet unclassified') -----
narrow
^ TextKern kern: -1!
----- Method: TextEmphasis class>>normal (in category 'as yet unclassified') -----
normal
^ self new emphasisCode: 0!
----- Method: TextEmphasis class>>struckOut (in category 'as yet unclassified') -----
struckOut
^ self new emphasisCode: 16!
----- Method: TextEmphasis class>>underlined (in category 'as yet unclassified') -----
underlined
^ self new emphasisCode: 4!
----- Method: TextEmphasis>>= (in category 'as yet unclassified') -----
= other
^ (other class == self class)
and: [other emphasisCode = emphasisCode]!
----- Method: TextEmphasis>>dominatedByCmd0 (in category 'as yet unclassified') -----
dominatedByCmd0
"Cmd-0 should turn off emphasis"
^ true!
----- Method: TextEmphasis>>dominates: (in category 'as yet unclassified') -----
dominates: other
(emphasisCode = 0 and: [other dominatedByCmd0]) ifTrue: [^ true].
^ (other class == self class)
and: [emphasisCode = other emphasisCode]!
----- Method: TextEmphasis>>emphasisCode (in category 'as yet unclassified') -----
emphasisCode
^ emphasisCode!
----- Method: TextEmphasis>>emphasisCode: (in category 'as yet unclassified') -----
emphasisCode: int
emphasisCode _ int.
setMode _ true!
----- Method: TextEmphasis>>emphasizeScanner: (in category 'as yet unclassified') -----
emphasizeScanner: scanner
"Set the emphasist for text scanning"
scanner addEmphasis: emphasisCode!
----- Method: TextEmphasis>>hash (in category 'as yet unclassified') -----
hash
"#hash is re-implemented because #= is re-implemented"
^emphasisCode hash
!
----- Method: TextEmphasis>>printOn: (in category 'as yet unclassified') -----
printOn: strm
super printOn: strm.
strm nextPutAll: ' code: '; print: emphasisCode!
----- Method: TextEmphasis>>set (in category 'as yet unclassified') -----
set
^ setMode and: [emphasisCode ~= 0]!
----- Method: TextEmphasis>>turnOff (in category 'as yet unclassified') -----
turnOff
setMode _ false!
----- Method: TextEmphasis>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
emphasisCode = 1 ifTrue: [strm nextPut: $b].
emphasisCode = 2 ifTrue: [strm nextPut: $i].
emphasisCode = 0 ifTrue: [strm nextPut: $n].
emphasisCode = 16 ifTrue: [strm nextPut: $=].
emphasisCode = 4 ifTrue: [strm nextPut: $u].!
TextAttribute subclass: #TextFontChange
instanceVariableNames: 'fontNumber'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
!TextFontChange commentStamp: '<historical>' prior: 0!
A TextFontChange encodes a font change applicable over a given range of text. The font number is interpreted relative to the textStyle governing display of this text.!
----- Method: TextFontChange class>>defaultFontChange (in category 'as yet unclassified') -----
defaultFontChange
"Answer a TextFontChange that represents the default font"
^ self new fontNumber: TextStyle default defaultFontIndex!
----- Method: TextFontChange class>>font1 (in category 'as yet unclassified') -----
font1
^ self new fontNumber: 1!
----- Method: TextFontChange class>>font2 (in category 'as yet unclassified') -----
font2
^ self new fontNumber: 2!
----- Method: TextFontChange class>>font3 (in category 'as yet unclassified') -----
font3
^ self new fontNumber: 3!
----- Method: TextFontChange class>>font4 (in category 'as yet unclassified') -----
font4
^ self new fontNumber: 4!
----- Method: TextFontChange class>>fontNumber: (in category 'as yet unclassified') -----
fontNumber: n
^ self new fontNumber: n!
----- Method: TextFontChange>>= (in category 'as yet unclassified') -----
= other
^ (other class == self class)
and: [other fontNumber = fontNumber]!
----- Method: TextFontChange>>dominates: (in category 'as yet unclassified') -----
dominates: other
^ other isKindOf: TextFontChange!
----- Method: TextFontChange>>emphasizeScanner: (in category 'as yet unclassified') -----
emphasizeScanner: scanner
"Set the font for text display"
scanner setFont: fontNumber!
----- Method: TextFontChange>>fontNumber (in category 'as yet unclassified') -----
fontNumber
^ fontNumber!
----- Method: TextFontChange>>fontNumber: (in category 'as yet unclassified') -----
fontNumber: int
fontNumber _ int!
----- Method: TextFontChange>>forFontInStyle:do: (in category 'as yet unclassified') -----
forFontInStyle: aTextStyle do: aBlock
aBlock value: (aTextStyle fontAt: fontNumber)!
----- Method: TextFontChange>>hash (in category 'as yet unclassified') -----
hash
"#hash is re-implemented because #= is re-implemented"
^fontNumber hash!
----- Method: TextFontChange>>printOn: (in category 'as yet unclassified') -----
printOn: strm
super printOn: strm.
strm nextPutAll: ' font: '; print: fontNumber!
----- Method: TextFontChange>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
strm nextPut: $f.
fontNumber printOn: strm.!
TextFontChange subclass: #TextFontReference
instanceVariableNames: 'font'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
!TextFontReference commentStamp: '<historical>' prior: 0!
A TextFontReference encodes a font change applicable over a given range of text. The font reference is absolute: unlike a TextFontChange, it is independent of the textStyle governing display of this text.!
----- Method: TextFontReference class>>toFont: (in category 'as yet unclassified') -----
toFont: aFont
^ self new toFont: aFont!
----- Method: TextFontReference>>= (in category 'comparing') -----
= other
^ (other class == self class)
and: [other font = font]!
----- Method: TextFontReference>>couldDeriveFromPrettyPrinting (in category 'as yet unclassified') -----
couldDeriveFromPrettyPrinting
^ false!
----- Method: TextFontReference>>emphasizeScanner: (in category 'as yet unclassified') -----
emphasizeScanner: scanner
"Set the actual font for text display"
scanner setActualFont: font!
----- Method: TextFontReference>>font (in category 'as yet unclassified') -----
font
^ font!
----- Method: TextFontReference>>forFontInStyle:do: (in category 'as yet unclassified') -----
forFontInStyle: aTextStyle do: aBlock
aBlock value: font!
----- Method: TextFontReference>>hash (in category 'comparing') -----
hash
"#hash is re-implemented because #= is re-implemented"
^font hash!
----- Method: TextFontReference>>printOn: (in category 'comparing') -----
printOn: aStream
aStream nextPutAll: 'a TextFontReference(';
print: font;
nextPut: $)!
----- Method: TextFontReference>>toFont: (in category 'as yet unclassified') -----
toFont: aFont
font _ aFont!
----- Method: TextFontReference>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
strm nextPut: $F.
strm nextPutAll: font familyName; nextPut: $#.
font height printOn: strm.!
TextAttribute subclass: #TextIndent
instanceVariableNames: 'amount'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
!TextIndent commentStamp: '<historical>' prior: 0!
create a hanging indent. !
----- Method: TextIndent class>>amount: (in category 'instance creation') -----
amount: amount
"create a TextIndent which will indent by the given amount. Currently this is a number of tabs, but may change in the futur"
^super new amount: amount!
----- Method: TextIndent class>>example (in category 'example') -----
example
"TextIndent example"
| text pg |
"create an example text with some indentation"
text _ 'abcdao euoaeuo aeuo aeuoaeu o aeuoeauefgh bcd efghi' asText.
text addAttribute: (TextColor red) from: 3 to: 8.
text addAttribute: (TextIndent amount: 1) from: 1 to: 2.
text addAttribute: (TextIndent amount: 2) from: 20 to: 35.
"stick it in a paragraph and display it"
pg _ text asParagraph.
pg compositionRectangle: (0 at 0 extent: 100 at 200).
pg textStyle alignment: 2.
pg displayAt: 0 at 0.
!
----- Method: TextIndent class>>tabs: (in category 'instance creation') -----
tabs: numTabs
"create an indentation by the given number of tabs"
^self amount: numTabs!
----- Method: TextIndent>>amount (in category 'access') -----
amount
"number of tab spaces to indent by"
^amount!
----- Method: TextIndent>>amount: (in category 'access') -----
amount: anInteger
"change the number of tabs to indent by"
amount _ anInteger!
----- Method: TextIndent>>dominates: (in category 'condensing') -----
dominates: anAttribute
^(self class == anAttribute class)!
----- Method: TextIndent>>emphasizeScanner: (in category 'setting indentation') -----
emphasizeScanner: scanner
scanner indentationLevel: amount!
----- Method: TextIndent>>printOn: (in category 'printing') -----
printOn: aStream
super printOn: aStream.
aStream nextPutAll: ' amount: '.
amount printOn: aStream!
TextAttribute subclass: #TextKern
instanceVariableNames: 'kern active'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Text'!
!TextKern commentStamp: '<historical>' prior: 0!
A TextKern encodes a kerning change applicable over a given range of text. Positive values of kern spread letters out, negative kern will cause them to overlap more. Note that kerns other than 0 will display somewhat slower, as kerning is not yet supported in the text scanning primitive. !
----- Method: TextKern class>>kern: (in category 'as yet unclassified') -----
kern: kernValue
^ self new kern: kernValue!
----- Method: TextKern>>= (in category 'as yet unclassified') -----
= other
^ (other class == self class)
and: [other kern = kern]!
----- Method: TextKern>>couldDeriveFromPrettyPrinting (in category 'as yet unclassified') -----
couldDeriveFromPrettyPrinting
^ false!
----- Method: TextKern>>dominatedByCmd0 (in category 'as yet unclassified') -----
dominatedByCmd0
"Cmd-0 should turn off kerning"
^ true!
----- Method: TextKern>>dominates: (in category 'as yet unclassified') -----
dominates: other
"NOTE: The use of active in this code is specific to its use in the method
Text class addAttribute: att toArray: others"
(active and: [other class == self class and: [other kern + kern = 0]])
ifTrue: [active _ false. ^ true]. "can only dominate once"
^ false!
----- Method: TextKern>>emphasizeScanner: (in category 'as yet unclassified') -----
emphasizeScanner: scanner
"Augment (or diminish) the kerning offset for text display"
scanner addKern: kern!
----- Method: TextKern>>hash (in category 'as yet unclassified') -----
hash
"#hash is re-implemented because #= is re-implemented"
^kern hash!
----- Method: TextKern>>isKern (in category 'testing') -----
isKern
^true!
----- Method: TextKern>>kern (in category 'as yet unclassified') -----
kern
^ kern!
----- Method: TextKern>>kern: (in category 'as yet unclassified') -----
kern: kernValue
kern _ kernValue.
self reset.!
----- Method: TextKern>>reset (in category 'as yet unclassified') -----
reset
active _ true!
----- Method: TextKern>>set (in category 'as yet unclassified') -----
set
^ active!
----- Method: TextKern>>writeScanOn: (in category 'as yet unclassified') -----
writeScanOn: strm
kern > 0 ifTrue: [
1 to: kern do: [:kk | strm nextPut: $+]].
kern < 0 ifTrue: [
1 to: 0-kern do: [:kk | strm nextPut: $-]].!
Object subclass: #LimitingLineStreamWrapper
instanceVariableNames: 'stream line limitingBlock position'
classVariableNames: ''
poolDictionaries: 'TextConstants'
category: 'Collections-Streams'!
!LimitingLineStreamWrapper commentStamp: '<historical>' prior: 0!
I'm a wrapper for a stream optimized for line-by-line access using #nextLine. My instances can be nested.
I read one line ahead. Reading terminates when the stream ends, or if the limitingBlock evaluated with the line answers true. To skip the delimiting line for further reading use #skipThisLine.
Character-based reading (#next) is permitted, too. Send #updatePosition when switching from line-based reading.
See examples at the class side.
--bf 2/19/1999 12:52!
----- Method: LimitingLineStreamWrapper class>>example1 (in category 'examples') -----
example1
"LimitingLineStreamWrapper example1"
"Separate chunks of text delimited by a special string"
| inStream msgStream messages |
inStream _ self exampleStream.
msgStream _ LimitingLineStreamWrapper on: inStream delimiter: 'From '.
messages _ OrderedCollection new.
[inStream atEnd] whileFalse: [
msgStream skipThisLine.
messages add: msgStream upToEnd].
^messages
!
----- Method: LimitingLineStreamWrapper class>>example2 (in category 'examples') -----
example2
"LimitingLineStreamWrapper example2"
"Demo nesting wrappers - get header lines from some messages"
| inStream msgStream headers headerStream |
inStream _ self exampleStream.
msgStream _ LimitingLineStreamWrapper on: inStream delimiter: 'From '.
headers _ OrderedCollection new.
[inStream atEnd] whileFalse: [
msgStream skipThisLine. "Skip From"
headerStream _ LimitingLineStreamWrapper on: msgStream delimiter: ''.
headers add: headerStream linesUpToEnd.
[msgStream nextLine isNil] whileFalse. "Skip Body"
].
^headers
!
----- Method: LimitingLineStreamWrapper class>>exampleStream (in category 'examples') -----
exampleStream
^ReadStream on:
'From me at somewhere
From: me
To: you
Subject: Test
Test
>From you at elsewhere
From: you
To: me
Subject: Re: test
okay
'!
----- Method: LimitingLineStreamWrapper class>>on:delimiter: (in category 'instance creation') -----
on: aStream delimiter: aString
^self new setStream: aStream delimiter: aString
!
----- Method: LimitingLineStreamWrapper>>atEnd (in category 'testing') -----
atEnd
^line isNil or: [limitingBlock value: line]!
----- Method: LimitingLineStreamWrapper>>close (in category 'stream protocol') -----
close
^stream close!
----- Method: LimitingLineStreamWrapper>>delimiter: (in category 'accessing') -----
delimiter: aString
"Set limitBlock to check for a delimiting string. Be unlimiting if nil"
self limitingBlock: (aString caseOf: {
[nil] -> [[:aLine | false]].
[''] -> [[:aLine | aLine size = 0]]
} otherwise: [[:aLine | aLine beginsWith: aString]])
!
----- Method: LimitingLineStreamWrapper>>lastLineRead (in category 'accessing') -----
lastLineRead
"Return line last read. At stream end, this is the boundary line or nil"
^ line!
----- Method: LimitingLineStreamWrapper>>limitingBlock: (in category 'accessing') -----
limitingBlock: aBlock
"The limitingBlock is evaluated with a line to check if this line terminates the stream"
limitingBlock _ aBlock fixTemps.
self updatePosition!
----- Method: LimitingLineStreamWrapper>>linesUpToEnd (in category 'accessing') -----
linesUpToEnd
| elements ln |
elements _ OrderedCollection new.
[(ln _ self nextLine) isNil] whileFalse: [
elements add: ln].
^elements!
----- Method: LimitingLineStreamWrapper>>next (in category 'accessing') -----
next
"Provide character-based access"
position isNil ifTrue: [^nil].
position < line size ifTrue: [^line at: (position _ position + 1)].
line _ stream nextLine.
self updatePosition.
^ Character cr!
----- Method: LimitingLineStreamWrapper>>nextLine (in category 'accessing') -----
nextLine
| thisLine |
self atEnd ifTrue: [^nil].
thisLine _ line.
line _ stream nextLine.
^thisLine
!
----- Method: LimitingLineStreamWrapper>>peekLine (in category 'accessing') -----
peekLine
self atEnd ifTrue: [^nil].
^ line!
----- Method: LimitingLineStreamWrapper>>printOn: (in category 'printing') -----
printOn: aStream
super printOn: aStream.
aStream nextPutAll: ' on '.
stream printOn: aStream!
----- Method: LimitingLineStreamWrapper>>setStream:delimiter: (in category 'private') -----
setStream: aStream delimiter: aString
stream _ aStream.
line _ stream nextLine.
self delimiter: aString. "sets position"
!
----- Method: LimitingLineStreamWrapper>>skipThisLine (in category 'accessing') -----
skipThisLine
line _ stream nextLine.
self updatePosition.
!
----- Method: LimitingLineStreamWrapper>>upToEnd (in category 'accessing') -----
upToEnd
| ln |
^String streamContents: [:strm |
[(ln _ self nextLine) isNil] whileFalse: [
strm nextPutAll: ln; cr]]!
----- Method: LimitingLineStreamWrapper>>updatePosition (in category 'accessing') -----
updatePosition
"Call this before doing character-based access"
position _ self atEnd ifFalse: [0]!
ArrayedCollection subclass: #Text
instanceVariableNames: 'string runs'
classVariableNames: ''
poolDictionaries: 'TextConstants'
category: 'Collections-Text'!
!Text commentStamp: '<historical>' prior: 0!
I represent a character string that has been marked with abstract changes in character appearance. Actual display is performed in the presence of a TextStyle which indicates, for each abstract code, an actual font to be used. A Text associates a set of TextAttributes with each character in its character string. These attributes may be font numbers, emphases such as bold or italic, or hyperling actions. Font numbers are interpreted relative to whatever textStyle appears, along with the text, in a Paragraph. Since most characters have the same attributes as their neighbors, the attributes are stored in a RunArray for efficiency. Each of my instances has
string a String
runs a RunArray!
----- Method: Text class>>addAttribute:toArray: (in category 'private') -----
addAttribute: att toArray: others
"Add a new text attribute to an existing set"
"NOTE: The use of reset and set in this code is a specific
hack for merging TextKerns."
att reset.
^ Array streamContents:
[:strm | others do:
[:other | (att dominates: other) ifFalse: [strm nextPut: other]].
att set ifTrue: [strm nextPut: att]]!
----- Method: Text class>>fromString: (in category 'instance creation') -----
fromString: aString
"Answer an instance of me whose characters are those of the argument, aString."
^ self string: aString attribute: (TextFontChange fontNumber: TextStyle default defaultFontIndex)!
----- Method: Text class>>fromUser (in category 'instance creation') -----
fromUser
"Answer an instance of me obtained by requesting the user to type a string."
"Text fromUser"
^ self fromString:
(FillInTheBlank request: 'Enter text followed by carriage return')
!
----- Method: Text class>>initTextConstants (in category 'class initialization') -----
initTextConstants
"Initialize constants shared by classes associated with text display, e.g.,
Space, Tab, Cr, Bs, ESC."
"1/24/96 sw: in exasperation and confusion, changed cmd-g mapping from 231 to 232 to see if I could gain any relief?!!"
| letter varAndValue tempArray width |
"CtrlA..CtrlZ, Ctrla..Ctrlz"
letter _ $A.
#( 212 230 228 196 194 226 241 243 214 229 200 217 246
245 216 202 210 239 211 240 197 198 209 215 242 231
1 166 228 132 130 12 232 179 150 165 136 153 182
14 15 138 17 18 19 11 21 134 145 151 178 167 ) do:
[:kbd |
TextConstants at: ('Ctrl', letter asSymbol) asSymbol put: kbd asCharacter.
letter _ letter == $Z ifTrue: [$a] ifFalse: [(letter asciiValue + 1) asCharacter]].
varAndValue _ #(
Space 32
Tab 9
CR 13
Enter 3
BS 8
BS2 158
ESC 160
Clear 173
).
varAndValue size odd ifTrue: [self error: 'unpaired text constant'].
(2 to: varAndValue size by: 2) do:
[:i | TextConstants at: (varAndValue at: i - 1) put: (varAndValue at: i) asCharacter].
varAndValue _ #(
CtrlDigits (159 144 143 128 127 129 131 180 149 135)
CtrlOpenBrackets (201 7 218 249 219 15)
"lparen gottn by ctrl-_ = 201; should be 213 but can't type that on Mac"
"location of non-character stop conditions"
EndOfRun 257
CrossedX 258
"values for alignment"
LeftFlush 0
RightFlush 1
Centered 2
Justified 3
"subscripts for a marginTabsArray tuple"
LeftMarginTab 1
RightMarginTab 2
"font faces"
Basal 0
Bold 1
Italic 2
"in case font doesn't have a width for space character"
"some plausible numbers-- are they the right ones?"
DefaultSpace 4
DefaultTab 24
DefaultLineGrid 16
DefaultBaseline 12
DefaultFontFamilySize 3 "basal, bold, italic"
).
varAndValue size odd ifTrue: [self error: 'unpaired text constant'].
(2 to: varAndValue size by: 2) do:
[:i | TextConstants at: (varAndValue at: i - 1) put: (varAndValue at: i)].
TextConstants at: #DefaultRule put: Form over.
TextConstants at: #DefaultMask put: Color black.
width _ Display width max: 720.
tempArray _ Array new: width // DefaultTab.
1 to: tempArray size do:
[:i | tempArray at: i put: DefaultTab * i].
TextConstants at: #DefaultTabsArray put: tempArray.
tempArray _ Array new: (width // DefaultTab) // 2.
1 to: tempArray size do:
[:i | tempArray at: i put: (Array with: (DefaultTab*i) with: (DefaultTab*i))].
TextConstants at: #DefaultMarginTabsArray put: tempArray.
"Text initTextConstants "!
----- Method: Text class>>initialize (in category 'class initialization') -----
initialize "Text initialize"
"Initialize constants shared by classes associated with text display."
TextConstants at: #CaretForm put:
(Form extent: 16 at 5
fromArray: #(2r001100e26 2r001100e26 2r011110e26 2r111111e26 2r110011e26)
offset: -3 at 0).
self initTextConstants!
----- Method: Text class>>new: (in category 'instance creation') -----
new: stringSize
^self fromString: (String new: stringSize)!
----- Method: Text class>>streamContents: (in category 'instance creation') -----
streamContents: blockWithArg
| stream |
stream _ TextStream on: (self new: 400).
blockWithArg value: stream.
^ stream contents!
----- Method: Text class>>string:attribute: (in category 'instance creation') -----
string: aString attribute: att
"Answer an instance of me whose characters are aString.
att is a TextAttribute."
^self string: aString attributes: (Array with: att)!
----- Method: Text class>>string:attributes: (in category 'instance creation') -----
string: aString attributes: atts
"Answer an instance of me whose characters are those of aString.
atts is an array of TextAttributes."
^self string: aString runs: (RunArray new: aString size withAll: atts)!
----- Method: Text class>>string:emphasis: (in category 'instance creation') -----
string: aString emphasis: emphasis
"This is an old method that is mainly used by old applications"
emphasis isNumber ifTrue:
[self halt: 'Numeric emphasis is not supported in Squeak'.
"But if you proceed, we will do our best to give you what you want..."
^ self string: aString runs: (RunArray new: aString size withAll:
(Array with: (TextFontChange new fontNumber: emphasis)))].
^ self string: aString attributes: emphasis!
----- Method: Text class>>string:runs: (in category 'private') -----
string: aString runs: anArray
^self basicNew setString: aString setRuns: anArray!
----- Method: Text>>= (in category 'comparing') -----
= other
"Am I equal to the other Text or String?
***** Warning ***** Two Texts are considered equal if they have the same characters in them. They might have completely different emphasis, fonts, sizes, text actions, or embedded morphs. If you need to find out if one is a true copy of the other, you must do (text1 = text2 and: [text1 runs = text2 runs])."
other isText ifTrue: ["This is designed to run fast even for megabytes"
^ string == other string or: [string = other string]].
other isString ifTrue: [^ string == other or: [string = other]].
^ false!
----- Method: Text>>addAttribute: (in category 'emphasis') -----
addAttribute: att
^ self addAttribute: att from: 1 to: self size!
----- Method: Text>>addAttribute:from:to: (in category 'emphasis') -----
addAttribute: att from: start to: stop
"Set the attribute for characters in the interval start to stop."
runs _ runs copyReplaceFrom: start to: stop
with: ((runs copyFrom: start to: stop)
mapValues:
[:attributes | Text addAttribute: att toArray: attributes])
!
----- Method: Text>>alignmentAt:ifAbsent: (in category 'emphasis') -----
alignmentAt: characterIndex ifAbsent: aBlock
| attributes emph |
self size = 0 ifTrue: [^aBlock value].
emph _ nil.
attributes _ runs at: characterIndex.
attributes do:[:att | (att isKindOf: TextAlignment) ifTrue:[emph _ att]].
^ emph ifNil: aBlock ifNotNil:[emph alignment]!
----- Method: Text>>allBold (in category 'emphasis') -----
allBold
"Force this whole text to be bold."
string size = 0 ifTrue: [^self].
self makeBoldFrom: 1 to: string size!
----- Method: Text>>append: (in category 'accessing') -----
append: stringOrText
self replaceFrom: string size + 1
to: string size with: stringOrText!
----- Method: Text>>asDisplayText (in category 'converting') -----
asDisplayText
"Answer a DisplayText whose text is the receiver."
^DisplayText text: self!
----- Method: Text>>asNumber (in category 'converting') -----
asNumber
"Answer the number created by interpreting the receiver as the textual
representation of a number."
^string asNumber!
----- Method: Text>>asOctetStringText (in category 'converting') -----
asOctetStringText
string class == WideString ifTrue: [
^ self class string: string asOctetString runs: self runs copy.
].
^self.
!
----- Method: Text>>asString (in category 'converting') -----
asString
"Answer a String representation of the textual receiver."
^string!
----- Method: Text>>asStringOrText (in category 'converting') -----
asStringOrText
"Answer the receiver itself."
^self!
----- Method: Text>>asText (in category 'converting') -----
asText
"Answer the receiver itself."
^self!
----- Method: Text>>asUrl (in category 'converting') -----
asUrl
^self asString asUrl!
----- Method: Text>>asUrlRelativeTo: (in category 'converting') -----
asUrlRelativeTo: aUrl
^self asString asUrlRelativeTo: aUrl!
----- Method: Text>>askIfAddStyle:req: (in category 'attributes') -----
askIfAddStyle: priorMethod req: requestor
"Ask the user if we have a complex style (i.e. bold) for the first time"
| tell answ old |
(Preferences browseWithPrettyPrint and: [Preferences colorWhenPrettyPrinting])
ifTrue: [self couldDeriveFromPrettyPrinting ifTrue: [^ self asString]].
self runs coalesce.
self unembellished ifTrue: [^ self asString].
priorMethod ifNotNil: [old _ priorMethod getSourceFromFile].
(old == nil or: [old unembellished])
ifTrue:
[tell _ 'This method contains style for the first time (e.g. bold or colored text).
Do you really want to save the style info?' translated.
answ _ (PopUpMenu labels: 'Save method with style
Save method simply' translated)
startUpWithCaption: tell.
answ = 2 ifTrue: [^ self asString]]!
----- Method: Text>>at: (in category 'accessing') -----
at: index
^string at: index!
----- Method: Text>>at:put: (in category 'accessing') -----
at: index put: character
^string at: index put: character!
----- Method: Text>>attributesAt: (in category 'emphasis') -----
attributesAt: characterIndex
"Answer the code for characters in the run beginning at characterIndex."
"NB: no senders any more (supplanted by #attributesAt:forStyle: but retained for the moment in order not to break user code that may exist somewhere that still calls this"
| attributes |
self size = 0
ifTrue: [^ Array with: (TextFontChange new fontNumber: 1)]. "null text tolerates access"
attributes _ runs at: characterIndex.
^ attributes!
----- Method: Text>>attributesAt:do: (in category 'emphasis') -----
attributesAt: characterIndex do: aBlock
"Answer the code for characters in the run beginning at characterIndex."
"NB: no senders any more (supplanted by #attributesAt:forStyle: but retained for the moment in order not to break user code that may exist somewhere that still calls this"
self size = 0 ifTrue:[^self].
(runs at: characterIndex) do: aBlock!
----- Method: Text>>attributesAt:forStyle: (in category 'emphasis') -----
attributesAt: characterIndex forStyle: aTextStyle
"Answer the code for characters in the run beginning at characterIndex."
| attributes |
self size = 0
ifTrue: [^ Array with: (TextFontChange new fontNumber: aTextStyle defaultFontIndex)]. "null text tolerates access"
attributes _ runs at: characterIndex.
^ attributes!
----- Method: Text>>copy (in category 'copying') -----
copy
^ self class new setString: string copy setRuns: runs copy
!
----- Method: Text>>copyFrom:to: (in category 'copying') -----
copyFrom: start to: stop
"Answer a copied subrange of the receiver."
| realStart realStop |
stop > self size
ifTrue: [realStop _ self size] "handle selection at end of string"
ifFalse: [realStop _ stop].
start < 1
ifTrue: [realStart _ 1] "handle selection before start of string"
ifFalse: [realStart _ start].
^Text
string: (string copyFrom: realStart to: realStop)
runs: (runs copyFrom: realStart to: realStop)!
----- Method: Text>>copyReplaceFrom:to:with: (in category 'copying') -----
copyReplaceFrom: start to: stop with: aTextOrString
| txt |
txt _ aTextOrString asText. "might be a string"
^self class
string: (string copyReplaceFrom: start to: stop with: txt string)
runs: (runs copyReplaceFrom: start to: stop with: txt runs)
!
----- Method: Text>>copyReplaceTokens:with: (in category 'copying') -----
copyReplaceTokens: oldSubstring with: newSubstring
"Replace all occurrences of oldSubstring that are surrounded
by non-alphanumeric characters"
^ self copyReplaceAll: oldSubstring with: newSubstring asTokens: true
"'File asFile Files File''s File' copyReplaceTokens: 'File' with: 'Snick'"!
----- Method: Text>>couldDeriveFromPrettyPrinting (in category 'attributes') -----
couldDeriveFromPrettyPrinting
"Return true if the receiver has any TextAttributes that are functional rather than simply appearance-related"
runs values do:
[:emphArray | emphArray do:
[:emph | emph couldDeriveFromPrettyPrinting ifFalse: [^ false]]].
^ true!
----- Method: Text>>deepCopy (in category 'copying') -----
deepCopy
^ self copy "Both string and runs are assumed to be read-only"!
----- Method: Text>>embeddedMorphs (in category 'accessing') -----
embeddedMorphs
"return the list of morphs embedded in me"
| morphs |
morphs := IdentitySet new.
runs withStartStopAndValueDo:
[:start :stop :attribs |
attribs
do: [:attrib | attrib anchoredMorph ifNotNil: [morphs add: attrib anchoredMorph]]].
^morphs select: [:m | m isMorph]!
----- Method: Text>>embeddedMorphsFrom:to: (in category 'accessing') -----
embeddedMorphsFrom: start to: stop
"return the list of morphs embedded in me"
| morphs |
morphs := IdentitySet new.
runs
runsFrom: start
to: stop
do:
[:attribs |
attribs
do: [:attr | attr anchoredMorph ifNotNil: [morphs add: attr anchoredMorph]]].
^morphs select: [:m | m isMorph]!
----- Method: Text>>emphasisAt: (in category 'emphasis') -----
emphasisAt: characterIndex
"Answer the fontfor characters in the run beginning at characterIndex."
| attributes emph |
self size = 0 ifTrue: [^ 0]. "null text tolerates access"
emph _ 0.
attributes _ runs at: characterIndex.
attributes do:
[:att | emph _ emph bitOr: att emphasisCode].
^ emph
!
----- Method: Text>>find: (in category 'emphasis') -----
find: attribute
"Return the first interval over which this attribute applies"
| begin end |
begin _ 0.
runs withStartStopAndValueDo:
[:start :stop :attributes |
(attributes includes: attribute)
ifTrue: [begin = 0 ifTrue: [begin _ start].
end _ stop]
ifFalse: [begin > 0 ifTrue: [^ begin to: end]]].
begin > 0 ifTrue: [^ begin to: end].
^ nil!
----- Method: Text>>findString:startingAt: (in category 'accessing') -----
findString: aString startingAt: start
"Answer the index of subString within the receiver, starting at index
start. If the receiver does not contain subString, answer 0."
^string findString: aString asString startingAt: start!
----- Method: Text>>findString:startingAt:caseSensitive: (in category 'accessing') -----
findString: aString startingAt: start caseSensitive: caseSensitive
"Answer the index of subString within the receiver, starting at index
start. If the receiver does not contain subString, answer 0."
^string findString: aString asString startingAt: start caseSensitive: caseSensitive!
----- Method: Text>>fontAt:withStyle: (in category 'emphasis') -----
fontAt: characterIndex withStyle: aTextStyle
"Answer the fontfor characters in the run beginning at characterIndex."
| attributes font |
self size = 0 ifTrue: [^ aTextStyle defaultFont]. "null text tolerates access"
attributes _ runs at: characterIndex.
font _ aTextStyle defaultFont. "default"
attributes do:
[:att | att forFontInStyle: aTextStyle do: [:f | font _ f]].
^ font!
----- Method: Text>>fontNumberAt: (in category 'emphasis') -----
fontNumberAt: characterIndex
"Answer the fontNumber for characters in the run beginning at characterIndex."
| attributes fontNumber |
self size = 0 ifTrue: [^1]. "null text tolerates access"
attributes _ runs at: characterIndex.
fontNumber _ 1.
attributes do: [:att | (att isMemberOf: TextFontChange) ifTrue: [fontNumber _ att fontNumber]].
^ fontNumber
!
----- Method: Text>>hash (in category 'comparing') -----
hash
"#hash is implemented, because #= is implemented. We are now equal to a string with the same characters. Hash must reflect that."
^ string hash!
----- Method: Text>>howManyMatch: (in category 'comparing') -----
howManyMatch: aString
^ self string howManyMatch: aString!
----- Method: Text>>isText (in category 'comparing') -----
isText
^ true!
----- Method: Text>>isoToSqueak (in category 'converting') -----
isoToSqueak
^self "no longer needed"!
----- Method: Text>>lineCount (in category 'accessing') -----
lineCount
^ string lineCount!
----- Method: Text>>macToSqueak (in category 'converting') -----
macToSqueak
"Convert the receiver from MacRoman to Squeak encoding"
^ self class new setString: string macToSqueak setRuns: runs copy!
----- Method: Text>>makeBoldFrom:to: (in category 'emphasis') -----
makeBoldFrom: start to: stop
^ self addAttribute: TextEmphasis bold from: start to: stop!
----- Method: Text>>makeSelectorBold (in category 'emphasis') -----
makeSelectorBold
"For formatting Smalltalk source code, set the emphasis of that portion of
the receiver's string that parses as a message selector to be bold."
| parser i |
string size = 0 ifTrue: [^ self].
i _ 0.
[(string at: (i _ i + 1)) isSeparator] whileTrue.
(string at: i) = $[ ifTrue: [^ self]. "block, no selector"
(parser _ Compiler parserClass new) parseSelector: string.
self makeBoldFrom: 1 to: (parser endOfLastToken min: string size)!
----- Method: Text>>makeSelectorBoldIn: (in category 'emphasis') -----
makeSelectorBoldIn: aClass
"For formatting Smalltalk source code, set the emphasis of that portion of
the receiver's string that parses as a message selector to be bold."
| parser |
string size = 0 ifTrue: [^self].
(parser _ aClass parserClass new) parseSelector: string.
self makeBoldFrom: 1 to: (parser endOfLastToken min: string size)!
----- Method: Text>>prepend: (in category 'accessing') -----
prepend: stringOrText
self replaceFrom: 1 to: 0 with: stringOrText!
----- Method: Text>>printOn: (in category 'printing') -----
printOn: aStream
self printNameOn: aStream.
aStream nextPutAll: ' for '; print: string!
----- Method: Text>>rangeOf:startingAt: (in category 'accessing') -----
rangeOf: attribute startingAt: index
"Answer an interval that gives the range of attribute at index position index. An empty interval with start value index is returned when the attribute is not present at position index. "
^string size = 0
ifTrue: [index to: index - 1]
ifFalse: [runs rangeOf: attribute startingAt: index]!
----- Method: Text>>rangeOf:startingAt:forStyle: (in category 'accessing') -----
rangeOf: attribute startingAt: index forStyle: aTextStyle
"aTextStyle is not really needed, it is kept for compatibility with an earlier method version "
self deprecated: 'Use Text>>rangeOf:startingAt: instead.'.
^self rangeOf: attribute startingAt: index!
----- Method: Text>>removeAttribute:from:to: (in category 'emphasis') -----
removeAttribute: att from: start to: stop
"Remove the attribute over the interval start to stop."
runs _ runs copyReplaceFrom: start to: stop
with: ((runs copyFrom: start to: stop)
mapValues:
[:attributes | attributes copyWithout: att])
!
----- Method: Text>>removeAttributesThat:replaceAttributesThat:by: (in category 'converting') -----
removeAttributesThat: removalBlock replaceAttributesThat: replaceBlock by: convertBlock
"Enumerate all attributes in the receiver. Remove those passing removalBlock and replace those passing replaceBlock after converting it through convertBlock"
| added removed new |
"Deliberately optimized for the no-op default."
added _ removed _ nil.
runs withStartStopAndValueDo: [ :start :stop :attribs |
attribs do: [ :attrib |
(removalBlock value: attrib) ifTrue:[
removed ifNil:[removed _ WriteStream on: #()].
removed nextPut: {start. stop. attrib}.
] ifFalse:[
(replaceBlock value: attrib) ifTrue:[
removed ifNil:[removed _ WriteStream on: #()].
removed nextPut: {start. stop. attrib}.
new _ convertBlock value: attrib.
added ifNil:[added _ WriteStream on: #()].
added nextPut: {start. stop. new}.
].
].
].
].
(added == nil and:[removed == nil]) ifTrue:[^self].
"otherwise do the real work"
removed ifNotNil:[removed contents do:[:spec|
self removeAttribute: spec last from: spec first to: spec second]].
added ifNotNil:[added contents do:[:spec|
self addAttribute: spec last from: spec first to: spec second]].!
----- Method: Text>>replaceFrom:to:with: (in category 'accessing') -----
replaceFrom: start to: stop with: aText
| txt |
txt _ aText asText. "might be a string"
string _ string copyReplaceFrom: start to: stop with: txt string.
runs _ runs copyReplaceFrom: start to: stop with: txt runs!
----- Method: Text>>replaceFrom:to:with:startingAt: (in category 'converting') -----
replaceFrom: start to: stop with: replacement startingAt: repStart
"This destructively replaces elements from start to stop in the receiver starting at index, repStart, in replacementCollection. Do it to both the string and the runs."
| rep newRepRuns |
rep _ replacement asText. "might be a string"
string replaceFrom: start to: stop with: rep string startingAt: repStart.
newRepRuns _ rep runs copyFrom: repStart to: repStart + stop - start.
runs _ runs copyReplaceFrom: start to: stop with: newRepRuns!
----- Method: Text>>reversed (in category 'converting') -----
reversed
"Answer a copy of the receiver with element order reversed."
^ self class string: string reversed runs: runs reversed.
" It is assumed that self size = runs size holds. "!
----- Method: Text>>runLengthFor: (in category 'emphasis') -----
runLengthFor: characterIndex
"Answer the count of characters remaining in run beginning with
characterIndex."
^runs runLengthAt: characterIndex!
----- Method: Text>>runs (in category 'private') -----
runs
^runs!
----- Method: Text>>runs: (in category 'accessing') -----
runs: anArray
runs := anArray!
----- Method: Text>>setString:setRuns: (in category 'private') -----
setString: aString setRuns: anArray
string _ aString.
runs _ anArray!
----- Method: Text>>setString:setRunsChecking: (in category 'private') -----
setString: aString setRunsChecking: aRunArray
"Check runs and do the best you can to make them fit..."
string _ aString.
"check the runs"
aRunArray ifNil: [^ aString asText].
(aRunArray isKindOf: RunArray) ifFalse: [^ aString asText].
aRunArray runs size = aRunArray values size ifFalse: [^ aString asText].
(aRunArray values includes: #()) ifTrue: [^ aString asText]. "not allowed?"
aRunArray size = aString size ifFalse: [^ aString asText].
runs _ aRunArray.!
----- Method: Text>>size (in category 'accessing') -----
size
^string size!
----- Method: Text>>squeakToIso (in category 'converting') -----
squeakToIso
^self "no longer needed"!
----- Method: Text>>squeakToMac (in category 'converting') -----
squeakToMac
"Convert the receiver from Squeak to MacRoman encoding"
^ self class new setString: string squeakToMac setRuns: runs copy!
----- Method: Text>>storeOn: (in category 'printing') -----
storeOn: aStream
aStream nextPutAll: '(Text string: ';
store: string;
nextPutAll: ' runs: ';
store: runs;
nextPut: $)!
----- Method: Text>>string (in category 'accessing') -----
string
"Answer the string representation of the receiver."
^string!
----- Method: Text>>translated (in category 'converting') -----
translated
^ string translated!
----- Method: Text>>unembellished (in category 'attributes') -----
unembellished
"Return true if the only emphases are the default font and bold"
| font1 bold |
font1 _ TextFontChange defaultFontChange.
bold _ TextEmphasis bold.
Preferences ignoreStyleIfOnlyBold ifFalse:
["Ignore font1 only or font1-bold followed by font1-plain"
^ (runs values = (Array with: (Array with: font1)))
or: [runs values = (Array with: (Array with: font1 with: bold)
with: (Array with: font1))]].
"If preference is set, then ignore any combo of font1 and bold"
runs withStartStopAndValueDo:
[:start :stop :emphArray |
emphArray do:
[:emph | (font1 = emph or: [bold = emph]) ifFalse: [^ false]]].
^ true!
----- Method: Text>>withSqueakLineEndings (in category 'converting') -----
withSqueakLineEndings
"Answer a copy of myself in which all sequences of <CR><LF> or <LF> have been changed to <CR>"
| newText |
(string includes: Character lf) ifFalse: [ ^self copy ].
newText _ self copyReplaceAll: String crlf with: String cr asTokens: false.
(newText asString includes: Character lf) ifFalse: [ ^newText ].
^newText copyReplaceAll: String lf with: String cr asTokens: false.!
More information about the Packages
mailing list