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

removeallmethods IdentityIndex
removeallclassmethods IdentityIndex

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

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

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

doc documentClassWith: txt.

self description: doc.
%

! ------------------- Instance methods for IdentityIndex
category: 'Error Handling'
method: IdentityIndex
_errorCannotInvokeRangeOperationOnIdentityIndex

"An attempt was made to use a range operation (< > = <= >=) on a path
 expression only supported by an identity index."

^ self _error: #rtErrIdentityIndexCannotInvokeRangeOperation
%

category: 'Searching'
method: IdentityIndex
_findFirstUnsharedPathTerm

"Returns the first path term that is not shared by any other indexes.  This is
 the first path term whose only index is the receiver.  If one is not found
 (the index shares the same path with another index), then returns nil."

| pathTerm sz indexObj |

sz := self size.
1 to: sz do: [ :i |
    pathTerm := self at: i.
    ( pathTerm size == 1 _and: [ self == (pathTerm at: 1) ] )
        ifTrue: [ ^ pathTerm ]
].
" check if there is an equality and identity index on the same path "
pathTerm := self at: self size.
" for each index that utilizes the last path term "
1 to: pathTerm size do: [ :i |
    indexObj := pathTerm at: i.
    " if some other index has the same number of path terms "
    ( indexObj ~~ self _and: [ indexObj size == sz ] )
        ifTrue: [ ^ pathTerm ]
].
^ nil
%

! deleted IdentityIndex>>changingSizeOfByteObject:to:

! deleted IdentityIndex>>findAllValuesEqualTo: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findAllValuesGreaterThanKey:andEquals: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findAllValuesIdenticalTo: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findAllValuesLessThanKey:andEquals: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findAllValuesNotEqualTo: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findAllValuesNotIdenticalTo: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findFirstValueEqualTo: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findFirstValueGreaterThanKey:andEquals: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findFirstValueIdenticalTo: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findFirstValueLessThanKey:andEquals: (see IdentityIndexQueryEvaluator)

! deleted IdentityIndex>>findFirstValueNotEqualTo: (see IdentityIndexQueryEvaluator)

category: 'Accessing'
method: IdentityIndex
firstPathTerm

"Returns the first path term in the path components list."

^ self at: 1
%

category: 'Testing'
method: IdentityIndex
hasIndexOnPath: pathArray

"Returns whether the receiver's path components match the path represented by
 pathArray."

self size == pathArray size
    ifFalse: [ ^ false ].

1 to: self size do: [ :i |
    (self at: i) name == (pathArray at: i)
        ifFalse: [ ^ false ]
].
^ isComplete
%

category: 'Testing'
method: IdentityIndex
hasIndexOnPath: pathArray startingAt: offset

"Returns whether the receiver's path components match the path represented by
 pathArray."

self size - offset + 1 == pathArray size
  ifFalse: [ ^ false ].

offset to: self size do: [ :i |
  (self at: i) name == (pathArray at: i + 1 - offset)
    ifFalse: [ ^ false ].
].
^ isComplete
%

category: 'Accessing'
method: IdentityIndex
indexDictionary

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

^indexDictionary
%

category: 'Updating'
method: IdentityIndex
indexDictionary: newValue

"Modifies the value of the instance variable 'indexDictionary'."

indexDictionary := newValue
%

category: 'Testing'
method: IdentityIndex
isIdentityIndex

"Returns true."

^ true
%

category: 'Testing'
method: IdentityIndex
isIndexOnNscElements

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

^ self lastPathTerm indicatesIndexOnNscElements
%

category: 'Testing'
method: IdentityIndex
isIndexOnRootNsc

"Returns whether the receiver is an index directly on the elements of
 the root NSC."

^ self lastPathTerm indicatesIndexOnRootNsc
%

category: 'Testing'
method: IdentityIndex
isPathEvaluator

"Returns false."

^ false
%

category: 'Testing'
method: IdentityIndex
isRangeEqualityIndex

"Returns false."

^ false
%

category: 'Accessing'
method: IdentityIndex
lastPathComponentsDictionaryOffset

