!=========================================================================
! Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved.
!
! $Id: pathterm.gs,v 1.22 2008-01-09 22:50:13 stever Exp $
!
! Superclass Hierarchy:
!   PathTerm, Array, SequenceableCollection, Collection, Object.
!
!=========================================================================

! class created in idxclasses.topaz

removeallmethods PathTerm
removeallclassmethods PathTerm

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

| doc |
doc := GsClassDocumentation _newForPrivateGsClass: self.

doc documentClassWith: (GsDocText new).

self description: doc.
%

! ------------------- Class methods for PathTerm
category: 'Instance Creation'
classmethod: PathTerm
new

"Create an initialized instance of the receiver."

^ self basicNew initialize
%

! ------------------- Instance methods for PathTerm
category: 'Error Handling'
method: PathTerm
_errorPathObjectNotAnNsc: anObject

"An object traversed along an index path through a set-valued instance
 variable was not an NSC."

anObject _error: #rtErrPathTermObjectNotAnNsc
%

category: 'Updating Indexes'
method: PathTerm
addDirectMappingFor: anObject logging: aBoolean

"There is a range index directly on the elements of the NSC.  Update the B-tree
 and the dependency list (if the object is a byte object)."

updateBtree ~~ nil
  ifTrue: [
    updateBtree btreeAt: anObject put: anObject.
    ( needsDepList _and: [ nil ~~ anObject ] )
      ifTrue: [ " add an entry in the dependency list "
        anObject
          getDepListAndAddPathTerm: self
          withIVOffset: 1
          logging: aBoolean
      ]
  ].

updateDict ~~ nil
  ifTrue: [ " insert an entry into the index dictionary "
    updateDict _at: anObject put: anObject term: self logging: aBoolean
  ]
%

category: 'Updating Indexes'
method: PathTerm
addMappingsUsing: mapInfo logging: aBoolean

"Adds index dictionary and dependency list entries for the object described by
 the map info object.  If aBoolean is true, operations will be recorded in the
 system redo log."

| pathTerm object nextObj nextMap |
pathTerm := mapInfo pathTerm.
pathTerm size == 0
  ifTrue: [ ^ self ].

object := mapInfo object.

nil == object
  ifTrue: [ ^ self ].

" add an entry to the objects dependency list "
object
  getDepListAndAddPathTerm: pathTerm
  withIVOffset: mapInfo ivOffset
  logging: aBoolean.

" nextObj is the same for all nextMaps, so choose the first one "
nextMap := mapInfo at: 1.
nextObj := nextMap object.

updateBtree ~~ nil
  ifTrue: [ " insert an entry into the B-tree "
    updateBtree btreeAt: nextObj put: object.
    ( needsDepList _and: [ nil ~~ nextObj ] )
      ifTrue: [ " add an entry to the very last object's dependency list "
        nextObj
          getDepListAndAddPathTerm: pathTerm
          withIVOffset: 1
          logging: aBoolean
      ]
  ].
updateDict ~~ nil
  ifTrue: [ " insert an entry into the index dictionary "
    updateDict _at: nextObj put: object term: pathTerm logging: aBoolean
  ].

1 to: mapInfo size do: [ :i |
  nextMap := mapInfo at: i.
  " make recursive call to add mappings "
  nextMap pathTerm ~~ nil
    ifTrue: [ nextMap pathTerm addMappingsUsing: nextMap logging: aBoolean ]
].

" nil out the dependency list to avoid object audit errors "
" mapInfo dependencyList: nil. "
%

category: 'Accessing'
method: PathTerm
_ivOffsetFor: anObject

"Returns the instance variable offset of anObject for the instance variable
 corresponding to the receiver path term."

^ (self _classOf: anObject) _ivOffsetOf: name
%

category: 'Accessing'
method: PathTerm
getRootNsc

"Returns the root NSC for the index in which the receiver is a term."

^ (self at: 1) nscRoot
%

category: 'Accessing'
method: PathTerm
getIndexDictionary

"Returns the index dictionary associated with the receiver."

| indexDict |
updateDict ~~ nil
    ifTrue: [ ^ updateDict ].

" get it from an index object if present "
1 to: self size do: [ :i |
    indexDict := (self at: i) indexDictionary.
    indexDict ~~ nil
        ifTrue: [ ^ indexDict ]
].
^ nil
%

category: 'Updating Indexes'
method: PathTerm
getMappingInfoFor: anObject
ifObject: origObject
atOffset: origOffset
replaceWith: otherObject

"Gathers all the objects, their dependency lists, and the instance variable
 offset for each path term in the tree of path terms.  Puts them in a
 MappingInfo object.  Builds a tree of MappingInfo objects for each child path
 term.

 It is possible that this method is invoked due to an instance variable
 modification.  In that case, we want to traverse the path terms as if the
 modification had occurred.  Thus, if anObject is the one that was modified
 (that is, if it is identical to origObject) and the instance variable offset of
 this path term is the same instance variable offset that was modified (that is,
 it is equal to origOffset), then rather than getting the next object along the
 path, use the otherObject."

| ivOffset nextObj mapInfo aMapInfo |

( nil == anObject _or: [ self size == 0 ] )
    ifTrue: [ ^ MappingInfo new pathTerm: self ].

ivOffset := self _ivOffsetFor: anObject.
ivOffset == nil
    ifTrue: [
        ^ #[ false,  " indicating no indexing objects were modified "
            (ErrorSymbols at: #rtErrObjectInvalidOffset),
            anObject,
            name ]
    ].

mapInfo := MappingInfo new
    object: anObject;
