!=========================================================================
! Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved.
!
! $Id: rangeindexreadstr.gs,v 1.13 2008-01-09 22:50:13 stever Exp $
!
! Superclass Hierarchy:
!   RangeIndexReadStream, BtreeReadStream, Stream, Object.
!
!=========================================================================

! class created in idxclasses.topaz

removeallmethods RangeIndexReadStream
removeallclassmethods RangeIndexReadStream

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

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

txt := (GsDocText new) details:
'RangeIndexReadStream, like its superclass BtreeReadStream, supports the
 composition of query results by providing access to a B-tree structure.  Its
 ''next'' and ''atEnd'' methods are used the same way as those of
 BtreeReadStream in iterating through the B-tree.

 RangeIndexReadStream differs from BtreeReadStream in that it uses the reverse
 mappings to B-tree nodes that are found in a RangeEqualityIndex to obtain the
 next entry.  You can supply that index when you create the stream, and the
 index identifies the ordering used to return the entries.'.
doc documentClassWith: txt.

txt := (GsDocText new) details:
'The RangeEqualityIndex for an instance of this class.'.
doc documentInstVar: #rangeIndex with: txt.

txt := (GsDocText new) details:
'An Array of Integers that indicates the offset into BucketValueBags
 along the path.'.
doc documentInstVar: #setIterationIndexes with: txt.

self description: doc.
%

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

"Create an initialized instance of the receiver."

| newOne |
newOne := super new.
newOne setIterationIndexes: Array new.
^ newOne
%

category: 'Instance Creation'
classmethod: RangeIndexReadStream
on: aRangeIndex

"Create a stream that can access the entire contents of the given
 RangeEqualityIndex."

| newStream |
newStream := super on: aRangeIndex btreeRoot.
newStream rangeIndex: aRangeIndex.
^ newStream
%

! ------------------- Instance methods for RangeIndexReadStream
category: 'Private'
method: RangeIndexReadStream
_advance

"Advances the B-tree portion of the stream without advancing any of the set
 iteration indexes."

^ super next
%

category: 'Testing'
method: RangeIndexReadStream
_atEnd

"Returns true if there are no more elements to return through the logical
 iteration of the stream."

^ super atEnd _and: [ self noMoreSetsToIterate ]
%

category: 'Testing'
method: RangeIndexReadStream
atEnd

"Returns true if there are no more elements to return through the logical
 iteration of the stream."

| atEnd |
atEnd := self _atEnd.
^ atEnd
%

category: 'Private'
method: RangeIndexReadStream
getNext

"Returns the next value on a stream of range index values."

| val |

" get the initial value at the end of the path
(but do not advance the stream yet) "
val := self _peekValue.

" if the index is not on the elements of the root NSC "
rangeIndex isIndexOnRootNsc
  ifFalse: [
    " now iterate through the path components to get the final value "
    rangeIndex lastPathComponentsDictionaryOffset downTo: 1 do: [ :i |
      val := self nextValueFor: val term: (rangeIndex at: i)
    ].
  ].
" advance the B-tree current position "
super next.
^ val
%

category: 'Accessing'
method: RangeIndexReadStream
next

"Returns the next value on a stream of range index values."

| result |
self _atEnd
    ifTrue: [ self _errorEndOfStream ].

result := self getNext.
^ result
%

category: 'Private'
method: RangeIndexReadStream
nextValueFor: val term: pathTerm

"Returns the next value on a stream of range index values."

| nextVal |

nextVal := rangeIndex indexDictionary at: val term: pathTerm.
" see if the next value is a BucketValueBag "
(BucketValueBag _hasInstance: nextVal)
  ifTrue: [ nextVal := self nextValueInBag: nextVal level: pathTerm offset ].

^ nextVal
%

category: 'Accessing'
method: RangeIndexReadStream
rangeIndex

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

^rangeIndex
%

category: 'Updating'
method: RangeIndexReadStream
rangeIndex: newValue

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

rangeIndex := newValue.
self initializeSetIterationIndexes
%

category: 'Accessing'
method: RangeIndexReadStream
setIterationIndexes

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

^setIterationIndexes
%

category: 'Updating'
method: RangeIndexReadStream
setIterationIndexes: newValue

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

setIterationIndexes := newValue
%

category: 'Private'
method: RangeIndexReadStream
nextValueInBag: aBag level: offset

"Get the next value in the given BucketValueBag and update the set iteration
 indexes accordingly."

| setOffset |

