!=========================================================================
! Copyright (C) by VMware, Inc. 1991-2011.  All Rights Reserved
!
! $Id: charcoll.gs,v 1.26.2.1 2008-02-21 01:13:26 lalmarod Exp $
!
!=========================================================================

removeallmethods CharacterCollection
removeallclassmethods CharacterCollection

! ------------------- Class methods for CharacterCollection

category: 'For Documentation Installation only'
classmethod: CharacterCollection
installDocumentation

| doc txt |
doc := GsClassDocumentation newForClass: self.

txt := (GsDocText new) details:
'CharacterCollection is an abstract superclass for behavior that is common
 to all indexed collections of Characters.

 Subclasses must reimplement the following selectors:

 size
 size:
 at:
 at:put:
 removeFrom:to:
 insertAll:at:

 However these selectors do not generate the subclass-responsibility error
 (error 2008) because to do so would break the Object | printString method.'.
doc documentClassWith: txt.

txt := (GsDocText new) details:
'Methods in this category are obsolete and are provided only for compatibility
 with earlier releases of GemStone.  They will be removed in a future release.'.
doc documentCategory: #'Backward Compatibility' with: txt.

txt := (GsDocText new) details:
'Some of these methods determine whether one String collates before another.
 In collation, the values of the receiver and aCharCollection are compared
 Character-by-Character, from left to right, in case-sensitive fashion.  If two
 CharacterCollections are of different length, and all Characters in the
 shorter collection are equal to their counterparts in the longer one, the
 shorter collection collates before the longer.

 Unlike the comparison methods for the superclass SequenceableCollection, these
 methods merely require that both the receiver and argument be kinds of
 CharacterCollection (rather than requiring both to be of the same class).'.
doc documentCategory: #Comparing with: txt.

txt := (GsDocText new) details:
'Methods in this category are obsolete and are provided only for compatibility
 with earlier releases of GemStone.  They will be removed in a future release.'.
doc documentClassCategory: #'Backward Compatibility' with: txt.

self description: doc.
%

category: 'Backward Compatibility'
classmethod: CharacterCollection
fromServerTextFile: aFileSpec

"Obsolete in GemStone 4.1.  Use an instance of GsFile to access the file system
 of the client or server machines."

^ self new _fromServerTextFile: aFileSpec
%

category 'Instance Creation'
classmethod: CharacterCollection
_newString

"Returns a new instance of the receiver, or instance of String as appropriate. 
 Reimplemented in subclasses as needed to handle canonical symbols."

^ self new
%

category 'Instance Creation'
classmethod: CharacterCollection
_newString: aSize

"Returns a new instance of the receiver, or instance of String as appropriate. 
 Reimplemented in subclasses as needed to handle canonical symbols."

^ self new: aSize
%

category: 'Instance Creation'
classmethod: CharacterCollection
fromStream: aStream width: anInteger

"Returns a new instance of the receiver's class that contains the next
 anInteger Characters of aStream."

| result |

self _checkReadStream: aStream forClass: CharacterCollection.
result:= self new: anInteger.
1 to: anInteger
  do: [ :i | result at: i put: aStream next ].
^ result
%

category: 'Instance Creation'
classmethod: CharacterCollection
withAll: aSequenceableCollection

"Returns a new instance of the receiver that contains the elements in the
 argument aSequenceableCollection."

| result |
result:= self new.
aSequenceableCollection do: [:each | result add: each].
^result
%

category: 'Instance Creation'
classmethod: CharacterCollection
withBytes: aByteObject

"Returns a new instance of the receiver that contains the bytes in the
 argument aByteObject."

| result |
result:= self new _basicSize: (aByteObject _basicSize).
1 to: (aByteObject _basicSize)
  do: [:i | result _basicAt: i put: (aByteObject _basicAt: i) ].
^ result.
%

! ------------------- Instance methods for CharacterCollection

category: 'Concatenating'
method: CharacterCollection
, aCharOrCharCollection

"Returns a new instance of the receiver's class that contains the elements of
 the receiver followed by the elements of aCharOrCharCollection.

 Warning: Creating a new instance and copying the receiver take time.  If you
 can safely modify the receiver, it can be much faster to use the addAll:
 method.  See the documentation of the Concatenating category of class
 SequenceableCollection for more details."

| newCollection |

newCollection := self copy.
newCollection addAll: aCharOrCharCollection.
^ newCollection
%

category: 'Comparing'
method: CharacterCollection
< aCharCollection

"Returns true if the receiver collates before the argument.  Returns false
 otherwise."

^ (self _charCollCompare: aCharCollection) == -1
%

category: 'Comparing'
method: CharacterCollection
<= aCharCollection

"Returns true if the receiver collates before the argument or if all of the
 corresponding Characters in the receiver and argument are equal.
 Returns false otherwise."

^ (self _charCollCompare: aCharCollection) <= 0
%

category: 'Comparing'
method: CharacterCollection
= aCharCollection

"Returns true if all of the corresponding Characters in the receiver and
 argument are equal.  Returns false otherwise."

self == aCharCollection ifTrue:[ ^ true].

(aCharCollection isKindOf: CharacterCollection)
  ifFalse: [ ^ false ].

(self size) ~~ (aCharCollection size)
  ifTrue: [ ^ false ].

^ (self _charCollCompare: aCharCollection) == 0
%

category: 'Comparing'
method: CharacterCollection
> aCharCollection

"Returns true if the receiver collates after the argument.  Returns false
 otherwise."

^ (self _charCollCompare: aCharCollection) == 1
%

category: 'Comparing'
method: CharacterCollection
>= aCharCollection

"Returns true if the receiver collates after the argument or if all of the
 corresponding Characters in the receiver and argument are equal.  Returns
 false otherwise."

^ (self _charCollCompare: aCharCollection) >= 0
%

category: 'Accessing'
method: CharacterCollection
_at: anIndex

"Private.  Reimplemented to return a kind of Character."

^ self at: anIndex
%

category: 'Private'
method: CharacterCollection
_charCollCompare: aCharCollection

"Returns -1 if self < aCharCollection, returns 0 if self = aCharCollection,
 and returns 1 if self > aCharCollection."

| selfSize argSize selfElement argElement |

aCharCollection _validateClass: CharacterCollection.

selfSize:= self size.
argSize:= aCharCollection size.

1 to: (selfSize min: argSize) do: [:i |
 
  selfElement := self at: i.
  argElement  := aCharCollection at: i.

  (selfElement < argElement) ifTrue: [ ^ -1 ].
  (selfElement > argElement) ifTrue: [ ^ 1 ]
  ].

"All elements of self and argument are equal."

(selfSize < argSize) ifTrue: [ ^ -1 ].
(selfSize > argSize) ifTrue: [ ^ 1 ].

^ 0 "equal"
%

category: 'Instance Creation'
method: CharacterCollection
_fromServerTextFile: aFileSpec

"Import the contents of aFileSpec into the receiver."

<primitive: 302>

aFileSpec _validateByteClass: CharacterCollection .
aFileSpec _error: #hostErrFileImport args: #()
%

category: 'Hashing'
method: CharacterCollection
hash

"Returns a positive Integer based on a case-sensitive hash of the contents 
 of the receiver.  The algorithm implemented is described in:

 [Pearson 90]
 Pearson, Peter K., Fast Hashing of Variable-Length Text Strings,
 Communications of the ACM 33, 6, (June 1990), 677-680."

<primitive: 31>
self _primitiveFailed: #hash .
self _uncontinuableError
%

! deleted _idxCompareForCompareEqualTo: v2.0

! deleted _idxCompareLessThanOrEqual: v2.0

! deleted _idxCompareGreaterThanOrEqual: v2.0

! deleted _idxCompareEqualTo: v2.0

! deleted _idxCompareForSortEqualTo: v2.0

category: 'Comparison'
method: CharacterCollection
_compareEqual: aCharacterCollection caseInsensitive: noCaseBoolean from: inclusiveStart to: inclusiveEnd
    "Considering only the characters between the inclusiveStart index
    and the inclusiveEndIndex, answer true if self = aCharacterCollection, and
    answer false otherwise. Assumes both collections have at least
    inclusiveEnd number of elements."

    | selfElement otherElement |