"   dependencyList: (DependencyList for: anObject); "
    ivOffset:ivOffset;
    pathTerm: self.

( anObject == origObject _and: [ ivOffset == origOffset ] )
    ifTrue: [ nextObj := otherObject ]
    ifFalse: [
        " get the next object along the path "
        nextObj := self _nextObjectFor: anObject atInstVar: ivOffset.
    ].

updateBtree ~~ nil
  ifTrue: [
    (self _checkBtreeComparisonWith: nextObj)
      ifFalse: [
        ^ #[ false,  " indicating no indexing objects were modified "
            (ErrorSymbols at: #rtErrRangeEqualityIndexInvalidClassKindForBtree),
            nextObj,
            updateBtree lastElementClass ]
      ]
  ].

( nil == nextObj _or: [ self children isEmpty ] )
    ifTrue: [ " adds nil to the end and returns "
        mapInfo addLast: (MappingInfo new object: nextObj).
        ^ mapInfo
    ].

" make recursive call to get mapping info "
1 to: children size do: [ :i |
    aMapInfo := (children at: i)
        getMappingInfoFor: nextObj
        ifObject: origObject
        atOffset: origOffset
        replaceWith: otherObject.

    (aMapInfo _class == MappingInfo)
        ifFalse: [ ^ aMapInfo ].
    mapInfo addLast: aMapInfo
].
^ mapInfo
%

category: 'Accessing'
method: PathTerm
getParentTerm

"Returns the parent path term of the receiver.  This is determined by looking
 at the previous path term of the first index object that utilizes this path
 term.  If there is no parent, returns nil."

offset > 1
    ifTrue: [ ^ (self at: 1) at: offset - 1 ]
    ifFalse: [ ^ nil ]
%

category: 'Testing'
method: PathTerm
indicatesIndexOnNscElements

"Returns true if the path term indicates that the index is on elements of the
 NSC itself.  This is the case if the path is '' (two single quotes).  This
 method is overridden by SetValuedPathTerm to return true."

^ name ==  #'' 
%

category: 'Testing'
method: PathTerm
indicatesIndexOnRootNsc

"Returns true if the path term indicates that the index is on elements of the
 NSC itself.  This is the case if the path is '' (two single quotes).  This
 method is not overridden by SetValuedPathTerm."

^ name ==  #'' 
%

category: 'Testing'
method: PathTerm
isRangeEqualityIndexLastPathTerm

"Returns true if the receiver is the last path term of a RangeEqualityIndex."

1 to: self size do: [ :i |
    ( (self at: i) isRangeEqualityIndex _and: [ (self at: i) size == offset ] )
        ifTrue: [ ^ true ]
].
^ false
%

category: 'Accessing'
method: PathTerm
name

"Returns the value of the instance variable 'name'."

^name
%

category: 'Updating'
method: PathTerm
name: newValue

"Modify the value of the instance variable 'name'."

name := newValue
%

category: 'Accessing'
method: PathTerm
offset

"Returns the value of the instance variable 'offset'."

^offset
%

category: 'Updating'
method: PathTerm
offset: newValue

"Modify the value of the instance variable 'offset'."

offset := newValue
%

category: 'Accessing'
method: PathTerm
updateBtree

"Returns the value of the instance variable 'updateBtree'."

^updateBtree
%

category: 'Updating'
method: PathTerm
updateBtree: newValue

"Modify the value of the instance variable 'updateBtree'."

updateBtree := newValue
%

category: 'Accessing'
method: PathTerm
updateDict

"Returns the value of the instance variable 'updateDict'."

^updateDict
%

category: 'Updating'
method: PathTerm
updateDict: newValue

"Modify the value of the instance variable 'updateDict'."

updateDict := newValue
%

category: 'Accessing'
method: PathTerm
needsDepList

"Returns the value of the instance variable 'needsDepList'."

^needsDepList
%

category: 'Updating'
method: PathTerm
needsDepList: newValue

"Modify the value of the instance variable 'needsDepList'."

needsDepList := newValue
%

category: 'Updating Indexes'
method: PathTerm
undoDirectMappingFor: anObject logging: doLogging

"There is a range index directly on the elements of the NSC."

updateDict ~~ nil
    ifTrue: [ " remove the index dictionary entry "
        updateDict
            removeKey: anObject
            value: anObject
            term: self
            logging: doLogging
    ]
%

category: 'Updating Indexes'
method: PathTerm
removeDirectMappingFor: anObject logging: doLogging

"There is a range index directly on the elements of the NSC.  Update the
 B-tree."

updateBtree ~~ nil
    ifTrue: [ " remove it from the B-tree "
        (updateBtree btreeRemoveKey: anObject value: anObject)
          "ifFalse: [ updateBtree _errorObjectNotInBtree: anObject value: anObject ]."
    ].

updateDict ~~ nil
    ifTrue: [ " remove the index dictionary entry "
        updateDict
            removeKey: anObject
            value: anObject
            term: self
            logging: doLogging
    ]
%

category: 'Removing'
method: PathTerm
removeIndex: indexObj

"The given index is being removed from this path term."

1 to: self size do: [ :i |
    indexObj == (self at: i)
        ifTrue: [ ^ self deleteObjectAt: i ]
]

%

category: 'Testing'
method: PathTerm
isConstrained

"Returns false: the path term is not constrained."

^ false
%

category: 'Initialization'
method: PathTerm
initialize

"Initialize the receiver with default values."

segments := SegmentSet new.
children := Array new.
needsDepList := false.
%

category: 'Accessing'
method: PathTerm
children

