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

removeallmethods PathEvaluator
removeallclassmethods PathEvaluator

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

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

txt := (GsDocText new) details:
''.

doc documentClassWith: txt.

self description: doc.
%

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

"Create a new initialized instance of the receiver."

^ self basicNew initialize
%

! ------------------- Instance methods for PathEvaluator
category: 'Initialization'
method: PathEvaluator
initialize

"Initialize a new instance of the receiver."

ivOffsets := Array new.
constraintClasses := Array new
%

category: 'Query Select'
method: PathEvaluator
findAllValuesEqualTo: aValue

"Returns a set of all values that satisfy the query."

^ self findAllValuesWithComparison: #_idxForCompareEqualTo: to: aValue 
%

category: 'Query Select'
method: PathEvaluator
findAllValuesGreaterThanKey: aValue andEquals: aBoolean
                                                                                                           
"Returns a set of all values that satisfy the query."
                                                                                                           
^ self
    findAllValuesWithComparison:
        (aBoolean
            ifTrue: [ #_idxForCompareGreaterThanOrEqualTo: ]
            ifFalse: [ #_idxForCompareGreaterThan: ])
    to: aValue
%

category: 'Query Select'
method: PathEvaluator
findAllValuesIdenticalTo: aValue

"Returns a set of all values that satisfy the query."

" optimize query for objects in the NSC itself "
(self at: 1) ==  #'' 
    ifTrue: [ ^ self _allIdenticalTo: aValue in: nsc ].

^ self findAllValuesWithComparison:  #== to: aValue 
%

category: 'Query Select'
method: PathEvaluator
findAllValuesLessThanKey: aValue andEquals: aBoolean

"Returns a set of all values that satisfy the query."

^ self 
    findAllValuesWithComparison:
        (aBoolean
            ifTrue: [ #_idxForCompareLessThanOrEqualTo: ]
            ifFalse: [ #_idxForCompareLessThan: ])
    to: aValue 
%

category: 'Query Select'
method: PathEvaluator
findAllValuesNotEqualTo: aValue

"Returns a set of all values that satisfy the query."

^ self findAllValuesWithComparison:  #_idxForCompareNotEqualTo: to: aValue 
%

category: 'Query Select'
method: PathEvaluator
findAllValuesNotIdenticalTo: aValue

"Returns a set of all values that satisfy the query."

|result|

" optimize query for objects in the NSC itself "
(self at: 1) = #'' ifTrue: [
  result := nsc _asIdentityBag copy .
  result removeAll: (self _allIdenticalTo: aValue in: nsc) .
  result _class = nsc speciesForSelect
    ifFalse:
      [|tmpResult |
      tmpResult := nsc speciesForSelect new.
      tmpResult addAll: result.
      result := tmpResult].
  ^ result
].

^ self
    findAllValuesWithComparison: #~~
    to: aValue
%

category: 'Query Detect'
method: PathEvaluator
findFirstValueEqualTo: aValue

"Returns the first value that satisfies the query."

| obj bag |
bag := nsc _asIdentityBag.
self isIndexOnRootNsc
    ifTrue: [
        1 to: bag size do: [ :i |
            ( (obj := bag _at: i) = aValue)
                ifTrue: [ ^ obj ]
        ]
    ]
    ifFalse: [
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        1 to: bag size do: [ :i |
            nil == (obj := bag _at: i)
                ifFalse: [
                    ((self _traverseObject: obj cache: ivOffsetCache) _idxForCompareEqualTo: aValue)
                        ifTrue: [ ^ obj ]
                ]
        ]
    ].

^ #_incompletePathTraversal
%

category: 'Query Detect'
method: PathEvaluator
findFirstValueGreaterThanKey: aValue
andEquals: aBoolean

"Returns the first value that satisfies the query."

^ self
    findFirstValueWithComparison:
        (aBoolean
            ifTrue: [ #_idxForCompareGreaterThanOrEqualTo: ]
            ifFalse: [ #_idxForCompareGreaterThan: ])
    to: aValue 
%

category: 'Query Detect'
method: PathEvaluator
findFirstValueIdenticalTo: aValue

"Returns the first value that satisfies the query."

| bag incomplete obj |
" optimize query for objects in the NSC itself "
self isIndexOnRootNsc
    ifTrue: [
        (nsc includesIdentical: aValue)
            ifTrue: [ ^ aValue ]
            ifFalse: [ ^ #_incompletePathTraversal ]
    ]
    ifFalse: [
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        bag := nsc _asIdentityBag.
        incomplete := #_incompletePathTraversal.

        1 to: bag size do: [ :i |
            nil == (obj := bag _at: i)
                ifFalse: [
                    aValue == (self _traverseObject: obj cache: ivOffsetCache)
                        ifTrue: [ ^ obj ]
                ]
        ]
    ].

^ #_incompletePathTraversal
%

category: 'Query Detect'
method: PathEvaluator
findFirstValueLessThanKey: aValue
andEquals: aBoolean

"Returns the first value that satisfies the query."

^ self
    findFirstValueWithComparison:
        (aBoolean
            ifTrue: [ #_idxForCompareLessThanOrEqualTo: ]
            ifFalse: [ #_idxForCompareLessThan: ])
    to: aValue 
%

category: 'Query Detect'
method: PathEvaluator
findFirstValueNotEqualTo: aValue

"Returns the first value that satisfies the query."

^ self findFirstValueWithComparison: #_idxForCompareNotEqualTo: to: aValue
%

category: 'Query Detect'
method: PathEvaluator
findFirstValueNotIdenticalTo: aValue

"Returns the first value that satisfies the query."

^ self findFirstValueWithComparison: #~~ to: aValue
%

category: 'Testing'
method: PathEvaluator
isIdentityIndex

"Returns false."

^ false
%

category: 'Testing'
method: PathEvaluator
isPathEvaluator

"Returns true."

^ true
%

category: 'Testing'
method: PathEvaluator
isRangeEqualityIndex

"Returns true.  A path evaluator can perform the searches of an equality
 index."

^ true
%

category: 'Accessing'
method: PathEvaluator
ivOffsets

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

^ivOffsets
%

category: 'Updating'
method: PathEvaluator
ivOffsets: newValue

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

ivOffsets := newValue
%

category: 'Accessing'
method: PathEvaluator
constraintClasses

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

^constraintClasses
%

category: 'Updating'
method: PathEvaluator
constraintClasses: newValue

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

constraintClasses := newValue
%

category: 'Accessing'
method: PathEvaluator
nsc

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

^nsc
%

category: 'Accessing'
method: PathEvaluator
nscRoot

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

^nsc
%

category: 'Updating'
method: PathEvaluator
nsc: newValue

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

nsc := newValue
%

category: 'Traversing'
method: PathEvaluator
traverseObject: anObject

"Traverse the sub-objects of the given object using the path names of the
 receiver.  This method assumes that the index is not over a set-valued
 instance variable.  For that kind of index, use the traverse:startingAt:into:
 method.  If a nil value is reached before the end of the path, returns the
 incompletePathTraversalObject."

self isIndexOnNscElements
    ifTrue: [ ^ anObject ].

nil == anObject
    ifTrue: [ ^ #_incompletePathTraversal ].

^ self _traverseObject: anObject
%

category: 'Testing'
method: PathEvaluator
isIndexOnNscElements

"Returns whether the receiver is a path directly on the elements of an NSC
 (either the root NSC or a set-valued instance variable along the path)."

^ (self at: self size) ==  #'' 
%

category: 'Testing'
method: PathEvaluator
isIndexOnRootNsc

"Returns whether the receiver is a path directly on the elements of the root
 NSC."

^ (self at: self size) ==  #'' 
%

! deleted _isPathConstrained v2.0

! deleted isPathConstrained v2.0

category: 'Traversing'
method: PathEvaluator
_traverseObject: anObject

"Traverse the sub-objects of the given object using the path names of the
 receiver.  This method assumes that the index is not over a set-valued
 instance variable.  For that kind of index, use the traverse:startingAt:into:
 method.  If a nil value is reached before the end of the path, returns the
 incompletePathTraversalObject."

^ self _traverse: anObject
    cachedOffsets: nil
    cachedClasses: nil
    incomplete: #_incompletePathTraversal
%

category: 'Traversing'
method: PathEvaluator
_createIvOffsetCache
  | ivOffsetCache |
ivOffsetCache := Array new: 2.
ivOffsetCache at: 1 put: (Array new: self size).
ivOffsetCache at: 2 put: (Array new: self size).
^ivOffsetCache
%

category: 'Traversing'
method: PathEvaluator
_traverseObject: anObject cache: ivOffsetCache

"Traverse the sub-objects of the given object using the path names of the
 receiver.  This method assumes that the index is not over a set-valued
 instance variable.  For that kind of index, use the traverse:startingAt:into:
 method.  If a nil value is reached before the end of the path, returns the
 incompletePathTraversalObject."

^ self _traverse: anObject
    cachedOffsets: (ivOffsetCache at: 2)
    cachedClasses: (ivOffsetCache at: 1)
    incomplete: #_incompletePathTraversal
%

category: 'Traversing'
method: PathEvaluator
_traverse: anObject
cachedOffsets: offsets
cachedClasses: classes
incomplete: incompletePathTraversal

<primitive:593>

| nextObj sz |
nextObj := anObject.
sz := self size.
1 to: sz do: [ :i |
  (offsets == nil)
     ifTrue: [  nextObj := self _nextObjectFor: nextObj at: i ]
     ifFalse: [  nextObj := self _nextObjectFor: nextObj at: i offsets: offsets classes: classes ].

  ( nil == nextObj _and: [ i ~~ sz ] )
    ifTrue: [ ^ #_incompletePathTraversal ].

  (nextObj == #_incompletePathTraversal)
    ifTrue: [ ^ nextObj ].

].
^ nextObj
%

category: 'Query Support'
method: PathEvaluator
_calculateIvOffsets

"Get the constraint class for each path name along the path in the receiver.
 For each constraint class, determine the instance variable offset for the
 corresponding path name, and place the offset in the receiver's Array of
 instance variable offsets.  Returns the receiver."
^ self _calculateIvOffsetsOnKind: nsc class elementConstraint
%

category: 'Traversing'
method: PathEvaluator
_nextObjectFor: anObject at: offset offsets: offsets classes: classes

"Returns the next object along the path in anObject for the named instance
 variable at the given offset in the receiver."

| ivOffset  |
(classes at: offset) ~~ anObject class
  ifTrue: [
    ivOffset := anObject class _ivOffsetOf: (self at: offset).
    ivOffset == nil
      ifTrue: [ anObject _errorInvalidOffset: (self at: offset) ].
    classes at: offset put: anObject class.
    offsets at: offset put: ivOffset.
  ]
  ifFalse: [
    ivOffset := offsets at: offset.
  ].

( ivOffset > anObject _primitiveSize )
    ifTrue: [ ^ #_incompletePathTraversal ].

^ anObject instVarAt: ivOffset
%

category: 'Query Support'
method: PathEvaluator
_calculateIvOffsetsOnKind: aClass

"Get the constraint class for each path name along the path in the receiver,
 starting with aClass.  For each constraint class, determine the instance
 variable offset for the corresponding path name, and place the offset in
 the receiver's Array of instance variable offsets.  Returns the receiver."

| class |
self _getConstraintsStartingOnKind: aClass.

1 to: constraintClasses size do: [ :i |
    (class := constraintClasses at: i) ~~ nil
        ifTrue: [ ivOffsets at: i put: (class _ivOffsetOf: (self at: i)) ]
        ifFalse: [ ivOffsets at: i put: nil ]
]
%

category: 'Query Select'
method: PathEvaluator
findAllValuesWithComparison: selector to: aValue

"Returns a set of all values that satisfy the query.  Must iterate through all
 elements in the NSC, traversing down the path for each object."

^ self _findAllValuesWithComparison: selector to: aValue
%

category: 'Query Select'
method: PathEvaluator
_findAllValuesWithComparison: selector to: aValue

""

| obj endObj tmpHolder bag |
tmpHolder := NscBuilder for: nsc speciesForSelect new max: self sizeForNscBuilder.

self isIndexOnRootNsc
    ifTrue: [
        bag := nsc _asIdentityBag.
        1 to: bag size do: [ :i |
            ( (obj := bag _at: i) perform: selector with: aValue)
                ifTrue: [ tmpHolder add: obj ]
        ]
    ]
    ifFalse: [
        | ivOffsetCache incomplete |
        ivOffsetCache := self _createIvOffsetCache.
        incomplete := #_incompletePathTraversal.
        bag := nsc _asIdentityBag.
        1 to: bag size do: [ :i |
            nil == (obj := bag _at: i)
                ifFalse: [
                    endObj := self _traverseObject: obj cache: ivOffsetCache.
                    ( endObj ~~ incomplete _and:
                    [ endObj perform: selector with: aValue ] )
                        ifTrue: [ tmpHolder add: obj ]
                ]
        ]
    ].
^ tmpHolder completeBag
%

category: 'Query Detect'
method: PathEvaluator
findFirstValueWithComparison: selector to: aValue

"Returns the first value that satisfies the query.  Must iterate through the
 elements in the NSC, traversing down the path for each object."

^ self
     _findFirstValueWithComparison: selector
     to: aValue
%

category: 'Query Detect'
method: PathEvaluator
_findFirstValueWithComparison: selector to: aValue

""

| obj endObj bag incomplete |
self isIndexOnRootNsc
    ifTrue: [
        bag := nsc _asIdentityBag.
        1 to: bag size do: [ :i |
            ( (obj := bag _at: i) perform: selector with: aValue)
                ifTrue: [ ^ obj ]
        ]
    ]
    ifFalse: [
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        incomplete := #_incompletePathTraversal.
        bag := nsc _asIdentityBag.
        1 to: bag size do: [ :i |
            nil == (obj := bag _at: i)
                ifFalse: [
                    endObj := self _traverseObject: obj cache: ivOffsetCache.
                    ( endObj ~~ incomplete _and:
                    [ endObj perform: selector with: aValue ] )
                        ifTrue: [ ^ obj ]
                ]
        ]
    ].
^ #_incompletePathTraversal
%

category: 'Sorting'
method: PathEvaluator
_sortOn: indexObjs using: sorter persistentRoot: persistentArrayOrNil

"Returns an Array of objects from the receiver's NSC that is sorted according
 to the paths traversed by indexObjs (with the direction of the sort specified
 in theBooleans)."

|  obj aColl firstIncompleteArray sortNodes anArray
  hasSecondarySort incompletesArrays incomplete indexManager |

sortNodes := sorter sortNodes.
hasSecondarySort := indexObjs size > 1.
incomplete := #_incompletePathTraversal.

incompletesArrays := sorter incompletes.
firstIncompleteArray := incompletesArrays at: 1.

aColl := nsc _asCollectionForSorting.

persistentArrayOrNil ~~ nil ifTrue: [ persistentArrayOrNil add: aColl ].
" optimize when no traversal is needed "
indexManager := IndexManager current.
self isIndexOnRootNsc
  ifTrue: [
    1 to: aColl size do: [ :i |
      obj := aColl _at: i.
      obj == nil
        ifTrue: [ firstIncompleteArray add: nil ]
        ifFalse: [
          sorter _addObject: obj
            inNodes: sortNodes
            hasSecondarySort: hasSecondarySort
        ].
      indexManager commitIndexMaintenance: self at: i.
    ].
  ]
  ifFalse: [
    1 to: aColl size do: [ :i |
      obj := aColl _at: i.
      obj == nil
        ifTrue: [ firstIncompleteArray add: nil ]
        ifFalse: [
          sorter _addObject: obj
            inNodes: sortNodes
            hasSecondarySort: hasSecondarySort
            incompletes: incompletesArrays
            incomplete: incomplete
        ].
      indexManager commitIndexMaintenance: self at: i.
    ]
  ].

anArray := Array new: nsc size.
persistentArrayOrNil ~~ nil 
  ifTrue: [ 
    persistentArrayOrNil add: anArray.
    indexManager commitIndexMaintenance: self at: aColl size.
 ].
^ sorter sortInto: anArray startingAt: 1
%

category: 'Sorting'
method: PathEvaluator
_sortOn: indexObjs directions: theBooleans persistentRoot: persistentArrayOrNil

"Returns an Array of objects from the receiver's NSC that is sorted according
 to the paths traversed by indexObjs (with the direction of the sort specified
 in theBooleans)."

  | sorter |

sorter := PathSorter on: indexObjs directions: theBooleans.
(IndexManager autoCommit _and: [ persistentArrayOrNil ~~ nil ])
  ifTrue: [
    IndexManager current executeStartingIndexMaintenance: [ | result |
      persistentArrayOrNil add: sorter.
      IndexManager current _doCommit.
      result := self _sortOn: indexObjs using: sorter persistentRoot: persistentArrayOrNil.
      persistentArrayOrNil size: 0.
      ^result
    ].
  ]
  ifFalse: [
    ^ self _sortOn: indexObjs using: sorter persistentRoot: nil
  ]
%

category: 'Accessing'
method: PathEvaluator
lastElementClass

"Returns the class of the last object along the path (can only be determined if
 the path is fully constrained).  Returns nil if the path is not fully
 constrained."

| lastClass |
lastClass := constraintClasses at: constraintClasses size.
lastClass == nil
    ifTrue: [ ^ nil ].

self isIndexOnRootNsc
    ifTrue: [ ^ lastClass ].

^ lastClass _constraintOn: (self at: self size)
%

category: 'Accessing'
method: PathEvaluator
progress: anInteger

"ignored - required for autoCommit functionality"
%

category: 'Accessing'
method: PathEvaluator
pathComponentsString

"Returns a string of the path components."

| str sz |
sz := self size.
str := String new.
1 to: sz do: [ :i |
    str addAll: (self at: i).
    i == sz
        ifFalse: [ str add: $.  ]
].
^ str

%

category: 'Query Support'
method: PathEvaluator
_allIdenticalTo: anObject in: aBag

"Returns a new NSC containing the given object with the number of occurrences
 as in the given NSC."

| result tmp |
result := self _primAllIdenticalTo: anObject in: aBag _asIdentityBag.
aBag _isIdentityBag
  ifFalse: [
    tmp := aBag class new: result size.
    tmp addAll: result.
    result := tmp
  ].
^ result
%

category: 'Query Support'
method: PathEvaluator
_primAllIdenticalTo: anObject in: aBag

"Returns a new NSC containing the given object with the number of occurrences as
 in the given NSC."

<primitive: 340>
self _primitiveFailed: #allIdenticalTo:in: .
self _uncontinuableError
%

category: 'Query Support'
method: PathEvaluator
hasSamePathAs: aPathEvaluator

"Returns whether the given path evaluator is on the same path as the receiver."

" make sure it is a path evaluator "
aPathEvaluator isPathEvaluator
    ifFalse: [ ^ false ].

" make sure they have the same number of terms "
self size == aPathEvaluator size
    ifFalse: [ ^ false ].

" check each term "
1 to: self size do: [ :i |
    (self at: i) == (aPathEvaluator at: i)
        ifFalse: [ ^ false ]
].
^ true
%

category: 'Query Support'
method: PathEvaluator
_getConstraintsStartingOnKind: aClass

"Calculate the constraint Array based upon the path beginning with
 instances of aClass."

| tmpClass |
" if path is empty string, then use given class "
(self at: 1) ==  #'' 
    ifTrue: [
        constraintClasses at: 1 put: aClass.
        ^ self
    ].

tmpClass := aClass.
1 to: self size do: [ :i |
    constraintClasses at: i put: tmpClass.
    tmpClass == nil
        ifFalse: [
            tmpClass isNsc
                ifTrue: [ tmpClass := tmpClass elementConstraint ]
                ifFalse: [ tmpClass := (tmpClass _constraintOn: (self at: i)) ]
        ].
]
%

category: 'Traversing'
method: PathEvaluator
_traverseObject: anObject
incompletesInto: incompleteArray
incomplete: incomplete

"Traverse the sub-objects of the given object using the path names of the
 receiver.  This method assumes that the index is not over a set-valued
 instance variable.  For that kind of index, use the traverse:startingAt:into:
 method.  If a nil value is reached before the end of the path, place the object
 into the appropriate Array in the incompleteArray of Arrays."

| nextObj sz |
nextObj := anObject.
sz := self size.
1 to: sz do: [ :i |
  nextObj := self _nextObjectFor: nextObj at: i.

  (( nil == nextObj _and: [ i ~~ sz ] ) _or: [ nextObj == #_incompletePathTraversal ])
    ifTrue: [
      incompleteArray == nil
        ifTrue: [ ^ nil ].

      (incompleteArray at: i + 1) add: anObject.
        ^ incomplete
    ].
].
^ nextObj
%

category: 'Traversing'
method: PathEvaluator
traverseObject: anObject
incompletesInto: incompleteArray
incomplete: incomplete

"Traverse the sub-objects of the given object using the path names of the
 receiver.  This method assumes that the index is not over a set-valued
 instance variable.  For that kind of index, use the traverse:startingAt:into:
 method.  If a nil value is reached before the end of the path, place the object
 into the appropriate Array in the incompleteArray of Arrays."

self isIndexOnNscElements
    ifTrue: [ ^ anObject ].

nil == anObject
    ifTrue: [ ^ incomplete ].

^ self _traverseObject: anObject
  incompletesInto: incompleteArray
  incomplete: incomplete
%

category: 'Query Select'
method: PathEvaluator
findAllValuesGreaterThan: val1
andEquals: bool1
andLessThan: val2
andEquals: bool2

""

| obj endObj tmpHolder bag incomplete |
" if query on NSC elements, execute optimized method (no traversal) "
self isIndexOnRootNsc
  ifTrue: [
    ^ self _findAllValuesGreaterThan: val1
      andEquals: bool1
      andLessThan: val2
      andEquals: bool2
  ].

tmpHolder := NscBuilder for: nsc speciesForSelect new max: self sizeForNscBuilder.

incomplete := #_incompletePathTraversal.
bag := nsc _asIdentityBag.

bool1
  ifTrue: [
    bool2
      ifTrue: [
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              endObj := self _traverseObject: obj cache: ivOffsetCache.

              ( endObj ~~ incomplete _and:
              [ ( endObj _idxForCompareGreaterThanOrEqualTo: val1 ) _and:
              [ endObj _idxForCompareLessThanOrEqualTo: val2 ] ] )
                ifTrue: [ tmpHolder add: obj ]
            ]
        ]
      ]
      ifFalse: [ " bool2 is false "
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              endObj := self _traverseObject: obj cache: ivOffsetCache.

              ( endObj ~~ incomplete _and:
              [ ( endObj _idxForCompareGreaterThanOrEqualTo: val1 ) _and:
              [ endObj _idxForCompareLessThan: val2 ] ] )
                ifTrue: [ tmpHolder add: obj ]
            ]
        ]
      ]
  ]
  ifFalse: [ " bool1 is false "
    bool2
      ifTrue: [
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              endObj := self _traverseObject: obj cache: ivOffsetCache.

              ( endObj ~~ incomplete _and:
              [ ( endObj _idxForCompareGreaterThan: val1 ) _and:
              [ endObj _idxForCompareLessThanOrEqualTo: val2 ] ] )
                ifTrue: [ tmpHolder add: obj ]
            ]
        ]
      ]
      ifFalse: [ " bool2 is false "
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              endObj := self _traverseObject: obj cache: ivOffsetCache.

              ( endObj ~~ incomplete _and:
              [ ( endObj _idxForCompareGreaterThan: val1 ) _and:
              [ endObj _idxForCompareLessThan: val2 ] ] )
                ifTrue: [ tmpHolder add: obj ]
            ]
        ]
      ]
  ].

^ tmpHolder completeBag
%

category: 'Query Detect'
method: PathEvaluator
findFirstValueGreaterThan: val1
andEquals: bool1
andLessThan: val2
andEquals: bool2

""

| obj endObj bag incomplete |
incomplete := #_incompletePathTraversal.
bag := nsc _asIdentityBag.

bool1
  ifTrue: [
    bool2
      ifTrue: [
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              endObj := self _traverseObject: obj cache: ivOffsetCache.

              ( endObj ~~ incomplete _and:
              [ ( endObj _idxForCompareGreaterThanOrEqualTo: val1 ) _and:
              [ endObj _idxForCompareLessThanOrEqualTo: val2 ] ] )
                ifTrue: [ ^ obj ]
            ]
        ]
      ]
      ifFalse: [ " bool2 is false "
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              endObj := self _traverseObject: obj cache: ivOffsetCache.

              ( endObj ~~ incomplete _and:
              [ ( endObj _idxForCompareGreaterThanOrEqualTo: val1 ) _and:
              [ endObj _idxForCompareLessThan: val2 ] ] )
                ifTrue: [ ^ obj ]
            ]
        ]
      ]
  ]
  ifFalse: [ " bool1 is false "
    bool2
      ifTrue: [
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              endObj := self _traverseObject: obj cache: ivOffsetCache.

              ( endObj ~~ incomplete _and:
              [ ( endObj _idxForCompareGreaterThan: val1 ) _and:
              [ endObj _idxForCompareLessThanOrEqualTo: val2 ] ] )
                ifTrue: [ ^ obj ]
            ]
        ]
      ]
      ifFalse: [ " bool2 is false "
        | ivOffsetCache |
        ivOffsetCache := self _createIvOffsetCache.
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              endObj := self _traverseObject: obj cache: ivOffsetCache.

              ( endObj ~~ incomplete _and:
              [ ( endObj _idxForCompareGreaterThan: val1 ) _and:
              [ endObj _idxForCompareLessThan: val2 ] ] )
                ifTrue: [ ^ obj ]
            ]
        ]
      ]
  ].

^ #_incompletePathTraversal
%

category: 'Converting'
method: PathEvaluator
asPathEvaluator

""

^ self
%

category: 'Converting'
method: PathEvaluator
asMostSpecificType

"Returns a new instance of a subclass, or self, depending upon the path."

self _calculateIvOffsets.
%

category: 'Query Support'
method: PathEvaluator
traverse: anObject andCompare: selector with: aValue

"Traverse the given object and perform the comparison operation
 on the object at the end of the path and the given value."

| endObj  |

^ ( (endObj := self _traverseObject: anObject) ~~ #_incompletePathTraversal _and:
[ endObj perform: selector with: aValue ] )
%

category: 'Query Support'
method: PathEvaluator
traverse: anObject andCompare: selector withTraverser: aTraverser

"Traverse the given object and perform the comparison operation on the object
 at the end of the path and the value obtained by using the traverser."

| endObj1 endObj2  |

^ (endObj1 := self _traverseObject: anObject) ~~ #_incompletePathTraversal _and:
[ (endObj2 := aTraverser _traverseObject: anObject) ~~ #_incompletePathTraversal _and:
[ endObj1 perform: selector with: endObj2 ] ]
%

category: 'Accessing'
method: PathEvaluator
sizeForNscBuilder

"Returns the size to be used when an NscBuilder is collecting objects."

^ nsc size 
%

category: 'Traversing'
method: PathEvaluator
_nextObjectFor: anObject at: offset

"Returns the next object along the path in anObject for the named instance
 variable at the given offset in the receiver."

| ivOffset |
ivOffset := anObject class _ivOffsetOf: (self at: offset).

ivOffset == nil
  ifTrue: [ anObject _errorInvalidOffset: (self at: offset) ].

( ivOffset > anObject _primitiveSize )
    ifTrue: [ ^ #_incompletePathTraversal ].

^ anObject instVarAt: ivOffset
%

category: 'Traversing'
method: PathEvaluator
_initTraverseCache

"Do nothing."

%

category: 'Query Select'
method: PathEvaluator
_findAllValuesGreaterThan: val1
andEquals: bool1
andLessThan: val2
andEquals: bool2

"This method is optimized for the case when the query is directly on
 the elements of the NSC (that is, no traversal is needed)."

| obj tmpHolder bag |
tmpHolder := NscBuilder for: nsc speciesForSelect new max: self sizeForNscBuilder.

bag := nsc _asIdentityBag.

bool1
  ifTrue: [
    bool2
      ifTrue: [
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              ( ( obj _idxForCompareGreaterThanOrEqualTo: val1 ) _and:
              [ obj _idxForCompareLessThanOrEqualTo: val2 ] )
                ifTrue: [ tmpHolder add: obj ]
            ]
        ]
      ]
      ifFalse: [ " bool2 is false "
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              ( ( obj _idxForCompareGreaterThanOrEqualTo: val1 ) _and:
              [ obj _idxForCompareLessThan: val2 ] )
                ifTrue: [ tmpHolder add: obj ]
            ]
        ]
      ]
  ]
  ifFalse: [ " bool1 is false "
    bool2
      ifTrue: [
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              ( ( obj _idxForCompareGreaterThan: val1 ) _and:
              [ obj _idxForCompareLessThanOrEqualTo: val2 ] )
                ifTrue: [ tmpHolder add: obj ]
            ]
        ]
      ]
      ifFalse: [ " bool2 is false "
        1 to: bag size do: [ :i |
          nil == (obj := bag _at: i)
            ifFalse: [
              ( ( obj _idxForCompareGreaterThan: val1 ) _and:
              [ obj _idxForCompareLessThan: val2 ] )
                ifTrue: [ tmpHolder add: obj ]
            ]
        ]
      ]
  ].

^ tmpHolder completeBag
%

category: 'Converting'
method: PathEvaluator
asQueryEvaluator

  ^self
%