setOffset := (setIterationIndexes at: offset) + 1.

setIterationIndexes at: offset put:
  (setOffset == aBag size ifTrue: [ 0 ] ifFalse: [ setOffset ]).

" return the object in the set "
^ aBag _at: setOffset.
%

category: 'Query Support'
method: RangeIndexReadStream
makeNsc

"Returns a new NSC that contains all the elements from the receiver."

| tmpHolder |
tmpHolder := NscBuilder for: rangeIndex nscRoot speciesForSelect new max: rangeIndex nscRoot size.

" If B-tree contains mapping to root object "
rangeIndex size == 1
    ifTrue: [ self _valuesAddInto: tmpHolder ]
    ifFalse: [ self _uniqueValuesAddInto: tmpHolder ].

" make the stream be at the end "
currentStack at: 1 put: 0.

^ tmpHolder completeBag
%

category: 'Query Support'
method: RangeIndexReadStream
_uniqueValuesAddInto: collection

"Iterates over the unique values of the B-tree, traversing the reverse mappings
 to the root object.  Adds the root to the given NSC."

| querySpec |
querySpec := BtreeQuerySpec new.
^self _uniqueValuesAddInto: collection spec: querySpec
%

category: 'Query Support'
method: RangeIndexReadStream
_uniqueValuesAddInto: collection comparing: aSelector key: aKey

"Iterates over the unique values of the B-tree, traversing the reverse mappings
 to the root object.  Adds the root to the given NSC."

| querySpec |
querySpec := BtreeComparisonQuerySpec key: aKey selector: aSelector.
^self _uniqueValuesAddInto: collection spec: querySpec
%

category: 'Query Support'
method: RangeIndexReadStream
_uniqueValuesAddInto: collection comparing: aSelector1 key: aKey1 and: aSelector2 key: aKey2

"Iterates over the unique values of the B-tree, traversing the reverse mappings
 to the root object.  Adds the root to the given NSC."

| querySpec |
querySpec := BtreeRangeComparisonQuerySpec key: aKey1 selector: aSelector1 and: aKey2 selector: aSelector2.
^self _uniqueValuesAddInto: collection spec: querySpec
%

category: 'Query Support'
method: RangeIndexReadStream
_uniqueValuesAddInto: collection identicalTo: aKey

"Iterate over the unique values of the B-tree, traversing the reverse mappings
 to the root object.  For each entry whose key is identical to the given key,
 add the root to the given NSC."

| querySpec |
querySpec := BtreeComparisonQuerySpec key: aKey selector: #==.
^self _uniqueValuesAddInto: collection spec: querySpec
%

category: 'Query Support'
method: RangeIndexReadStream
_uniqueValuesAddInto: collection notEqualTo: aKey

"Iterate over the unique values of the B-tree, traversing the reverse mappings
 to the root object.  For each entry whose key is not identical to the given
 key, add the root to the given NSC."

| querySpec |
querySpec := BtreeComparisonQuerySpec key: aKey selector: #~=.
^self _uniqueValuesAddInto: collection spec: querySpec
%

category: 'Query Support'
method: RangeIndexReadStream
_uniqueValuesAddInto: collection notIdenticalTo: aKey

"Iterate over the unique values of the B-tree, traversing the reverse mappings
 to the root object.  For each entry whose key is not identical to the given
 key, add the root to the given NSC."

| querySpec |
querySpec := BtreeComparisonQuerySpec key: aKey selector: #~~.
^self _uniqueValuesAddInto: collection spec: querySpec
%

category: 'Query Support'
method: RangeIndexReadStream
_uniqueValuesAddInto: collection spec: querySpec

"Iterates over the unique values of the B-tree, traversing the reverse mappings
 to the root object.  Adds the root to the given NSC."

| leaf index eSize prevVal offset pathTerm |

" get the index into the leaf node and see if it has reached the end "
index := currentStack at: 1.
index == 0
    ifTrue: [ ^ self ].

rangeIndex isIndexOnNscElements
    ifTrue: [ offset := rangeIndex size ]
    ifFalse: [ offset := rangeIndex lastPathComponentsDictionaryOffset max: 1 ].
pathTerm := rangeIndex at: offset.