"Returns the value of the instance variable 'children'."

^children
%

category: 'Updating'
method: PathTerm
children: newValue

"Modify the value of the instance variable 'children'."

children := newValue
%

category: 'Updating'
method: PathTerm
_determineChildren

"Determine the children of the receiver by getting the next path term for each
 index object that utilizes the receiver path term."


| indexObj |
children size: 0.
1 to: self size do: [ :i |
    indexObj := self at: i.
    ( indexObj size > offset _and:
    [ (children includesIdentical: (indexObj at: offset + 1)) not ] )
        ifTrue: [ children addLast: (indexObj at: offset + 1) ]
]
%

category: 'Updating Indexes'
method: PathTerm
removeMappingsFor: anObject lastOne: aBoolean logging: doLogging

"Remove entries in the index dictionary and dependency lists for anObject."


| nextObj depList more isLast |
self size == 0
  ifTrue: [ ^ self ].

nextObj := self _nextObjectFor: anObject.

updateBtree ~~ nil
  ifTrue: [
    " remove an entry from the B-tree "
    (updateBtree btreeRemoveKey: nextObj value: anObject).
      "ifFalse: [ updateBtree _errorObjectNotInBtree: nextObj value: anObject ]."
    " remove dependency list entry for next object "
    DependencyList removePathTerm: self for: nextObj.
  ].

more := false.
updateDict ~~ nil
  ifTrue: [
    " remove the index dictionary entry "
    more := updateDict removeKey: nextObj value: anObject term: self logging: doLogging.
  ].

( nil ~~ nextObj _and: [ children isEmpty not ] )
  ifTrue: [ " make recursive call to remove mappings "
    isLast := aBoolean _and: [ more not ].
    1 to: children size do: [ :i |
      (children at: i) removeMappingsFor: nextObj
        lastOne: isLast
        logging: doLogging
    ].
    isLast
      ifTrue: [
        " remove dependency list entries for next object "
        (depList := DependencyList for: nextObj) ~~ nil
          ifTrue: [ depList removePathTerms: children for: nextObj ]
      ]
  ]
%

category: 'Updating Indexes'
method: PathTerm
undoMappingsFor: anObject lastOne: aBoolean logging: doLogging

"Remove entries in the index dictionary and dependency lists for anObject."


| nextObj depList more isLast exception |
self size == 0
  ifTrue: [ ^ self ].

exception := Exception
  category: nil
  number: nil
  do: [ :ex :cat :num :args |
    "Handle any error thrown during execution of _nextObjectFor:
     Cannot resolve the path for this guy --- can't have been added to index,
     in fact this object could have caused the failure in the first place"
    ^ self
].

nextObj := self _nextObjectFor: anObject.

exception remove.

updateBtree ~~ nil
  ifTrue: [
    " remove dependency list entry for next object "
    DependencyList removePathTerm: self for: nextObj.
  ].

more := false.
updateDict ~~ nil
  ifTrue: [
    " remove the index dictionary entry "
    more := updateDict removeKey: nextObj value: anObject term: self logging: doLogging.
  ].

( nil ~~ nextObj _and: [ children isEmpty not ] )
  ifTrue: [ " make recursive call to remove mappings "
    isLast := aBoolean _and: [ more not ].
    1 to: children size do: [ :i |
      (children at: i) undoMappingsFor: nextObj
        lastOne: isLast
        logging: doLogging
    ].
    isLast
      ifTrue: [
        " remove dependency list entries for next object "
        (depList := DependencyList for: nextObj) ~~ nil
          ifTrue: [ depList removePathTerms: children for: nextObj ]
      ]
  ]
%

category: 'Updating Indexes'
method: PathTerm
update: anObject
at: ivOffset
to: newValue

"The instance variable for anObject is being changed to newValue.
 Update the index dictionary and dependency lists for the old value and the
 newValue.  Returns true if the index objects were modified correctly;
 otherwise returns an Array containing error information."


| maps depList more continue occurrences mapInfo doLogging oldValue |

self size == 0
  ifTrue: [ ^ true ].

maps := Array new.
self getIndexDictionary ~~ nil
  ifTrue: [
    " get the mapping info for all next path terms "
    1 to: children size do: [ :i |
      mapInfo := (children at: i)
        getMappingInfoFor: newValue
        ifObject: anObject
        atOffset: ivOffset
        replaceWith: newValue.

      " if the result is not a map info object, it is an Array used
        for error information "
      (mapInfo _class == MappingInfo)
        ifFalse: [ ^ mapInfo ].

      maps addLast: mapInfo
    ]
  ].

doLogging := UnorderedCollection _isRcIndexLoggingEnabled.
oldValue := anObject instVarAt: ivOffset.

occurrences := 0.
updateBtree ~~ nil
  ifTrue: [
    (self _checkBtreeComparisonWith: newValue)
      ifFalse: [
        ^ #[ false,  " indicating no indexing objects were modified "
            (ErrorSymbols at: #rtErrRangeEqualityIndexInvalidClassKindForBtree),
            newValue,
            updateBtree lastElementClass ]
      ].

    " remove B-tree entry for range index "
    [ updateBtree btreeRemoveKey: oldValue value: anObject ] whileTrue: [
      occurrences := occurrences + 1
    ].
    occurrences == 0
      ifTrue: [
        ^ #[ false,  " indicating no indexing objects were modified "
            (ErrorSymbols at: #rtErrRangeEqualityIndexObjectNotInBtree),
            oldValue,
            anObject ]
      ].

    ( needsDepList _and: [ nil ~~ oldValue ] )
      ifTrue: [
        occurrences timesRepeat: [
          DependencyList removePathTerm: self for: oldValue
        ]
      ].
  ].