noCaseBoolean
  ifTrue: [
    inclusiveStart to: inclusiveEnd do: 
        [:i |
         selfElement := self at: i.
         otherElement  := aCharacterCollection at: i.
         (selfElement isEquivalent: otherElement)
             ifFalse:
                 [ ^false ]].
  ]
  ifFalse: [
    "use =, if inclusiveStart == 1 and inclusiveEnd == self size and inclusiveEnd == aCharacterCollection size"
    ((((inclusiveStart == 1) 
      _and: [inclusiveEnd == self size]) 
        _and: [inclusiveEnd == aCharacterCollection size]) 
          _and: [self class = aCharacterCollection class])
      ifTrue: [
        "collapses to an ="
        ^self = aCharacterCollection.
      ].
    inclusiveStart to: inclusiveEnd do: 
        [:i |
         selfElement := self at: i.
         otherElement  := aCharacterCollection at: i.
         (selfElement = otherElement)
             ifFalse:
                 [ ^false ]].
  ].
^true
%

category: 'Comparison'
method: CharacterCollection
_compare: aCharacterCollection caseInsensitive: noCaseBoolean from: inclusiveStart to: inclusiveEnd
    "Considering only the characters between the inclusiveStart index
    and the inclusiveEndIndex, answer -1 if self <
    aCharacterCollection, answer 0 if self = aCharacterCollection, and
    answer 1 otherwise. Assumes both collections have at least
    inclusiveEnd number of elements."

    | selfElement otherElement |
    inclusiveStart to: inclusiveEnd do: 
        [:i |
         selfElement := self at: i.
         otherElement  := aCharacterCollection at: i.
         ((noCaseBoolean _and: [selfElement isEquivalent: otherElement]) 
              _or: [noCaseBoolean not _and: [selfElement = otherElement]])
             ifFalse:
                 [(selfElement < otherElement) ifTrue: 
                      [^-1].
                  (selfElement > otherElement) ifTrue: 
                      [^1]]].
    ^0
%
category: 'Adding'
method: CharacterCollection
add: aCharOrCharColl

"Appends all of the elements of aCharOrCharColl to the receiver and returns
 aCharOrCharColl."

"Note: In GemStone 4.1, this method returned the receiver."

| index |

(aCharOrCharColl isKindOf: Collection) 
  ifTrue:[
    index := self size.
    aCharOrCharColl do: [ :aChar |
      index := index + 1.
      self at: index put: aChar.
      ].
    ^ aCharOrCharColl.
    ].

^ self at: (self size + 1) put: aCharOrCharColl.
%

category: 'Adding'
method: CharacterCollection
addAll: aCharOrCharCollection

"Equivalent to add: aCharOrCharCollection."

self add: aCharOrCharCollection
%

category: 'Adding'
method: CharacterCollection
addLast: aCharOrCharCollection

"Equivalent to add: aCharOrCharCollection."

self add: aCharOrCharCollection
%

category: 'Converting'
method: CharacterCollection
asArrayOfKeywords

"Returns an Array of keyword substrings held by the receiver.  The receiver
 is assumed to be a colon-separated list of substrings.  These substrings
 are extracted and collected in an Array.  If the receiver contains no
 colons, the Array will hold a copy of the receiver."

| c nextName result |

result := #[].
nextName := self speciesForConversion new.
1 to: self size do: [ :i |
  c := self at: i.
  nextName add: c.
  c == $: ifTrue: [
    result add: nextName.
    nextName := self speciesForConversion new.
  ].
].
nextName size > 0 ifTrue: [
  result add: nextName
]
ifFalse: [
  result size == 0 ifTrue: [result add: nextName]
].
^result
%

category: 'Converting'
method: CharacterCollection
asArrayOfSubstrings

"Returns an Array of substrings held by the receiver. The receiver
 is assumed to be a separator-separated list of substrings.  These substrings
 are extracted and collected in an Array.  If the receiver contains no
 separators, the Array will hold a copy of the receiver.  Separators not meant
 to separate substrings may be escaped with a $\ Character."

| nextName result esc c |

result := Array new.
self size == 0 ifTrue: [
  ^result
].
nextName := self speciesForConversion new.
esc := false.
1 to: self size do:
  [ :i |
  c := self at: i.
  esc ifTrue:
    [
    nextName add: c.
    esc := false.
    ]
  ifFalse:
    [
    c == $\ ifTrue:
	[ esc := true ]
    ifFalse:
	[
	c isSeparator ifTrue:
          [
        nextName size > 0 ifTrue: [result add: nextName].
          nextName := self speciesForConversion new.
          ]
	ifFalse:
          [ nextName add: c ].
        ].
    ].
  ].

esc ifTrue:
  [ nextName add: $\ ].

(nextName size > 0 _or: [result size == 0]) ifTrue:
  [ result add: nextName ].

^result
%

category: 'Converting'
method: CharacterCollection
asDecimalFloat

"Returns a DecimalFloat whose value is represented by the receiver."

^ DecimalFloat fromString: self
%

category: 'Converting'
method: CharacterCollection
asDoubleByteString

"Returns a DoubleByteString representation of the receiver."

^ DoubleByteString withAll: self.
%

category: 'Converting'
method: CharacterCollection
asFloat

"Returns a SmallDouble or Float whose value is represented by the receiver."

^ Float fromString: self
%

category: 'Converting'
method: CharacterCollection
asInteger

"Returns an Integer whose value is represented by the receiver."

^ Integer fromString: self
%

! fix  33681 , 36647
category: 'Private'
method: CharacterCollection
_trimReturningSelf: aBool

"If aBool is true and the receiver has no leading or trailing white
 space, this returns the receiver.  If aBool is false and the receiver
 has no leading or trailing white space, this returns a copy of the receiver.
 However, if there is white space this returns a trimmed copy of the receiver
 no matter what the value of aBool."

| first last done ind sentinel selfSize |

((selfSize := self size) == 0) ifTrue: [
  aBool ifTrue: [^self]
       ifFalse: [^self class _newString ]
].

sentinel := selfSize + 1.
done := false.
ind := 1.
first := 0.

(self at: 1) isSeparator
  ifTrue: [
    ind := 2.
    [done _or: [ind == sentinel]] whileFalse: [
       (self at: ind) isSeparator ifFalse: [
           done := true.
           first := ind.
       ].
       ind := ind + 1.
    ].
  ]
  ifFalse: [first := 1].

first == 0 ifTrue: [^self class _newString ].

last := selfSize.
(self at: selfSize) isSeparator ifTrue: [
  done := false.
  ind := selfSize - 1.
  [done] whileFalse: [
     (self at: ind) isSeparator
       ifFalse: [^self copyFrom: first to: ind ].
     ind := ind - 1.
     (ind == 0)
        ifTrue: [^self class _newString ].
  ].
].

 ((first == 1) _and: [last == (selfSize)])
  ifTrue: [
    aBool
    ifTrue: [^self]
    ifFalse: [^self copy]
  ]
  ifFalse: [^self copyFrom: first to: last].
%

category: 'Formatting'
method: CharacterCollection
trimWhiteSpace

"Returns a copy of the receiver with leading and trailing white space removed."

^self _trimReturningSelf: false
%

category: 'Converting'
method: CharacterCollection
asNumber

"Returns the receiver converted to a kind of number.  If the receiver contains
 all digits (with optional radix notation), returns a kind of Integer.  If the
 receiver has a slash, returns a Fraction.  Otherwise conversion to a Float is
 attempted.  An error may result if the receiver does not contain the proper
 format for a kind of Number."