leaf := currentStack at: 2.
eSize := endNode entrySize.
prevVal := #_incompletePathTraversal.
querySpec rangeIndex: rangeIndex offset: offset pathTerm: pathTerm.
" while current node is not the end node "
[ leaf ~~ endNode ] whileTrue: [
    " iterate through the values of the current node "
  prevVal := querySpec traverseValuesFrom: leaf
                                  start: index
                                  end: leaf numElements * eSize 
                                  previous: prevVal
                                  into: collection.
    " look down the stack for the next leaf node "
    self _nextLeaf.

    leaf := currentStack at: 2.
    index := 1
].
" iterate through the values of the end node "
querySpec traverseValuesFrom: leaf
                 start: index
                 end: endIndex 
                 previous: prevVal
                 into: collection. 
%

category: 'Query Support'
method: RangeIndexReadStream
approxNumElements

"Returns the number of leaf node entries represented by the receiver.  This is
 approximately the number of elements because objects may share sub-objects
 which converge to the same leaf node entry."

| result |
self atEnd
  ifTrue: [
    ^ 0
  ].

result := Array with: 0.

(currentStack at: currentStack size) numElementsInStack: currentStack 
    level:  currentStack size - 1
    endNode: endNode
    endIndex: endIndex 
    into: result.

" Add one because the last entry is not counted "
^ (result at: 1) + 1
%

category: 'Copying'
method: RangeIndexReadStream
copy

"Returns a copy of the receiver."

| newOne |

newOne := super copy.
newOne currentStack: currentStack copy.
^ newOne
%

! deleted RangeIndexReadStream>>makeNscFilterSymbols: v2.1

category: 'Query Support'
method: RangeIndexReadStream
makeNscComparing: aSelector key: aKey

"Returns a new NSC that contains all the elements from the receiver."

| tmpHolder |
tmpHolder := NscBuilder for: rangeIndex nscRoot speciesForSelect new max: rangeIndex nscRoot size.

" If B-tree contains mapping to root object "
rangeIndex size == 1
    ifTrue: [ self _valuesAddInto: tmpHolder comparing: aSelector key: aKey]
    ifFalse: [ self _uniqueValuesAddInto: tmpHolder comparing: aSelector key: aKey].

" make the stream be at the end "
currentStack at: 1 put: 0.

^ tmpHolder completeBag
%

category: 'Query Support'
method: RangeIndexReadStream
makeNscComparing: aSelector1 key: aKey1 and: aSelector2 key: aKey2

"Returns a new NSC that contains all the elements from the receiver."

| tmpHolder |
tmpHolder := NscBuilder for: rangeIndex nscRoot speciesForSelect new max: rangeIndex nscRoot size.

" If B-tree contains mapping to root object "
rangeIndex size == 1
    ifTrue: [ self _valuesAddInto: tmpHolder comparing: aSelector1 key: aKey1 and: aSelector2 key: aKey2]
    ifFalse: [ self _uniqueValuesAddInto: tmpHolder comparing: aSelector1 key: aKey1 and: aSelector2 key: aKey2].

" make the stream be at the end "
currentStack at: 1 put: 0.

^ tmpHolder completeBag
%

category: 'Query Support'
method: RangeIndexReadStream
getNextValueComparing: aSelector key: aKey

" iterate through the stream, return first value that satisfies query "

[ self atEnd not ] whileTrue: [
  (self _peekKey perform: aSelector with: aKey)
        ifTrue: [ ^ self getNext ].
    self _btreeNext
].

^ #_incompletePathTraversal
%

category: 'Query Support'
method: RangeIndexReadStream
getNextValueComparing: aSelector1 key: aKey1 and: aSelector2 key: aKey2

" iterate through the stream, return first value that satisfies query "

[ self atEnd not ] whileTrue: [
  | key |
  key := self _peekKey.
  ((key perform: aSelector1 with: aKey1) _and: [ key perform: aSelector2 with: aKey2 ])
        ifTrue: [ ^ self getNext ].
    self _btreeNext
].

^ #_incompletePathTraversal
%

category: 'Private'
method: RangeIndexReadStream
initializeSetIterationIndexes

"Initialize the setIterationIndexes for a new instance."

rangeIndex == nil
  ifTrue: [ ^ self ].

setIterationIndexes := Array new: rangeIndex lastPathComponentsDictionaryOffset.
1 to: setIterationIndexes size do: [ :i | setIterationIndexes at: i put: 0 ].
%

category: 'Private'
method: RangeIndexReadStream
noMoreSetsToIterate

"Return whether the setIterationIndexes specifies any more sets over
which to iterate."

1 to: setIterationIndexes size do: [ :i |
  (setIterationIndexes at: i) > 0
    ifTrue: [ ^ false ]
].
^ true
%