updateDict ~~ nil
  ifTrue: [
    continue := true.
    [ continue ] whileTrue: [
      " remove the index dictionary entry "
      more := updateDict removeKey: oldValue
        value: anObject
        term: self
        logging: doLogging
        ifAbsent: [ continue := false ].
      occurrences := occurrences + 1
    ].
    occurrences := occurrences - 1.

    nil == oldValue
      ifFalse: [
        " make recursive call to remove mappings "
        1 to: children size do: [ :i |
          occurrences timesRepeat: [
            (children at: i)
              removeMappingsFor: oldValue
              lastOne: more not
              logging: doLogging
          ]
        ].
        more
          ifFalse: [
            depList := DependencyList for: oldValue.
            depList ~~ nil
              ifTrue: [ depList removePathTerms: children for: oldValue ]
          ]
      ]
  ].

updateBtree ~~ nil
  ifTrue: [ " insert an entry into the B-tree "
    occurrences timesRepeat: [ updateBtree btreeAt: newValue put: anObject ]
  ].

updateDict ~~ nil
  ifTrue: [ " insert an entry into the index dictionary "
    occurrences timesRepeat: [
      updateDict _at: newValue put: anObject term: self logging: doLogging 
    ]
  ].

( needsDepList _and: [ newValue ~~ nil ] )
  ifTrue: [
    occurrences timesRepeat: [
      newValue getDepListAndAddPathTerm: self withIVOffset: 1 logging: true
    ]
  ].

updateBtree == nil
  ifTrue: [ " make recursive call to add mappings "
    1 to: maps size do: [ :i |
      mapInfo := maps at: i.
      occurrences timesRepeat: [
        mapInfo pathTerm addMappingsUsing: mapInfo logging: doLogging
      ]
    ]
  ].
^ true
%

category: 'Accessing'
method: PathTerm
_classOf: anObject

"Returns the class of the given object."

<primitive: 166>
self _primitiveFailed: #_classOf: .
self _uncontinuableError
%

category: 'Accessing'
method: PathTerm
_nextObjectFor: anObject

"Returns the object at the instance variable that corresponds to the receiver
 path term."


^ self _nextObjectFor: anObject atInstVar: (self _ivOffsetFor: anObject)
%

category: 'Accessing'
method: PathTerm
_nextObjectFor: anObject atInstVar: ivOffset

"Returns the object at the instance variable that corresponds to the receiver
 path term."

<primitive: 167>
self _primitiveFailed: #_nextObjectFor:atInstVar: .
self _uncontinuableError
%

category: 'Updating Indexes'
method: PathTerm
addMappingsForObject: anObject logging: aBoolean

"Add index dictionary and dependency list entries for anObject."


| ivOffset nextObj |

( nil == anObject _or: [ self size == 0 ] )
  ifTrue: [ ^ self ].

(ivOffset := self _ivOffsetFor: anObject) == nil
  ifTrue: [
    anObject _errorInvalidOffset: name .
    self _uncontinuableError
  ].

" add an entry to the objects dependency list "
anObject
  getDepListAndAddPathTerm: self
  withIVOffset: ivOffset
  logging: aBoolean.

" get the next object along the path "
nextObj := self _nextObjectFor: anObject atInstVar: ivOffset.

updateBtree ~~ nil
  ifTrue: [ " insert an entry into the B-tree "
    updateBtree btreeAt: nextObj put: anObject.

    needsDepList
      ifTrue: [
        " add an entry to the very last object's dependency list "
        nextObj
          getDepListAndAddPathTerm: self
          withIVOffset: 1
          logging: aBoolean
      ]
  ].

updateDict ~~ nil
  ifTrue: [ " insert an entry into the index dictionary "
    updateDict _at: nextObj put: anObject term: self logging: aBoolean
  ].

nil == nextObj
  ifTrue: [ ^ self ].

1 to: children size do: [ :i |
  " make recursive call to add mappings "
  (children at: i) addMappingsForObject: nextObj logging: aBoolean
]
%

category: 'Reduced Conflict Support'
method: PathTerm
_selectiveAbort

"With removal of segments, nothing needs to be done."

%

category: 'Audit'
method: PathTerm
auditNsc: nsc on: aString level: level

"Verifies that the equality index objects are consistent.
 Returns a string that describes any inconsistencies found.  This method
 is currently unsupported, but is provided for customer support." 

| indexObj btreeCounts count |
"aString add: 'entering obj, num is ', num printString; lf."

self size == 0
  ifTrue: [ ^ aString ].

" for each index that utilizes the path term "
btreeCounts := Array new.
1 to: self size do: [ :i |
    indexObj := self at: i.
    ( indexObj isRangeEqualityIndex _and: [ indexObj size == offset ] )
        ifTrue: [
            " verify B-tree has correct entries "
            count := indexObj btreeRoot auditNsc: nsc for: self offset: i on: aString.
            btreeCounts add: count.
        ]
].
1 to: children size do: [ :i |
      | resultArray |
    resultArray := (children at: i) auditNsc: nsc
                                            on: aString
                                            level: level + 1.
    btreeCounts add: resultArray
].
^btreeCounts
%

category: 'Audit'
method: PathTerm
auditNscCounts: nsc for: obj on: aString count: btreeCounts

"Private.  This method
 is currently unsupported, but is provided for customer support." 

| nextObj index |

self size == 0
  ifTrue: [ ^ self ].