| v zero nine str idx |
str := self trimWhiteSpace.
str size == 0 ifTrue: [ ^0 ].
zero := $0 asciiValue.
nine := $9 asciiValue.
idx := 1.
(str at: 1) = $- ifTrue: [
  idx := 2
]
ifFalse: [
  (str at: 1) = $+ ifTrue: [
    idx := 2
  ]
].
idx to: str size do: [:i |
  v := (str at: i) asciiValue.
  (zero <= v _and: [v <= nine]) ifFalse: [
    (v == ($# asciiValue) _or: [ v == $r asciiValue ]) ifTrue: [
      ^ Integer fromString: str .
    ] ifFalse:[
      v == ($/ asciiValue) ifTrue: [
        ^Fraction fromStream: (ReadStream on: str)
      ].
      ^Float fromStream: (ReadStream on: str)
    ]
  ]
].
^Integer fromString: str
%

category: 'Converting'
method: CharacterCollection
asJISString

"Returns a JISString representation of the receiver."

| selfSize aJISString |

selfSize := self size.
aJISString := JISString new: selfSize.
1 to: selfSize do: [ :i |
  aJISString at: i put: (self at: i) asJISCharacter.
  ].

^ aJISString.
%

category: 'Converting'
method: CharacterCollection
asUppercase

"Returns a new instance of the receiver's class, with all lower-case
 characters in the receiver changed to upper-case."

| result |
result:= self species new: (self size).
1 to: (self size)
  do: [:i | result at: i put: (self at: i) asUppercase ].
^ result
%

category: 'Converting'
method: CharacterCollection
asLowercase

"Returns a new instance of the receiver's class, with all upper-case
 characters in the receiver changed to lower-case."

| result |
result:= self species new: (self size).
1 to: (self size)
  do: [:i | result at: i put: (self at: i) asLowercase ].
^ result
%

category: 'Converting'
method: CharacterCollection
asSmallFloat

"Deprecated .
 Returns a SmallDouble or Float whose value is represented by the receiver."

^ Float fromString: self
%

category: 'Converting'
method: CharacterCollection
asString

"Returns a String representation of the receiver."

^ String withAll: self.
%
category: 'Converting'
method: CharacterCollection
asHexString

"Returns a String containing a hexadecimal printed representation of the
 contents of the receiver.  For example, the message 'abc' asHexString
 returns the String '616163'.

 The receiver must be a byte format object."

<primitive: 467>
self _validateByteClass: CharacterCollection .
self _primitiveFailed: #asHexString .
%

category: 'Converting'
method: CharacterCollection
asSymbolKind

"Returns a canonical symbol containing the same Characters as the receiver."

CharacterCollection subclassResponsibility: #asSymbolKind
%

category: 'Comparing'
method: CharacterCollection
at: anIndex equals: aCharCollection

"Returns true if aCharCollection is contained in the receiver starting at
 anIndex.  Returns false otherwise.

 Note that this method returns true only if aCharCollection begins exactly at
 the position designated by anIndex.  To locate a pattern beginning on or after
 anIndex, see the method findPattern:startingAt: in category 'Searching'."

   | argSize argIndex |
   "check for errors"
   aCharCollection _validateClass: CharacterCollection.
   anIndex _validateClass: SmallInteger.
   ((anIndex <= 0) _or: [anIndex > self size])
   ifTrue: [ ^ self _errorIndexOutOfRange: anIndex ].

   "if argument size is 0 always returns true"
   argSize:= aCharCollection size.
   (argSize == 0)
   ifTrue:
      [ ^ true ].

   "if argument longer than receiver starting at anIndex, returns false"
   ((anIndex + argSize - 1) <= self size)
   ifFalse:
      [ ^ false ].

   "loop through receiver and argument"
   argIndex:= 1.
   anIndex to: (anIndex + (argSize) - 1)
           do: [:i | ((self at: i) = (aCharCollection at: argIndex))
                     ifFalse: [ ^ false ].
                     argIndex:= argIndex + 1
               ].
   ^ true
%

category: 'Accessing'
method: CharacterCollection
byteAt: index

"Considers the receiver as an Array of bytes and returns the byte at position
 index."

^ self _basicAt: index.
%

category: 'Formatting'
method: CharacterCollection
describeClassName

"CharacterCollections may be said to describe themselves, so this method
 returns the receiver."

^ self
%

category: 'Backward Compatibility'
method: CharacterCollection
insert: aCharOrCharCollection at: anIndex

"Obsolete in GemStone 5.0.  Use the insertAll:at: method instead."

^ self insertAll: aCharOrCharCollection at: anIndex.
%

category: 'Adding'
method: CharacterCollection
insertAll: aCharOrCharCollection at: anIndex

"Inserts aCharOrCharCollection into the receiver at the specified index and
 returns aCharOrCharCollection."

(aCharOrCharCollection isKindOf: CharacterCollection)
   ifTrue: [ ^ super insertAll: aCharOrCharCollection at: anIndex ].

(aCharOrCharCollection isKindOf: AbstractCharacter)
  ifTrue: [
    self copyFrom: anIndex 
               to: (self size)
             into: self 
       startingAt: (anIndex + 1).
    self at: anIndex put: aCharOrCharCollection.
    ^aCharOrCharCollection.
    ].

^ aCharOrCharCollection _error: #rtErrInvalidArgClass 
                        args: #[AbstractCharacter, CharacterCollection].
%

category: 'Adding'
method: CharacterCollection
insertObject: anObject at: anIndex

"(R) Inserts anObject into the receiver at index anIndex and returns
 aCollection."

"reimplemented to use at:put: instead of _basicAt:put:"
| selfSize |

anIndex <= 0 ifTrue:[ ^ self _errorIndexOutOfRange: anIndex ].
selfSize := self size.
anIndex > selfSize ifTrue:[
  anIndex > (selfSize + 1) ifTrue:[ ^ self _errorIndexOutOfRange: anIndex].
  ^ self at: anIndex put: anObject.
  ].

"Not adding to the end of the receiver. Create a gap for anObject to
 be copied into."
self copyFrom: anIndex to: selfSize into: self startingAt: anIndex + 1 .

^ self at: anIndex put: anObject.
%

category: 'Testing'
method: CharacterCollection
sameAs: aCharCollection

"Returns true if the receiver is equivalent to aCharCollection.  The receiver
 is equivalent to aCharCollection if the receiver contains the same Characters
 as aCharCollection regardless of case or internal representation.  For
 example, if $a is in aCharCollection, it is equivalent to any representation
 of an 'a' in the receiver's character set."

(aCharCollection isKindOf: CharacterCollection)
  ifFalse: [ ^false ].

(self size ~~ aCharCollection size)
  ifTrue: [ ^false ].

1 to: (self size)
  do: [:i | ((self at: i) isEquivalent: (aCharCollection at: i))
            ifFalse: [ ^false ].
      ].
^true
%
category: 'Testing'
method: CharacterCollection
equalsNoCase: aCharCollection
    "Returns true if the receiver is equivalent to aCharCollection.
    The receiver is equivalent to aCharCollection if the receiver
    contains the same Characters as aCharCollection regardless of case
    or internal representation.  For example, if $a is in
    aCharCollection, it is equivalent to any representation of an 'a'
    in the receiver's character set."

    ^self sameAs: aCharCollection
%
category: 'Testing'
method: CharacterCollection
isEquivalent: aCharCollection

"Returns true if the receiver is equivalent to aCharCollection.  The receiver
 is equivalent to aCharCollection if the receiver contains the same Characters
 as aCharCollection regardless of case or internal representation.  For
 example, if $a is in aCharCollection, it is equivalent to any representation
 of an 'a' in the receiver's character set."

"Note: This method behaves the same as #sameAs:"

^ self sameAs: aCharCollection
%
! fixed 13411

category: 'Updating'
method: CharacterCollection
lf

"Appends a line-feed to the receiver and returns the receiver."

self add: Character lf
%

! merge fix from 6.x,   use "self class with:"
category: 'Updating'
method: CharacterCollection
addLineDelimiters

"Returns a copy of the receiver that contains each occurrence of the backslash 
 Character replaced by the line-feed Character."

^ self copyReplaceAll: '\' with: (self class with: Character lf).
%

category: 'Updating'
method: CharacterCollection
byteAt: index put: newElement

"Considers the receiver as an Array of bytes and sets the byte at position 
 index to newElement.  The argument newElement replaces any previously stored 
 value and must be an Integer between 0 and 255."

^ self _basicAt: index put: newElement.
%

category: 'Comparing'
method: CharacterCollection
matchPattern: aPattern

"Returns true if the receiver matches aPattern, false if it doesn't.  An exact
 match is required.  For partial matching, use the 'Searching' method
 findPattern:startingAt: instead.

 The argument aPattern is a kind of Array containing zero or more
 CharacterCollections, plus zero or more occurrences of the special Characters
 $* or $?.  If either $* or $? occurs in aPattern, it acts as a wild card.
 The Character $? matches any single Character in the receiver, and $* matches
 any sequence of zero or more Characters in the receiver.  For example,

 'weimaraner' matchPattern: #('w' $* 'r')

 returns true, because the Character $* is interpreted as a wild card.

 If either of these special Characters occurs in the receiver, it is
 interpreted literally.  For example,

 'w*r' matchPattern: #('weimaraner')

 returns false - because the Character $* occurs in the receiver, it is
 interpreted as a literal asterisk (not as a wild card)."

| match         "indicates if the current pattern matched"
  pattern       "the Array of pattern elements"
  selfSize      "the size of the receiver"
  patternSize   "the number of elements in the pattern Array"
  startIndexArr "an Array of indexes into the receiver; this Array is
                 parallel to the pattern Array (each element in this Array
                 corresponds to the starting index for each pattern element)"
  index         "index into the pattern Array and the startIndexArr Array"
  thisPattern   "the current element of the pattern to match"
  selfIndex     "an index into the receiver"
|

 "The pattern Array must be processed so that there are no *? pairs in it.
 They must all be converted to ?* pairs for the algorithm to work correctly."
 pattern:= Array withAll: aPattern.
 patternSize:= pattern size.
 index := 1.
 [ index < patternSize ]
 whileTrue:
 [ (((pattern at: index) isEquivalent: $* ) 
    _and:[ (pattern at: index+1) isEquivalent: $? ]) 
   ifTrue:[ 
     pattern at: index put: $?.
     pattern at: index+1 put: $*.
     index := 1 max: index-1.
   ]
   ifFalse:
   [ index := index + 1 ].
 ].

 "initialize"
 selfSize := self size.
 startIndexArr:= Array new: (patternSize + 1).
                 "last element is set, but not used"
 index := 1.
 startIndexArr at: 1 put: 1.

 "if no patterns to match, exit early"
 (patternSize == 0)
 ifTrue:
    [^ selfSize == 0 ].

 [index <= patternSize]
 whileTrue:
 [ thisPattern := pattern at: index.
   selfIndex := startIndexArr at: index.
   match := true.

   (thisPattern isKindOf: CharacterCollection) "pattern to match is a string"
   ifTrue:
   [ (selfIndex + thisPattern size - 1) > selfSize
     ifTrue: "this pattern too big to match rest of receiver"
        [^ false ].

     index = patternSize "processing the final pattern"
     ifTrue:
     [ ((index > 1) _and: [ (pattern at: index - 1) isEquivalent: $* ])
       ifTrue: "is not the first pattern _and_ previous pattern was a $*"
       [ ((selfSize == 0) _and: [thisPattern size == 0])
         ifTrue: [^ true].
         ^(self findString: thisPattern
                startingAt: selfSize - thisPattern size + 1) ~~ 0
         "find the pattern far enough back in the string so that only
         the final chars match"
       ]
       ifFalse: "processing first pattern _or_ previous pattern was not $*"
       [ (match:= (selfIndex + thisPattern size - 1 = selfSize))
         ifTrue: "exactly enough chars in self to match thisPattern"
         [ (selfSize == 0)
           ifTrue: [match:= (thisPattern size == 0)]
           ifFalse: [match:= self at: selfIndex equals: thisPattern ].
         ]
       ].
     ]
     ifFalse: "not processing the final pattern"
     [ ((index > 1) _and: [ (pattern at: index - 1) isEquivalent: $* ])
       ifTrue: "not first pattern _and_ previous pattern was $*"
       [ (((selfSize == 0) _and: [thisPattern size == 0]) _or:
         [(selfIndex:= self findString: thisPattern
                           startingAt: selfIndex) > 0])
         ifTrue: "thisPattern was found"
         [ startIndexArr at: index put: selfIndex.
           startIndexArr at: index + 1 put: selfIndex + thisPattern size.
         ]
         ifFalse: "thisPattern was not found"
            [^ false ]
       ]
       ifFalse: "first pattern _or_ previous pattern was not $*"
       [ (((selfSize == 0) _and: [thisPattern size == 0]) _or:
         [(self at: selfIndex equals: thisPattern)])
         ifTrue:
            [startIndexArr at: index + 1 put: selfIndex+ thisPattern size]
         ifFalse:
            [match := false ].
       ].
     ]
   ]
   ifFalse: "thisPattern is not a string"
   [ (thisPattern isEquivalent: $*)
     ifTrue:
        [startIndexArr at: (index + 1) put: selfIndex]
     ifFalse:
     [ (thisPattern isEquivalent: $?)
       ifTrue:
       [ selfIndex > selfSize
         ifTrue: "no char to match; already at end of self"
            [^ false ].
         startIndexArr at: (index + 1) put: (selfIndex + 1).
         index = patternSize "processing the last pattern"
         ifTrue:
            [match := selfIndex = selfSize.].
       ]
       ifFalse: "next pattern is neither a $* or $?"
       [ ^ aPattern _error: #rtErrBadPattern].
     ].
   ].  "end ifTrue:ifFalse"

   match
   ifTrue:
      [index := index + 1 ] "advance to the next term in the pattern"
   ifFalse:
   [ "If there is a preceding $* term in the pattern, backup to the
      term following it, and advance position in the string by 1."
      [ index := index - 1.
        index < 2 ifTrue:
           [^ false ].
        (pattern at: index - 1) isEquivalent: $*
      ] untilTrue.
      startIndexArr at: index put: ((startIndexArr at: index) + 1).
   ].
].  "end whileTrue:"

^ true
%

category: 'Comparing'
method: CharacterCollection
matchesAnyOf: aCollectionOfCharacterColls

"Returns true if the receiver returns true to the message match: with any of
 the objects in the given collection; returns false otherwise.  Examples:

   'xyz' matchesAnyOf: #('xyz' 'abc*')
     true
   'xyz' matchesAnyOf: #('ayz' 'abc')
     false
   'x#z' matchesAnyOf: #('x@z' '*')
     false

 The class JISString does not support this method."

aCollectionOfCharacterColls do: [:coll |
  (self match: coll) ifTrue: [ ^true ]
].
^false
%

category: 'Comparing'
method: CharacterCollection
match: prefix

"Returns true if the argument prefix is a prefix of the receiver, and
 false if not.  The comparison is case-sensitive."

self size == 0 ifTrue: [ ^ prefix size == 0 ].
^ self at: 1 equals: prefix
%

category: 'Formatting'
method: CharacterCollection
printOn: aStream

"Puts a displayable representation of the receiver on the given stream."

"Should be reimplemented for more efficiency in subclasses."

aStream nextPutAll: self quoted .
%

category: 'Formatting'
method: CharacterCollection
quoted

"Returns a copy of the receiver enclosed in single-quote marks, with contained
 single-quote Characters doubled.  The copy is of the same class as the
 receiver."

| sz result targetIdx lastIdx idx |
sz := self size.
result := self class _newString: sz + 2.
result at: 1 put: $'.
targetIdx := 2.
lastIdx := 1.
[ (idx := self indexOf: $' startingAt: lastIdx) == 0 ] whileFalse: [
  self copyFrom: lastIdx to: idx into: result startingAt: targetIdx.
  targetIdx := targetIdx + (idx - lastIdx) + 2.
  result at: targetIdx - 1 put: $'.
  lastIdx := idx + 1 ].
lastIdx <= sz ifTrue: [
  self copyFrom: lastIdx to: sz into: result startingAt: targetIdx ].
result at: targetIdx + (sz - lastIdx) + 1 put: $'.
^result
%

category: 'Updating'
method: CharacterCollection
space

"Appends a space to the receiver and returns the receiver."

self add: $ .
%

category: 'Private'
method: CharacterCollection
speciesForConversion

"Return the class of the receiver.  Subclasses should reimplement this method."

^ self class.
%

category: 'Updating'
method: CharacterCollection
tab

"Appends a tab to the receiver and returns the receiver."

self add: $	.
^ self.
%

category: 'Backward Compatibility'
method: CharacterCollection
toServerTextFile: aFileSpec

"Obsolete in GemStone 4.1.  Use an instance of GsFile to access the file system
 of the client or server machines."

<primitive: 301>

aFileSpec _validateByteClass: CharacterCollection.
self _error: #hostErrFileExport args: #[aFileSpec]
%

category: 'Formatting'
method: CharacterCollection
width: anInteger

"Pads the receiver with spaces to create an object of size anInteger.
 If anInteger is positive, the spaces are added to the right of the receiver.
 If anInteger is negative, the spaces are added to the left of the receiver.
 If the size of the receiver is already greater than anInteger, the receiver
 is left unchanged."

| onRight s change |

change := anInteger abs - self size.
change > 0
ifTrue:
  [ onRight := anInteger > 0.
    s := (self speciesForConversion) new: change.
    s atAllPut: $ .
    onRight
    ifTrue:
      [ self addAll: s ]
    ifFalse:
      [ self insertAll: s at: 1 ]
  ]
%

category: 'Formatting'
method: CharacterCollection
wrapTo: col 

"Word-wrap the receiver to column col, treating tab Characters as modulo-8."

^ self _wrapTo: col indentingWith: ''
%

category: 'Formatting'
method: CharacterCollection
_wrapTo: col indentingWith: indentStr

"Returns a new instance of the class of the receiver.

 Word-wrap the receiver to column col, treating tab Characters as modulo-8.
 Whenever a line-feed is inserted, prepend indentStr to the subsequent line."

| ch curcol linestart wordstart lf tab str |

lf := Character lf.
tab := Character tab.
curcol := 1.
wordstart := 0.
linestart := 1.
str := self class _newString.

1 to: self size do: [ :i |
  ch := self at: i.
  ch == lf ifTrue: [
    str add: (self copyFrom: linestart to: i).
    linestart := i + 1.
    wordstart := 0.
    curcol := 1.
  ]
  ifFalse: [
    ch isSeparator ifTrue: [
      wordstart := 0
    ]
    ifFalse: [
      wordstart == 0 ifTrue: [
        wordstart := i
      ].
    ].

    ch == tab ifTrue: [
      curcol := (curcol + 8) \\ 8
    ]
    ifFalse: [
      curcol := curcol + 1
    ].

    curcol > col ifTrue: [
      (wordstart == 0 _or: [linestart == wordstart]) ifTrue: [
	str add: (self copyFrom: linestart to: i).
	linestart := i + 1.
	curcol := 1.
      ]
      ifFalse: [
	str add: (self copyFrom: linestart to: wordstart - 1).
	linestart := wordstart.
	curcol := i - wordstart + 1.
      ].
      str add: lf.
      str add: indentStr .
      curcol := curcol + indentStr size . 
    ].
  ].
].

linestart <= self size ifTrue: [
  str add: (self copyFrom: linestart to: self size)
].

^str.
%

! fix bug 11542
! fixed bug 35799
category: 'Copying'
method: CharacterCollection
copyFrom: startIndex to: stopIndex

"Returns a new SequenceableCollection containing the elements of the receiver
 between startIndex and stopIndex, inclusive.  The result is of the same class
 as the receiver, unless the receiver is a Symbol or DoubleByteSymbol,
 in which case the result class is respectively String or DoubleByteString.

 Both startIndex and stopIndex must be positive integers not larger than the
 size of the receiver, with startIndex <= stopIndex.
 If startIndex > stopIndex and both are positive, an empty collection is returned.
 "

| result |
(startIndex > stopIndex) ifTrue: [ 
  stopIndex < 0 ifTrue:[ self _error: #rtErrBadCopyFromTo args: #[stopIndex] ].
  ^ self class new 
].
(startIndex < 1)
   ifTrue: [ ^ self _errorIndexOutOfRange: startIndex].

((stopIndex > self size) _or: [(stopIndex < 0)])
   ifTrue: [ ^ self _errorIndexOutOfRange: stopIndex].

result := (self class _newString: (stopIndex - startIndex + 1)).
self copyFrom: startIndex to: stopIndex into: result startingAt: 1.
^ result
%

category: 'Copying'
method: CharacterCollection
copyFrom: index1 to: index2 into: aSeqCollection startingAt: destIndex

"(R) Copies the elements of the receiver between index1 and index2, inclusive,
 into aSeqCollection starting at destIndex, overwriting the previous contents.
 If aSeqCollection is the same object as the receiver, the source and
 destination blocks may overlap.  Returns the receiver."

"Reimplemented to use at:, not _basicAt:"

| i |

(index1 > index2)
  ifTrue: [ ^ index1 _error: #rtErrBadCopyFromTo args: #[index2]].

(index1 < 1)
  ifTrue: [ ^ self _errorIndexOutOfRange: index1].

(index2 > self size)
  ifTrue: [ ^ self _errorIndexOutOfRange: index2].

((destIndex < 1) _or: [(destIndex > (aSeqCollection size + 1))])
   ifTrue: [ ^ aSeqCollection _errorIndexOutOfRange: destIndex].

((self == aSeqCollection) & (destIndex >= index1) & (destIndex <= index2))
  ifTrue: [
    "extend size of collection first if needed and copy from back to front "
    ((destIndex + index2 - index1) > aSeqCollection size) 
      ifTrue: [ aSeqCollection size: (destIndex + index2 - index1) ].

    i := destIndex + (index2 - index1).
    index2 downTo: index1 do: [ :j |
      self at: i put: (self at: j).
      i := i - 1.
      ].
    ]
  ifFalse: [
    i := destIndex.
    index1 to: index2 do: [ :j |
      aSeqCollection at: i put: (self at: j).
      i := i + 1.
      ].
  ].

^ self.
%

category: 'Copying'
method: CharacterCollection
copyWithout: anObject

"Returns a copy of the receiver that does not contain the given object.
 Comparisons are by equality."

"Note: In GemStone 4.1, the comparisons were by identity."

| copy element sz |

copy := self class _newString .

sz := 0.
1 to: self size do: [:i |
  element := self at: i.
  (element = anObject)
    ifFalse: [
      sz := sz + 1.
      copy at: sz put: element.
      ]
  ].

^copy
%

category: 'Case-Sensitive Searching'
method: CharacterCollection
findString: subString startingAt: startIndex

"If a receiver contains subString beginning at some point at or after
 startIndex, this returns the index at which subString begins.  If the
 receiver does not contain subString, this returns 0.

 The search is case-sensitive."

^ self _findString: subString startingAt: startIndex ignoreCase: false
%

category: 'Case-Insensitive Searching'
method: CharacterCollection
includesString: aString

"Returns true if aString is contained as a subString within the receiver,
 using a case-insensitive search.  Returns false otherwise."

^ (self _findString: aString startingAt: 1 ignoreCase: true) > 0
%

category: 'Case-Insensitive Searching'
method: CharacterCollection
findStringNoCase: subString startingAt: startIndex

"If a receiver contains subString beginning at some point at or after
 startIndex, this returns the index at which subString begins.  If the
 receiver does not contain subString, this returns 0.

 The search is case-insensitive."

^ self _findString: subString startingAt: startIndex ignoreCase: true
%

category: 'Private'
method: CharacterCollection
_findSmallString: subString startingAt: startIndex ignoreCase: aBoolean

CharacterCollection 
  subclassResponsibility: #_findSmallString:startingAt:ignoreCase: .

self _uncontinuableError
%

category: 'Private'
method: CharacterCollection
_findString: subString startingAt: startIndex ignoreCase: aBoolean

"If a receiver contains subString beginning at some point at or after
 startIndex, this returns the index at which subString begins.  If the
 receiver does not contain subString, this returns 0."

| sub offset limit |
startIndex > self size ifTrue: [^0]. "Fix for bug 15038"

subString size <= 4048 "virtual machine constant" ifTrue:[
  ^ self _findSmallString: subString startingAt: startIndex
               ignoreCase: aBoolean
  ].
sub := subString copyFrom: 1 to: 4048 "virtual machine constant" .
offset := 1 .
limit := self size - (subString size - 1) .
[ offset <= limit ] whileTrue:[
  offset := self _findSmallString: sub startingAt: offset ignoreCase: aBoolean.
  offset > 0 ifTrue:[
    (self _at: offset equals: subString ignoreCase: aBoolean)
      ifTrue:[ ^ offset ]
      ifFalse:[ offset := offset + 1 ].
    ].
  ].
^ 0
%

category: 'Searching'
method: CharacterCollection
findPattern: aPattern startingAt: anIndex 

"This method searches the receiver, beginning at anIndex, for a substring that
 matches aPattern.  If a matching substring is found, this method returns the
 index of the first Character of the substring.  Otherwise, this returns 0.

 The argument aPattern is an Array containing zero or more CharacterCollections
 plus zero or more occurrences of the special Characters asterisk or
 question-mark.  See the description of the matchPattern: method for more
 information about this argument.

 Performs a case-sensitive search."

^ self _findPattern: aPattern startingAt: anIndex ignoreCase: false
%

category: 'Searching'
method: CharacterCollection
findPatternNoCase: aPattern startingAt: anIndex 

"This method searches the receiver, beginning at anIndex, for a substring that
 matches aPattern.  If a matching substring is found, this method returns the
 index of the first Character of the substring.  Otherwise, this returns 0.

 The argument aPattern is an Array containing zero or more CharacterCollections
 plus zero or more occurrences of the special Characters asterisk or
 question-mark.  See the description of the matchPattern: method for more
 information about this argument.

 Performs a case-insensitive search."

^ self _findPattern: aPattern startingAt: anIndex ignoreCase: true
%

category: 'Searching'
method: CharacterCollection
_findPattern: aPattern startingAt: anIndex ignoreCase: caseInsens

"This method searches the receiver, beginning at anIndex, for a substring that
 matches aPattern.  If a matching substring is found, this method returns the
 index of the first Character of the substring.  Otherwise, this returns 0.

 The argument aPattern is an Array containing zero or more CharacterCollections
 plus zero or more occurrences of the special Characters asterisk or
 question-mark.  See the description of the matchPattern: method for more
 information about this argument.

 If caseInsens is true, a case-insensitive search is performed.  Otherwise,
 caseInsens should be false and a case-sensitive search is performed."

| i             "loop counter"
  pattern       "the argument aPattern, converted to an Array"
  selfSize      "the size of the receiver"
  patternSize   "the number of elements in the pattern Array"
  startIndex    "an Array that corresponds to the pattern Array, with each
                 element containing the starting index into the receiver of
                 the corresponding pattern"
  index         "the index into both the pattern and the startIndex Arrays"
  next          "the current pattern (element of the pattern Array) to
                 be matched"
  cursor        "the index into the receiver"
|

aPattern _validateClass: Array.
(anIndex <= 0) ifTrue: [ ^ self _errorIndexOutOfRange: anIndex ].

pattern := Array withAll: aPattern.
patternSize := pattern size.

" First, the pattern Array must be processed so that there are no *? pairs
  it in.  They must all be converted to ?* pairs for the algorithm to work
  correctly."
i := 1.
[ i < patternSize ]
whileTrue:
  [ ((pattern at: i) isEquivalent: $*) & ((pattern at: i+1) isEquivalent: $?)
    ifTrue:
      [ pattern at: i put: $?.
        pattern at: i+1 put: $*.
        i := 1 max: i-1.
      ]
    ifFalse:
      [ i := i + 1 ].
  ].

"initialize"
selfSize := self size.
startIndex := Array new: (patternSize + 1)."last element is set, but not used"
index := 1.
(anIndex > self size)
   ifTrue: [startIndex at: 1 put: (self size + 1)] "Fix for bug 15038"
   ifFalse: [startIndex at: 1 put: anIndex].

(patternSize == 0) "no pattern to match"
ifTrue:
  [ startIndex <= selfSize
    ifTrue:
      [ ^ startIndex ]
    ifFalse:
      [ ^ 0 ].
  ].

[index <= patternSize]
whileTrue:
  [ next := pattern at: index.
    cursor := startIndex at: index.

    (next isKindOf: CharacterCollection) "pattern element is a string"
    ifTrue:
      [ ((cursor > selfSize) _or:
        [(cursor + next size - 1) > selfSize]) "pattern element too big to "
        ifTrue: "match beginning at cursor location"
          [ ^ 0 ].
        ((index == 1) _or: [ (pattern at: index - 1) isEquivalent: $* ])
        ifTrue: "is first pattern or end of *; can skip chars to find match"
          [ cursor := self _findString: next startingAt: cursor 
				ignoreCase: caseInsens .
            (cursor == 0)
            ifTrue:
              [ ^ 0 ]
            ifFalse:
              [ startIndex at: index put: cursor.
                startIndex at: index + 1 put: cursor + next size.
              ]
          ]
        ifFalse: "can't skip chars to find match"
          [ (self _at: cursor equals: next ignoreCase: caseInsens )
            ifTrue:
              [ startIndex at: index + 1 put: cursor + next size ]
            ifFalse:
              [
                [ (index := index - 1) < 1
                  ifTrue:
                    [ ^ 0 ].
                  ((pattern at: index) isKindOf: CharacterCollection) _or:
                  [ (index == 1) _or: [ (pattern at: index - 1) isEquivalent: $* ] ]
                ]
                untilTrue.
                startIndex at: index put: ((startIndex at: index) + 1).
                index := index - 1.
              ].
          ].
      ]
    ifFalse: "pattern element not a string"
      [ (next isEquivalent: $*) "pattern element is *"
        ifTrue:
          [ startIndex at: (index + 1) put: cursor
          ]
        ifFalse:
          [ (next isEquivalent: $?)  "pattern element is ?"
            ifTrue:
              [ cursor > selfSize
                ifTrue:
                  [ ^ 0 ].
                startIndex at: (index + 1) put: (cursor + 1)
              ]
            ifFalse: "found a pattern element other than ?, * or string"
              [ ^ aPattern _error: #rtErrBadPattern
              ].
          ].
      ].

    index := index + 1
  ].

^ startIndex at: 1
%

category: 'Searching'
method: CharacterCollection
indexOf: pattern matchCase: flag startingAt: startIndex

"Searches the receiver, beginning at anIndex, for a substring that
 matches aPattern.  If a matching substring is found, returns the
 index of the first Character of the substring.  Otherwise, returns 0.

 The argument pattern is an Array containing zero or more CharacterCollections
 plus zero or more occurrences of the special Characters asterisk or
 question-mark.  See the description of the matchPattern: method for more
 information about this argument.

 If the flag argument is true, a case-sensitive search is performed.  Otherwise,
 a case-insensitive search is performed."

^ self _findPattern: pattern startingAt: startIndex ignoreCase: (flag not).
%

category: 'Searching'
method: CharacterCollection
maxConsecutiveSubstring

"Returns the largest substring within the receiver that contains Characters with
 consecutive ASCII values.  For example, the message

   'abxabcdxabc' maxConsecutiveSubstring

 yields the result 'abcd'.

 If there are no such substrings larger than 2 Characters, returns a String that
 contains the first Character in the receiver."

| mySize bigSize bigStart aStart aSize lastVal thisVal |

mySize := self size .
mySize < 2 ifTrue:[ ^ self ].
bigSize := 1 .
bigStart := 1 .
aStart := 1 .
aSize := 1 .
lastVal := (self at: 1) asciiValue  .
2 to: mySize do:[:j |
  lastVal := lastVal + 1 .
  thisVal := (self at: j) asciiValue .
  thisVal == lastVal 
    ifTrue:[ aSize := aSize + 1 ]
    ifFalse:[
      aSize > bigSize ifTrue:[
	bigStart := aStart .
	bigSize := aSize .
	].
      aSize := 1 .
      lastVal := thisVal .
      aStart := j .
      ].
   ].
aSize > bigSize ifTrue:[
  bigStart := aStart.
  bigSize := aSize.
  ].
^ self copyFrom: bigStart to: (bigStart + bigSize - 1) .
%

category: 'Searching'
method: CharacterCollection
maxRepeatingSubstring

"Returns the largest substring within the receiver that contains repetitions of
 a Character, using case-sensitive comparison.  For example, the message

   'aaxbbbBxccc' maxRepeatingSubstring

 yields the result 'bbb'.

 If there are no such substrings larger than 1 Character, returns a String that
 contains the first Character in the receiver."

| mySize bigSize bigStart aStart aSize lastChar |

mySize := self size .
mySize < 2 ifTrue:[ ^ self ].
bigSize := 1 .
bigStart := 1 .
aStart := 1 .
aSize := 1 .
lastChar := self at: 1 .
2 to: mySize do:[:j | | thisChar |
  thisChar := self at: j .
  thisChar == lastChar 
    ifTrue:[ aSize := aSize + 1 ]
    ifFalse:[
      aSize > bigSize ifTrue:[
	bigStart := aStart .
	bigSize := aSize .
	].
      aSize := 1 .
      lastChar := thisChar .
      aStart := j .
      ].
   ].
aSize > bigSize ifTrue:[
  bigStart := aStart.
  bigSize := aSize.
  ].
^ self copyFrom: bigStart to: (bigStart + bigSize - 1) .
%

category: 'Private'
method: CharacterCollection
_deepCopyWith: copiedObjDict

""

| copy |

copy := copiedObjDict at: self otherwise: nil.
copy ~~ nil ifTrue: [ ^ copy ].

^ self copy.
%

category: 'Searching'
method: CharacterCollection
maxSameTypeSubstring

"Returns the largest substring within the receiver that contains either all
 digits, all alphabetic characters, or all special characters.  For example, the
 message

   'axv2435,.-' maxSameTypeSubstring

 yields the result '2435'.

 If there are no such substrings larger than 1 Character, returns a String that
 contains the first Character in the receiver.

 This method may generate an error if the receiver is a JapaneseString."

| mySize bigSize bigStart aStart aSize lastType |

mySize := self size .
mySize < 2 ifTrue:[ ^ self ].
bigSize := 1 .
bigStart := 1 .
aStart := 1 .
aSize := 1 .
lastType := (self at: 1) _type  .
2 to: mySize do:[:j | | thisType |
  thisType := (self at: j) _type .
  thisType == lastType 
    ifTrue:[ aSize := aSize + 1 ]
    ifFalse:[
      aSize > bigSize ifTrue:[
	bigStart := aStart .
	bigSize := aSize .
	].
      aSize := 1 .
      lastType := thisType .
      aStart := j .
      ].
   ].
aSize > bigSize ifTrue:[
  bigStart := aStart.
  bigSize := aSize.
  ].
^ self copyFrom: bigStart to: (bigStart + bigSize - 1) .
%

category: 'Converting'
method: CharacterCollection
trimLeadingSeparators

"Returns a CharacterCollection containing the same Characters as the receiver,
 but with leading separators removed."

| startIndex |

self isEmpty ifTrue: [ ^ self ].
(self at: 1) isSeparator ifFalse: [ ^ self ].

startIndex := 2.
[(startIndex <= self size) _and: [(self at: startIndex) isSeparator]]
  whileTrue: [ startIndex := startIndex + 1 ].

(startIndex <= self size)
  ifTrue: [ ^ self copyFrom: startIndex to: self size ].

^ ''.
%

category: 'Converting'
method: CharacterCollection
trimLeadingBlanks

"Returns a CharacterCollection containing the same Characters as the receiver,
 but with leading blanks removed."

| startIndex blank |

self isEmpty ifTrue: [ ^ self ].
blank := Character space.
((self at: 1) == blank) ifFalse: [ ^ self ].

startIndex := 2.
[(startIndex <= self size) _and: [((self at: startIndex) == blank)]]
  whileTrue: [ startIndex := startIndex + 1 ].

(startIndex <= self size)
  ifTrue: [ ^ self copyFrom: startIndex to: self size ].

^ ''.
%

category: 'Converting'
method: CharacterCollection
trimTrailingSeparators

"Returns a CharacterCollection containing the same Characters as the receiver,
 but with trailing separators removed."

| endIndex selfSize |

self isEmpty ifTrue: [ ^ self ].
selfSize := self size.

(self at: selfSize) isSeparator ifFalse: [ ^ self ].

endIndex := (selfSize - 1).
[(endIndex >= 1) _and: [(self at: endIndex) isSeparator]]
  whileTrue: [ endIndex := endIndex - 1 ].

(endIndex >= 1)
  ifTrue: [ ^ self copyFrom: 1 to: endIndex ].

^ ''.
%

category: 'Converting'
method: CharacterCollection
trimTrailingBlanks

"Returns a CharacterCollection containing the same Characters as the receiver,
 but with trailing blanks removed."

| endIndex blank selfSize |

self isEmpty ifTrue: [ ^ self ].
selfSize := self size.
blank := Character space.

((self at: selfSize) == blank) ifFalse: [ ^ self ].

endIndex := (selfSize - 1).
[(endIndex >= 1) _and: [((self at: endIndex) == blank)]]
  whileTrue: [ endIndex := endIndex - 1 ].

(endIndex >= 1)
  ifTrue: [ ^ self copyFrom: 1 to: endIndex ].

^ ''.
%

category: 'Converting'
method: CharacterCollection
trimSeparators

"Returns a CharacterCollection containing the same Characters as the receiver,
 but with leading and trailing separators removed."

self isEmpty ifTrue: [ ^ self ].
^ (self trimLeadingSeparators) trimTrailingSeparators.
%

category: 'Converting'
method: CharacterCollection
trimBlanks

"Returns a CharacterCollection containing the same Characters as the receiver,
 but with leading and trailing blanks removed."

self isEmpty ifTrue: [ ^ self ].
^ (self trimLeadingBlanks) trimTrailingBlanks.
%

category: 'Converting'
method: CharacterCollection
subStrings

"Returns an Array of CharacterCollections where element represents a word in 
 the receiver.  A word is a group of Characters separated by one or more 
 separators."

^ self asArrayOfSubstrings.
%
    
! merge fix 18152  from 6,x     to fix 34079
category: 'Converting'
method: CharacterCollection
subStrings: aCharacter
  "Returns an Array of CharacterCollections in which each element represents a
   substring separated by aCharacter.  The result will include empty substrings when
   two adjacent separators exist, as well as if a separator is the first or last element."

  | result startIndex endIndex |
  result := Array new.
  startIndex := 1.

  [ endIndex := self indexOf: aCharacter startingAt: startIndex.
    endIndex == 0
  ] whileFalse: [
    (endIndex == startIndex) ifTrue:[ result add: self class _newString ].
    (endIndex > startIndex) ifTrue:[
       result add: (self copyFrom: startIndex to: endIndex - 1)
    ].
    startIndex := endIndex + 1
  ].
  (startIndex > self size)
     ifTrue:[ result add:  self class _newString]
     ifFalse:[ result add: (self copyFrom: startIndex to: self size)].

  ^ result.
%


category: 'Converting'
method: CharacterCollection
asArrayOfPathTerms

"Returns an Array of path substrings held by the receiver.  The receiver
 is assumed to be a period-separated list of substrings.  These substrings
 are extracted and collected in an Array.  If the receiver contains no
 periods, the Array will hold a copy of the receiver.  The $\ Character
 is no longer recognized as an escape character.

 Raises an error if an element is not a valid path term."

| nextName result |

result := Array new.
nextName := self speciesForConversion new.

self do: [:c |
  (c == $.) 
    ifTrue: [
      nextName _isValidPathTermName
        ifFalse: [
          ^ self _error: #rtErrInvalidIndexPathExpression
                 args: #[ nextName asSymbol ]
      ].
      result add: nextName asSymbol .
      nextName := self speciesForConversion new.
      ]
    ifFalse: [
      nextName add: c
      ].
].

nextName size > 0 
  ifTrue: [
    nextName _isValidPathTermName
      ifFalse: [
        ^ self _error: #rtErrInvalidIndexPathExpression
               args: #[ nextName asSymbol ]
    ].
    result add: nextName asSymbol
    ]
  ifFalse: [
    result size == 0 
      ifTrue: [result add: nextName asSymbol]
    ].

^result
%

category: 'Testing'
method: CharacterCollection
isValidIdentifier

"Returns true if the receiver is a valid GemStone Smalltalk variable name, 
 and false otherwise."

| c first |

(self size > 64 _or: [self size == 0]) ifTrue: [ ^false ].

first := self at: 1.
(first isLetter _or: [first == $_ ]) ifFalse: [ ^false ].

2 to: self size do: [:i |
  c := self at: i.
  (c isAlphaNumeric _or: [c == $_ ]) ifFalse: [ ^false ].
  ].

^true
%

category: 'Indexing Support'
method: CharacterCollection
_isValidPathTermName

"Returns true if the receiver is a valid term in a path expression."

^ self isValidIdentifier _and: [self ~~ #*]
%

category: 'Accessing'
method: CharacterCollection
numArgs

"Returns the number of arguments the receiver would take, were the receiver
 a message selector."

| numArguments aSubstring |

self isInfix ifTrue: [ ^ 1 ].

numArguments := 0.
aSubstring := self class new.

"Determine the number of keywords."
self do: [ :aChar |
  aSubstring add: aChar.
  (aChar == $:)
    ifTrue: [
      aSubstring isKeyword
        ifTrue: [ numArguments := numArguments + 1 ]
        ifFalse: [ ^ 0 ].
      aSubstring := self class new.
      ].
  ].

(numArguments > 0 _and: [self last ~~ $:]) ifTrue: [^0].
^ numArguments
%

! merge fix from 6.x ,  allow first character to be $_ 
category: 'Testing'
method: CharacterCollection
isKeyword

"Returns true if the receiver is a keyword.  Returns false otherwise."

| mySize firstCh |

mySize := self size.
mySize > 1  ifFalse: [ ^ false ].

firstCh := self at: 1 .
( firstCh isLetter _or: [ firstCh = $_ ] ) ifFalse: [ ^ false ].
2 to: (mySize - 1) do: [ :i | | ch |
  (((ch := self at: i) isAlphaNumeric) or: [ch = $:]) ifFalse: [ ^ false ].
  ].

(self at: mySize) = $: ifFalse: [ ^ false ].

^ true
%

category: 'New Indexing Comparison'
method: CharacterCollection
_classSortOrdinal

^ 10
%
category: 'New Indexing Comparison'
method: CharacterCollection
_idxForCompareGreaterThan: arg

""

^arg _idxForCompareCharacterCollectionGreaterThanSelf: self
%
category: 'New Indexing Comparison'
method: CharacterCollection
_idxForCompareGreaterThanOrEqualTo: arg

""

^arg _idxForCompareCharacterCollectionGreaterThanOrEqualToSelf: self
%
category: 'New Indexing Comparison'
method: CharacterCollection
_idxForCompareLessThan: arg

""

^arg _idxForCompareCharacterCollectionLessThanSelf: self
%
category: 'New Indexing Comparison'
method: CharacterCollection
_idxForCompareLessThanOrEqualTo: arg

""

^arg _idxForCompareCharacterCollectionLessThanOrEqualToSelf: self
%
category: 'New Indexing Comparison'
method: CharacterCollection
_idxForSortEqualTo: arg

""

^arg _idxForSortEqualToCharacterCollection: self
%
category: 'New Indexing Comparison'
method: CharacterCollection
_idxForSortGreaterThan: arg

""

^arg _idxForSortCharacterCollectionGreaterThanSelf: self
%
category: 'New Indexing Comparison'
method: CharacterCollection
_idxForSortGreaterThanOrEqualTo: arg

""

^arg _idxForSortCharacterCollectionGreaterThanOrEqualToSelf: self
%
category: 'New Indexing Comparison'
method: CharacterCollection
_idxForSortLessThan: arg

""

^arg _idxForSortCharacterCollectionLessThanSelf: self
%
category: 'New Indexing Comparison'
method: CharacterCollection
_idxForSortLessThanOrEqualTo: arg

""

^arg _idxForSortCharacterCollectionLessThanOrEqualToSelf: self
%

category: 'New Indexing Comparison'
method: CharacterCollection
_idxForSortNotEqualTo: arg

""

^arg _idxForSortNotEqualToCharacterCollection: self
%

category: 'New Indexing Comparison - for Sort'
method: CharacterCollection
_idxForSortCharacterCollectionGreaterThanOrEqualToSelf: aCharacterCollection

""

^(aCharacterCollection _idxPrimCompareLessThan: self) not
%
category: 'New Indexing Comparison - for Sort'
method: CharacterCollection
_idxForSortCharacterCollectionGreaterThanSelf: aCharacterCollection

""

^aCharacterCollection _idxPrimCompareGreaterThan: self
%
category: 'New Indexing Comparison - for Sort'
method: CharacterCollection
_idxForSortCharacterCollectionLessThanOrEqualToSelf: aCharacterCollection

""

^(aCharacterCollection _idxPrimCompareGreaterThan: self) not
%
category: 'New Indexing Comparison - for Sort'
method: CharacterCollection
_idxForSortCharacterCollectionLessThanSelf: aCharacterCollection

""

^aCharacterCollection _idxPrimCompareLessThan: self
%
category: 'New Indexing Comparison - for Sort'
method: CharacterCollection
 _idxForSortEqualToCharacterCollection: aCharacterCollection

""

    | end max selfSize otherSize |
    (aCharacterCollection isKindOf: CharacterCollection) ifFalse:
        [^false].
    max := RangeEqualityIndex _maxCharsForStringComparison.
    selfSize := self size.
    otherSize := aCharacterCollection size.
    (selfSize < max) & (otherSize < max)
        ifTrue:
            [selfSize ~~ otherSize
                 ifTrue: [^false]
                 ifFalse:
                     [end := selfSize]]
        ifFalse:
            [selfSize := selfSize min: max.
             otherSize := otherSize min: max.
             selfSize ~~ otherSize
                 ifTrue: [^false]
                 ifFalse:
                     [end := selfSize]].
    ^self _compareEqual: aCharacterCollection caseInsensitive: false from: 1 to: end
%

category: 'New Indexing Comparison - for Sort'
method: CharacterCollection
 _idxForSortNotEqualToCharacterCollection: aCharacterCollection

""

^ (self _idxForSortEqualToCharacterCollection: aCharacterCollection) not
%

category: 'New Indexing Comparison - for Compare'
method: CharacterCollection
_idxForCompareCharacterCollectionGreaterThanOrEqualToSelf: aCharacterCollection

""

^(aCharacterCollection _idxPrimCompareLessThan: self) not
%
category: 'New Indexing Comparison - for Compare'
method: CharacterCollection
_idxForCompareCharacterCollectionGreaterThanSelf: aCharacterCollection

""

^aCharacterCollection _idxPrimCompareGreaterThan: self
%
category: 'New Indexing Comparison - for Compare'
method: CharacterCollection
_idxForCompareCharacterCollectionLessThanOrEqualToSelf: aCharacterCollection

""

^(aCharacterCollection _idxPrimCompareGreaterThan: self) not
%
category: 'New Indexing Comparison - for Compare'
method: CharacterCollection
_idxForCompareCharacterCollectionLessThanSelf: aCharacterCollection

""

^aCharacterCollection _idxPrimCompareLessThan: self
%
category: 'New Indexing Comparison - for Compare'
method: CharacterCollection
_idxForCompareEqualToDoubleByteString: aString

""

^self _idxForSortEqualToCharacterCollection: aString
%
category: 'New Indexing Comparison - for Compare'
method: CharacterCollection
_idxForCompareEqualToString: aString

""

^self _idxForSortEqualToCharacterCollection: aString
%

category: 'New Indexing Comparison'
method: CharacterCollection
_idxForCompareNotEqualTo: arg

""

^(self _idxForCompareEqualTo: arg) not
%
category: 'New Indexing Comparison - prims'
method: CharacterCollection
_idxPrimCompareLessThan: arg

"This comparison method is reimplemented by subclasses with more 
 efficient primitive implementations."

^ self < arg
%
category: 'New Indexing Comparison - prims'
method: CharacterCollection
_idxPrimCompareGreaterThan: arg

"This comparison method is reimplemented by subclasses with more 
 efficient primitive implementations."

^ self > arg
%