"Returns the last index into the path components list that is mapped in the
 index dictionary (as opposed to the B-tree)."

^ self size
%

category: 'Accessing'
method: IdentityIndex
lastPathTerm

"Returns the last path term in the path components list."

^ self at: self size
%

category: 'Accessing'
method: IdentityIndex
nscRoot

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

^nscRoot
%

category: 'Updating'
method: IdentityIndex
nscRoot: newValue

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

nscRoot := newValue
%

category: 'Accessing'
method: IdentityIndex
pathComponentsString

"Returns a string of the path components."

^ self pathComponentsStringStartingAt: 1
%

category: 'Repository Conversion'
method: IdentityIndex
pathComponentsStringVersion: versionNum

"This method is a kludge.  The right way would be to have corresponding
 obsolete classes because instance variables were added going from 4.1 to 5.0."

(versionNum == 50)
  ifTrue: [ ^ self pathComponentsString ].

(versionNum == 41)
  ifTrue: [ ^ self pathComponentsString41 ].

self halt: 'Invalid version num specified.'
%

category: 'Repository Conversion'
method: IdentityIndex
pathComponentsString41

"This method is a kludge.  The right way would be to have corresponding
 obsolete classes because instance variables were added going from 4.1 to 5.0."

| objSize newIdIndex numInstVars |

newIdIndex := IdentityIndex new.

numInstVars := IdentityIndex allInstVarNames size.
objSize := self _primitiveSize.

(objSize > numInstVars)
  ifTrue: [
    4 to: numInstVars do: [ :i |
      newIdIndex add: (self instVarAt: i).
      ].

    1 to: self size do: [ :i |
      newIdIndex add: (self at: i).
      ].
    ]
  ifFalse: [
    4 to: objSize do:  [ :i |
      newIdIndex add: (self instVarAt: i).
      ].
    ].

^ newIdIndex pathComponentsString.
%

category: 'Accessing'
method: IdentityIndex
pathComponentsStringStartingAt: offset

"Returns a string of the path components."

| str sz |

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

category: 'Accessing'
method: IdentityIndex
pathSize

"Returns the number of path terms."

^ self size
%

category: 'Traversing'
method: IdentityIndex
traverse: anObject upTo: aPathTerm

"Traverse the sub-objects of the given object using the path terms of the
 receiver up to the given path term.  This method assumes that the index is not
 over a set-valued instance variable.  For that kind of index, use the
 traverse:upTo:startingAt:into: method."

| nextObj ivOffset sz i|

nil == anObject
    ifTrue: [ ^ anObject ].
nextObj := anObject.
sz := self size.
i := 1.
[ (self at: i) ~~ aPathTerm _and: [ i <= sz ] ] whileTrue: [
    ivOffset := nextObj _findIVOffsetForPathTerm: (self at: i).
    ivOffset == nil
        ifTrue: [
            nextObj _errorPathTermNotInDependencyList: (self at: i)  .
            nextObj := nil
            ]
        ifFalse:[ nextObj := nextObj instVarAt: ivOffset ] .
    nil == nextObj
        ifTrue: [ ^ nextObj ].
    i := i + 1
].
^ nextObj
%

category: 'Traversing'
method: IdentityIndex
traverse: anObject
upTo: aPathTerm
startingAt: anOffset
into: resultSet
addingIndex: addingIndex

"Traverse the sub-objects of anObject, using the path terms of the receiver up
 to the given path term.  Add each object at the end of the traversal to the
 result set.  If addingIndex is true, add an index list entry for the
 receiver to any NSCs encountered along the path; if false, remove an index
 list entry for the receiver.  Returns the result set."

| nextObj ivOffset sz i |

nextObj := anObject.
sz := self size.
i := anOffset.
[ (self at: i) ~~ aPathTerm _and:
[ (i <= sz) _and:
[ nil ~~ nextObj ] ] ] whileTrue: [

    " get the next object along the path "
    ivOffset := nextObj _findIVOffsetForPathTerm: (self at: i).
    ivOffset == nil
      ifTrue: [
        nextObj _errorPathTermNotInDependencyList: (self at: i) .
        nextObj := nil
        ]
      ifFalse:[ nextObj := nextObj instVarAt: ivOffset ] .
    i := i + 1
].
( nil ~~ nextObj _and: [ resultSet ~~ nil ] )
    ifTrue: [ resultSet add: nextObj ].