self indicatesIndexOnNscElements
    ifTrue: [ ^ self auditDirectNscCounts: nsc for: obj on: aString count: btreeCounts ].

nil == obj
    ifTrue: [ ^ self ].

nextObj := self auditDepListFor: obj index: nil on: aString
.
index := 1.
1 to: self size do: [ :i |
      | count indexObj |
    indexObj := self at: i.
    ( indexObj isRangeEqualityIndex _and: [ indexObj size == offset ] )
        ifTrue: [
          count := btreeCounts at: index.
          (count == 0)
            ifTrue: [
              aString add: Character lf;
                  add: ' -- The number of entries in Btree [';
                  add: (self at: i) btreeRoot asOop asString;
                  add: '] for pathTerm [';
                  add: self asOop asString;
                  add: ' offset ';
                  add: offset asString;
                  add: '] does not match the number of entries in the base collection (extra elements in either the base collection or btree).';
                  add: Character lf
            ].
          btreeCounts at: index put: (count - 1).
          index := index + 1.
        ]
].

1 to: children size do: [ :i |
    (children at: i) auditNscCounts: nsc
        for: nextObj
        on: aString
        count: (btreeCounts at: index).
    index := index + 1.
].
%

category: 'Audit'
method: PathTerm
auditDirectNscCounts: nsc for: obj on: aString count: btreeCounts

"Private.  This method
 is currently unsupported, but is provided for customer support." 

| index |

self size == 0
  ifTrue: [ ^ nil ].

(nil == obj)
  ifFalse: [
      | depList |
    depList := DependencyList for: obj.
    depList == nil
      ifTrue: [
        self auditInfo: #[ obj, nil, depList ] on: aString.
        aString add: ' Object has no dependency list'; add: Character lf.
      ].
  ].

index := 1.
1 to: self size do: [ :i |
      | count indexObj |
    indexObj := self at: i.
    ( indexObj isRangeEqualityIndex _and: [ indexObj size == offset ] )
        ifTrue: [
          count := btreeCounts at: index.
          (count == 0)
            ifTrue: [
              aString add: Character lf;
                  add: ' -- The number of entries in Btree [';
                  add: (self at: i) btreeRoot asOop asString;
                  add: '] for pathTerm [';
                  add: self asOop asString;
                  add: ' offset ';
                  add: offset asString;
                  add: '] does not match the number of entries in the base collection (extra elements in either the base collection or btree).';
                  add: Character lf
            ].
          btreeCounts at: index put: (count - 1).
          index := index + 1.
        ]
].
%

category: 'ObsoleteIDX - Audit'
method: PathTerm
auditBtree: btreeNode entryFor: obj and: nextObj occurrences: num on: aString builder: nscBuilder

"Private.  Unsupported method for GemStone Technical Support."

| bag |
self size == 0
  ifTrue: [ ^ self ].

btreeNode _findAllValuesForIdenticalKey: nextObj into: nscBuilder.
bag := nscBuilder completeBag.
(bag occurrencesOf: obj) == num 
    ifFalse: [
        self auditInfo: #[ nextObj, nil, nil ] on: aString.
        aString add: 'Btree [';
            add: btreeNode asOop asString;
            add: '] has incorrect number of entries (';
            add: (bag occurrencesOf: obj) asString;
            add: ') for [';
            add: obj asOop asString;
            add: '] of class ';
            add: obj class asString;
            add: ' (should be '; add: num asString; add: ')';
            add: Character lf
    ].
^ bag size
%

category: 'Audit'
method: PathTerm
auditDepListFor: obj index: indexObj on: aString

"Private.  Unsupported method for GemStone Technical Support."



| depList j nextObj ivOffset |
( nil == obj _or: [ self size == 0 ] )
    ifTrue: [ ^ nil ].

depList := DependencyList for: obj.
depList == nil
    ifTrue: [
        self auditInfo: #[ obj, indexObj, depList ] on: aString.
        aString add: ' Object has no dependency list'; add: Character lf.
        nextObj := self _nextObjectFor: obj.
    ]
    ifFalse: [
        j := depList _findOffsetForPathTerm: self.
        j == nil
            ifTrue: [
                self auditInfo: #[ obj, indexObj, depList ] on: aString.
                aString add: 'DependencyList has no entry for path term';
                        add: Character lf.
                nextObj := self _nextObjectFor: obj.
             ]
             ifFalse: [
                 ivOffset := depList at: j + 1.
                 ivOffset ~~ (self _ivOffsetFor: obj)
                     ifTrue: [
                         self auditInfo: #[ obj, indexObj, depList ] on: aString.
                         aString add: 'DependencyList has incorrect iv offset ';
                             add: ivOffset asString.
                         ivOffset := self _ivOffsetFor: obj.
                         aString add: ' instead of ';
                             add: ivOffset asString; add: Character lf
                     ].
                 nextObj := obj instVarAt: ivOffset.
             ]
    ].
((needsDepList _and: [ nextObj ~~ nil ]) _and: [ DependencyList needsDepList: nextObj ])
  ifTrue: [
    depList := DependencyList for: nextObj.
    depList == nil
        ifTrue: [
            self auditInfo: #[ nextObj, indexObj, depList ] on: aString.
            aString add: ' Object has no dependency list'; add: Character lf.
        ]
  ].
^ nextObj
%

category: 'Audit'
method: PathTerm
auditDepListForByteObject: obj occurrences: num index: indexObj on: aString

"Private.  Unsupported method for GemStone Technical Support."



| depList j |
(self size == 0 _or: [ obj isInvariant ])
  ifTrue: [ ^ self ].

depList := DependencyList for: obj.
depList == nil
    ifTrue: [
        self auditInfo: #[ obj, indexObj, depList ] on: aString.
        aString add: ' Object has no dependency list'; add: Character lf.
    ]
    ifFalse: [
        j := depList _findOffsetForPathTerm: self.
        j == nil
            ifTrue: [
                 "hack to avoid false positives for index paths ending in *"
                  self name = #*
                     ifFalse: 
                         [
                         self auditInfo: #[ obj, indexObj, depList ] on: aString.
                         aString add: 'DependencyList has no entry for path term';
                             add: Character lf
                         ]
            ]
            ifFalse: [
                (depList at: j + 1) == num
                    ifFalse: [
                        self auditInfo: #[ obj, indexObj, depList ] on: aString.
                        aString add: ' DependencyList has incorrect ref count ';
                            add: (depList at: j + 1) asString;
                            add: ' for occurrences ';
                            add: num asString; add: Character lf.
                    ]
            ]
    ]
%

category: 'Audit'
method: PathTerm
auditDictionaryEntryFor: obj and: nextObj occurrences: num on: aString

"Private.  Unsupported method for Technical Support."


| val |

self size == 0
  ifTrue: [ ^ 0 ].

val := self getIndexDictionary at: nextObj term: self ifAbsent: [
    self auditInfo: #[ nextObj, nil, nil ] on: aString.
    aString add: 'Index Dictionary has no entry for object [';
        add: obj asOop asString;
        add: '] of class ';
        add: obj class asString; add: Character lf.
    ^ 0
].
(BucketValueBag _hasInstance: val)
    ifTrue: [
        (val occurrencesOf: obj) == num 
             ifFalse: [
                self auditInfo: #[ nextObj, nil, nil ] on: aString.
                aString add: 'Index Dictionary has incorrect number of entries (';
                    add: (val occurrencesOf: obj) asString;
                    add: ') for [';
                    add: obj asOop asString;
                    add: '] of class ';
                    add: obj class asString;
                    add: ' (should be ';
                    add: num asString; add: ')';
                    add: Character lf
            ].
        ^ val size
    ]
    ifFalse: [
        val == obj
            ifFalse: [
                self auditInfo: #[ nextObj, nil, nil ] on: aString.
                aString add: 'Index Dictionary has incorrect entry for [';
                    add: obj asOop asString;
                    add: '] of class ';
                    add: obj class asString;add: Character lf
            ].
        ^ 1
    ]
%

category: 'ObsoleteIDX - Audit'
method: PathTerm
auditDirectObject: obj occurrences: num on: aString builder: nscBuilder

"Private.  Unsupported method for GemStone Technical Support."

"Verifies that the given object has the correct dependency list, index
 dictionary, and B-tree entries for the receiver."

| indexObj dictChecked |
self size == 0
  ifTrue: [ ^ self ].

dictChecked := false.
" for each index that utilizes the path term "
nscBuilder _resetForAudit.
1 to: self size do: [ :i |
    indexObj := self at: i.
    indexObj isRangeEqualityIndex
        ifTrue: [
            " verify B-tree has correct entries "
            self auditBtree: indexObj btreeRoot
                entryFor: obj
                and: obj
                occurrences: num
                on: aString
                builder: nscBuilder.
            nscBuilder _resetForAudit.

            (DependencyList for: obj) ~~ nil
                ifTrue: [ " verify dependency list "
                    self auditDepListForByteObject: obj
                        occurrences: num
                        index: indexObj
                        on: aString
                ]
        ]
        ifFalse: [ " verify index dictionary has correct entries "
            dictChecked
                ifFalse: [
                    self auditDictionaryEntryFor: obj
                        and: obj
                        occurrences: num
                        on: aString.
                    dictChecked := true
                ]
        ]
]
%

category: 'Audit'
method: PathTerm
auditInfo: array on: aString

"Private.  Unsupported method for GemStone Technical Support."



| obj indexObj depList |
self size == 0
  ifTrue: [ ^ self ].

obj := array at: 1.
indexObj := array at: 2.
depList := array at: 3.

aString add: 'object'.
(obj isKindOf: CharacterCollection)
    ifTrue: [ aString add: Character space; add: obj ].
aString add: ' [';
    add: obj asOop asString;
    add: '] of class ';
    add: obj class asString.
indexObj ~~ nil
     ifTrue: [
         aString add: ', ';
         add: (indexObj isRangeEqualityIndex ifTrue: [ 'equality' ] ifFalse:
 [ 'identity' ]);
         add: ' index <';
         add: indexObj pathComponentsString;
         add: '> [';
         add: indexObj asOop asString;
             add: ']'
    ].
aString add: ', path term <';
    add: name.
name == #*
    ifTrue: [ aString add: offset asString ].
aString add: '> [';
    add: self asOop asString;
    add: ']'.
depList ~~ nil
    ifTrue: [
        aString add: ', depList [';
            add: depList asOop asString;
            add: ']'
    ].
aString add: '
    -- '
%

category: 'ObsoleteIDX - Audit'
method: PathTerm
auditObject: obj occurrences: num on: aString builder: nscBuilder

"Private.  Unsupported method for GemStone Technical Support."

"Verifies that the given object has the correct dependency list, index
 dictionary, and B-tree entries for the receiver."

| indexObj nextObj dictChecked newNum |
self size == 0
  ifTrue: [ ^ aString ].

nscBuilder _resetForAudit.