^ resultSet
%

category: 'Traversing'
method: IdentityIndex
traverseObject: anObject

"Traverse the sub-objects of the given object using the path terms 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
 incompletePathTraversal object."

self isIndexOnNscElements
    ifTrue: [ ^ anObject ].

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

^ self _traverseObject: anObject
%

category: 'Updating Indexes'
method: IdentityIndex
updatingByteObject: aByteObject startingAt: startPt withNewValue: newValue

"The byte object is being updated.  For identity indexes, nothing is required
 to keep the index up to date."

%

category: 'Traversing'
method: IdentityIndex
_traverseObject: anObject

"Traverse the sub-objects of the given object using the path terms 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
 incompletePathTraversal object."

| nextObj ivOffset sz pathTerm |

nextObj := anObject.
sz := self size.
1 to: sz do: [ :i |
    pathTerm := self at: i.
    ivOffset := nextObj _findIVOffsetForPathTerm: pathTerm.
    ivOffset == nil
        ifTrue: [
           nextObj _errorPathTermNotInDependencyList: pathTerm .
           nextObj := nil
           ]
        ifFalse:[ nextObj := nextObj instVarAt: ivOffset ] .

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

category: 'Updating Indexes'
method: IdentityIndex
addDirectMappingsFor: aBag
indexList: iList
updateIndexListSegments: aBoolean

"Add an entry for an index on the elements of the NSC."

| anObject firstPathTerm indexMgr |

indexMgr := self indexManager.

firstPathTerm := self firstPathTerm.
indexMgr autoCommit
  ifTrue: [ nscRoot _lockForIndexCreation ].

aBoolean
  ifTrue: [
    1 to: aBag size do: [ :i |
      anObject := aBag _at: i.
      " insert an entry into the index dictionary "
      indexDictionary
        _at: anObject
        put: anObject
        term: firstPathTerm
        logging: false.
      indexMgr commitIndexMaintenance: self at: i.
    ]
  ]
  ifFalse: [
    1 to: aBag size do: [ :i |
      anObject := aBag _at: i.
      " insert an entry into the index dictionary "
      indexDictionary
        _at: anObject
        put: anObject
        term: firstPathTerm
        logging: false.
      indexMgr commitIndexMaintenance: self at: i.
    ]
  ]
%

category: 'Updating Indexes'
method: IdentityIndex
preIndexRemoval

isComplete := false.
progress := 0.

^ nil
%

category: 'Updating Indexes'
method: IdentityIndex
preIndexCreation

"Initializes the working set of collision buckets in the index dictionary."

isComplete := false.
progress := 0.

indexDictionary ~~ nil
  ifTrue: [ indexDictionary initializeWorkingSetInterval: self ].
^ nil
%

category: 'Updating Indexes'
method: IdentityIndex
postIndexCreation: original

"Iterates through all ID entry holders, updating the index dictionary."

" indicate that it is ok to update the dictionary "
indexDictionary ~~ nil
  ifTrue: [ | array |
    " get the Array of IndexDictionaryEntryHolder's "
    array := indexDictionary entryHolders.
    1 to: array size do: [ :i | | holder |
      holder := array at: i.
      holder update.
      holder size: 0.
    ].
    " nil out the Array of ID entry holders for garbage collection "
    indexDictionary entryHolders: nil.
    indexDictionary okToUpdate: true.
    "now ensure there's room for growth"
    indexDictionary rebuildTable: 0 for: self.
  ].

isComplete := true.

self indexManager autoCommit
  ifTrue: [ |systm |
    systm := System .
    systm commitTransaction
      ifFalse: [ nscRoot _errorCouldNotCommitDuringIndexCreation ].
    systm transactionMode == #manualBegin ifTrue:
        [systm beginTransaction]
  ]
%

! deleted IdentityIndex>>findFirstValueNotIdenticalTo:(see IdentityIndexQueryEvaluator)

category: 'Support'
method: IdentityIndex
asPathEvaluator

"Returns a PathEvaluator over the same path as the receiver."

| pathEval |

pathEval := PathEvaluator new.

1 to: self size do: [ :i |
    pathEval add: (self at: i) name
].
pathEval nsc: nscRoot.
^ pathEval asMostSpecificType
%

category: 'Support'
method: IdentityIndex
_addAllFor: aKey
into: anNscBuilderOrArray
offset: offset
pathTerm: pathTerm

"Traverse the reverse mappings for the given key, adding the object at the end
 of the traversal to the given NscBuilder or Array."

<primitive: 328>
self _primitiveFailed: #_addAllFor:into:offset:pathTerm: .
self _uncontinuableError
%

category: 'Updating'
method: IdentityIndex
_setPathTermState

"For each path term, indicate the need to update the index dictionary."

1 to: self size do: [ :i |
    (self at: i) updateDict: indexDictionary.
]
%

category: 'Support'
method: IdentityIndex
hasSamePathAs: anIndex

"Returns whether the given index is on the same path as the receiver.
 This can only be true if the index is identical."

^ self == anIndex
%

category: 'Indexing Support'
method: IdentityIndex
_partialPathComponentsStringUpTo: offset

"Returns the path components string up to the given offset."

| str |
str := String new.
1 to: (offset min: self size) do: [ :i |
    str add: (self at: i) name.
    i == offset
        ifFalse: [ str add: $. ]
].
^ str
%

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

"Traverse the sub-objects of the given object using the path terms 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 ivOffset sz pathTerm |

nextObj := anObject.
sz := self size.
1 to: sz do: [ :i |
    pathTerm := self at: i.
    ivOffset := nextObj _findIVOffsetForPathTerm: pathTerm.
    ivOffset == nil
        ifTrue: [
           nextObj _errorPathTermNotInDependencyList: pathTerm .
           nextObj := nil
           ]
        ifFalse:[ nextObj := nextObj instVarAt: ivOffset ] .

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

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

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

"Traverse the sub-objects of the given object using the path terms 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: 'Accessing'
method: IdentityIndex
lastElementClass

" "

^ nscRoot getLastElementConstraintOnPath: self pathComponentsString
%

category: 'Accessing'
method: IdentityIndex
sizeForNscBuilder

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

^ nscRoot size
%

category: 'Formatting'
method: IdentityIndex
printOn: aStream

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

"Copy the implementation from Object so we don't inherit it from Collection."

aStream nextPutAll: self asString
%

category: 'Clustering'
method: IdentityIndex
clusterDepthFirst

"Cluster the receiver.  Only need to cluster the path terms."

1 to: self size do: [ :i |
  (self at: i) clusterDepthFirst
]
%

category: 'Statistics'
method: IdentityIndex
_statisticsInto: dict

"Puts statistical information into the given dictionary."

%

! Deleted:  _obj: anObject isIdenticalTo: anotherObject

category: 'Testing'
method: IdentityIndex
_isIndexObject

"Returns true if the receiver is an index object; returns false otherwise."

^ true
%

category: 'Accessing'
method: IdentityIndex
progress

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

^progress
%

category: 'Testing'
method: IdentityIndex
isComplete

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

isComplete == nil
  ifTrue: [ ^ true ].
^ isComplete
%

category: 'Accessing'
method: IdentityIndex
nwayMergeProgress

"Returns nil (identity indexes do not utilize n-way merge)."

^ nil
%

category: 'Updating'
method: IdentityIndex
progress: newValue

"Modifies the value of the instance variable 'progress'."

progress := newValue
%

category: 'Updating'
method: IdentityIndex
isComplete: newValue

"Modifies the value of the instance variable 'isComplete'."

isComplete := newValue
%

category: 'Updating'
method: IdentityIndex
_clear

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

indexDictionary := nil.
nscRoot := nil.
rcRangeBucket := nil.
self size: 0.
%

category: 'Converting'
method: IdentityIndex
asQueryEvaluator

  ^IdentityIndexQueryEvaluator on: self
%