self indicatesIndexOnNscElements
    ifTrue: [ ^ self auditDirectObject: obj occurrences: num on: aString builder: nscBuilder ].

nil == obj
    ifTrue: [ ^ aString ].

nextObj := self auditDepListFor: obj index: indexObj on: aString.
dictChecked := false.
" for each index that utilizes the path term "
1 to: self size do: [ :i |
    indexObj := self at: i.
    ( indexObj isRangeEqualityIndex _and: [ indexObj size == offset ] )
        ifTrue: [
            " verify B-tree has correct entries "
            newNum := self auditBtree: indexObj btreeRoot
                entryFor: obj
                and: nextObj
                occurrences: num
                on: aString
                builder: nscBuilder.
            nscBuilder _resetForAudit.

            " if the path term is the last path term of a range index "
            (DependencyList for: nextObj) ~~ nil
                ifTrue: [
                    self auditDepListForByteObject: nextObj
                        occurrences: newNum
                        index: indexObj
                        on: aString.
                ]
        ]
        ifFalse: [ " verify index dictionary has correct entries "
            dictChecked
                ifFalse: [
                    newNum := self auditDictionaryEntryFor: obj
                        and: nextObj
                        occurrences: num
                        on: aString.
                    dictChecked := true
                ]
        ]
].

obj ~~ nil
    ifTrue: [
        1 to: children size do: [ :i |
            (children at: i) auditObject: nextObj
                occurrences: newNum
                on: aString
                builder: nscBuilder.
            nscBuilder _resetForAudit.
        ]
]
%

category: 'Audit'
method: PathTerm
auditIndexDictionaryForDirectObject: obj occurrences: num on: aString

"Private.  Unsupported method for GemStone Technical Support."

"Verifies that the given object has the correct index dictionary entries for 
the receiver."

| indexObj dictChecked |
self size == 0
  ifTrue: [ ^ self ].

dictChecked := false.
"since there's only one index dictionary, shared by all indexes, when you find
the first one audit that and stop"
1 to: self size do: 
    [ :i |
    indexObj := self at: i.
    indexObj isRangeEqualityIndex
        ifFalse: 
            [ " verify index dictionary has correct entries "
            dictChecked
                ifFalse: 
                     [self auditDictionaryEntryFor: obj
                          and: obj
                          occurrences: num
                          on: aString.
                    dictChecked := true]
            ]
    ]
%

category: 'Audit'
method: PathTerm
auditIndexDictionaryForObject: obj occurrences: num on: aString

"Private.  Unsupported method for GemStone Technical Support."

"Verifies that the given object has the correct index dictionary entries for the receiver."

| indexObj nextObj newNum dictChecked |
self size == 0
  ifTrue: [ ^ aString ].

self indicatesIndexOnNscElements
    ifTrue: [ ^ self auditIndexDictionaryForDirectObject: obj occurrences: num on: aString ].

nil == obj
    ifTrue: [ ^ aString ].

nextObj := self _nextObjectFor: obj.

dictChecked := false.
" for each index that utilizes the path term "
1 to: self size do: [ :i |
    indexObj := self at: i.
    ( indexObj isRangeEqualityIndex _and: [ indexObj size == offset ] )
       ifFalse: [ " verify index dictionary has correct entries "
            dictChecked
                ifFalse: [
                    newNum := self auditDictionaryEntryFor: obj
                        and: nextObj
                        occurrences: num
                        on: aString.
                    dictChecked := true
                ]
        ]
].

obj ~~ nil
    ifTrue: [
        1 to: children size do: [ :i |
            (children at: i) auditIndexDictionaryForObject: nextObj
                occurrences: newNum
                on: aString
        ]
]
%

category: 'ObsoleteIDX - Audit'
method: PathTerm
auditBtreeForObject: obj occurrences: num on: aString builder: nscBuilder

"Private.  Unsupported method for GemStone Technical Support."

"Verifies that the given object has the B-tree entries for the receiver."

| indexObj nextObj newNum |
"aString add: 'entering obj, num is ', num printString; lf."

self size == 0
  ifTrue: [ ^ aString ].

nscBuilder _resetForAudit.

self indicatesIndexOnNscElements
  ifTrue: [ ^ self auditBtreeForDirectObject: obj occurrences: num on: aString builder: nscBuilder ].

nil == obj
    ifTrue: [ ^ aString ].

nextObj := self _nextObjectFor: obj.

" for each index that utilizes the path term "
1 to: self size do: [ :i |
    indexObj := self at: i.
    ( indexObj isRangeEqualityIndex _and: [ indexObj size == offset ] )
        ifTrue: [
            " verify B-tree has correct entries "
            newNum := self auditBtree: indexObj btreeRoot
                entryFor: obj
                and: nextObj
                occurrences: num
                on: aString
                builder: nscBuilder.
            nscBuilder _resetForAudit.
        ]
].

obj ~~ nil
    ifTrue: [
        1 to: children size do: [ :i |
            (children at: i) auditBtreeForObject: nextObj
                occurrences: newNum
                on: aString
                builder: nscBuilder.
            nscBuilder _resetForAudit.
        ]
]

%

category: 'ObsoleteIDX - Audit'
method: PathTerm
auditBtreeForDirectObject: obj occurrences: num on: aString builder: nscBuilder

"Private.  Unsupported method for GemStone Technical Support."

"Verifies that the given object has the correct B-tree entries for the receiver."

| indexObj dictChecked |

self size == 0
  ifTrue: [ ^ self ].

dictChecked := false.
" for each index that utilizes the path term "
nscBuilder _resetForAudit.
1 to: self size do: [ :i |
    indexObj := self at: i.
    indexObj isRangeEqualityIndex
        ifTrue: [
            " verify B-tree has correct entries "
            self auditBtree: indexObj btreeRoot
                entryFor: obj
                and: obj
                occurrences: num
                on: aString
                builder: nscBuilder.
            nscBuilder _resetForAudit.
        ]
]

%

category: 'Accessing'
method: PathTerm
_thisAndAllChildTermsInto: array

"Returns an Array containing the receiver and all child path terms."


array add: self.

1 to: children size do: [ :i |
  (children at: i) _thisAndAllChildTermsInto: array
].

^ array
%

category: 'Accessing'
method: PathTerm
_thisAndAllChildTerms

"Returns an Array containing the receiver and all child path terms."


^ self _thisAndAllChildTermsInto: Array new
%

category: 'Updating Indexes'
method: PathTerm
cleanupDependencyListFor: anObject

""


| ivOffset nextObj depList |

( nil == anObject _or: [ self size == 0 ] )
    ifTrue: [ ^ self ].

(depList := DependencyList for: anObject) == nil
  ifTrue: [ ^ self ].

" remove the receiver from the dependency list "
ivOffset := depList removeCompletelyPathTerm: self for: anObject.

self indicatesIndexOnRootNsc
  ifTrue: [ ^ self ].

" not sure why ivOffset would be zero --- because of modification tracking (dkh)"
ivOffset == 0
  ifTrue: [ nextObj := self _nextObjectFor: anObject ]
  ifFalse: [ 
      | exception |
    exception := Exception
      category: nil
      number: nil
      do: [ :ex :cat :num :args |
        "Handle any error thrown during execution of _nextObjectFor:
         Cannot resolve the path for this guy --- can't have been added to index,
         in fact this object could have caused the failure in the first place"
        ^ self
    ].
    nextObj := self _nextObjectFor: anObject atInstVar: ivOffset.
    exception remove.
  ].

nil == nextObj
  ifTrue: [ ^ self ].

needsDepList
  ifTrue: [
    (depList := DependencyList for: nextObj) ~~ nil
      ifTrue: [ depList removeCompletelyPathTerm: self for: nextObj ].
    ^ self
  ].

1 to: children size do: [ :i |
  (children at: i) cleanupDependencyListFor: nextObj
]
%

category: 'Testing'
method: PathTerm
_checkBtreeComparisonWith: anObject

"Returns whether the receiver's B-tree can hold an entry with anObject as the
 key."


^ updateBtree _canCompareWith: anObject
%

category: 'Updating Indexes'
method: PathTerm
_clear


"Assigns nil to important instance variables and sets the receiver's size
 to zero."

children := nil.
updateBtree := nil.
updateDict := nil.
self size: 0.
%

! deleted _doNotPerformPretraversalFor: v2.0

! deleted _totalNumSetValuedTerms v2.0

! deleted _pretraverse:ifObject:atOffset:replaceWith:

category: 'Clustering'
method: PathTerm
clusterDepthFirst

"Cluster the receiver."


name cluster.
segments cluster.
children cluster.

1 to: children size do: [ :i | (children at: i) cluster ]
%

category: 'Accessing'
method: PathTerm
_classOfNextObject

"Returns the class of the next object along the path, or Object if it cannot
 be determined."


1 to: self size do: [ :i |
  ( (self at: i) isRangeEqualityIndex _and: [ (self at: i) size == offset ] )
    ifTrue: [ ^ (self at: i) lastElementClass ]
].
^ Object
%

category: 'Testing'
method: PathTerm
_isObsoletePathTerm

"Returns whether the receiver is obsolete."

^ self size == 0
%

category: 'Private'
method: PathTerm
_makeObsolete

"Makes the receiver and all its children obsolete."



1 to: children size do: [ :i | (children at: i) _makeObsolete ].
self _clear.
%

category: 'Testing'
method: PathTerm
isLastTermOfAnIndex

"Return if the receiver is the last term of one of its indexes."



1 to: self size do: [ :i |
  (self at: i) last == self
    ifTrue: [ ^ true ]
].
^ false
%

category: 'Audit'
method: PathTerm
allDependencyListsFor: anObject into: all

"Put any dependency lists that can be found from the receiver
into the given set."



| ivOffset nextObj depList |
( nil == anObject _or: [ self size == 0 ] )
    ifTrue: [ ^ self ].

(depList := DependencyList for: anObject) == nil
  ifTrue: [ ^ self ].

all add: depList.

self indicatesIndexOnRootNsc
  ifTrue: [ ^ self ].

ivOffset := depList getInstVarOffsetWithPathTerm: self.

ivOffset == 0
  ifTrue: [ nextObj := self _nextObjectFor: anObject ]
  ifFalse: [ nextObj := self _nextObjectFor: anObject atInstVar: ivOffset ].

nil == nextObj
  ifTrue: [ ^ self ].

needsDepList
  ifTrue: [
    (depList := DependencyList for: nextObj) ~~ nil
      ifTrue: [ all add: depList ].
    ^ self
  ].

1 to: children size do: [ :i |
  (children at: i) allDependencyListsFor: nextObj into: all
]
%

category: 'Testing'
method: PathTerm
isFast

""

^ true
%

category: 'Accessing'
method: PathTerm
changeToSegment: aSegment

"Assign the receiver and its children to the given segment."

self assignToSegment: aSegment.
children assignToSegment: aSegment.
1 to: children size do: [ :i |
    (children at: i) changeToSegment: aSegment
].
segments assignToSegment: aSegment.
%

