[Abora-cvs] abora-ash/src/org/abora/ash/ent NonSameBranchException.java,NONE,1.1 EntException.java,N
Status: Alpha
Brought to you by:
dgjones
From: <dg...@us...> - 2003-05-04 18:32:11
|
Update of /cvsroot/abora/abora-ash/src/org/abora/ash/ent In directory sc8-pr-cvs1:/tmp/cvs-serv3306/src/org/abora/ash/ent Added Files: NonSameBranchException.java EntException.java SplitNode.java CollectionLeaf.java LeafNode.java NonCompatibleBranchException.java ContentLeaf.java EntNode.java SequenceNumber.java ChildNode.java RootNode.java Log Message: -Add start of Ash subproject, which is currently the beginnings of a port of the dolphin-demo smalltalk server code --- NEW FILE: NonSameBranchException.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; /** */ public class NonSameBranchException extends EntException { /** * Constructor for NonSameBranchException. */ public NonSameBranchException() { super(); } /** * Constructor for NonSameBranchException. * @param message */ public NonSameBranchException(String message) { super(message); } /** * Constructor for NonSameBranchException. * @param message * @param cause */ public NonSameBranchException(String message, Throwable cause) { super(message, cause); } /** * Constructor for NonSameBranchException. * @param cause */ public NonSameBranchException(Throwable cause) { super(cause); } } --- NEW FILE: EntException.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; import org.abora.ash.engine.AboraException; /** */ public class EntException extends AboraException { /** * Constructor for EntException. */ public EntException() { super(); } /** * Constructor for EntException. * @param message */ public EntException(String message) { super(message); } /** * Constructor for EntException. * @param message * @param cause */ public EntException(String message, Throwable cause) { super(message, cause); } /** * Constructor for EntException. * @param cause */ public EntException(Throwable cause) { super(cause); } } --- NEW FILE: SplitNode.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; import java.util.ArrayList; import java.util.List; /** */ public class SplitNode extends ChildNode { private int split = 0; private EntNode left = null; private EntNode right = null; private int leftDsp = 0; private int rightDsp = 0; public SplitNode(SequenceNumber branch, int split, EntNode left, int leftDsp, EntNode right) { this(branch, split, left, leftDsp, right, 0); } public SplitNode(SequenceNumber branch, int split, EntNode left, int leftDsp, EntNode right, int rightDsp) { super(branch); this.split = split; this.left = left; this.leftDsp = leftDsp; this.right = right; this.rightDsp = rightDsp; } public SplitNode(SequenceNumber branch, int split, EntNode left, EntNode right) { this(branch, split, left, 0, right, 0); } public SplitNode(SequenceNumber branch, int split, EntNode left, EntNode right, int rightDsp) { this(branch, split, left, 0, right, rightDsp); } private int applyDsp(int dsp, int position) { return position + invertDsp(dsp); } protected void assertIsChild(EntNode node) { if (left != node && right != node) { throw new IllegalStateException("unknown child"); } } // assertIsChild: node // (left ~~ node and: [right ~~ node]) ifTrue: [EntError signal: 'unknown child']. // #todo "ensure left !!= right"! protected EntNode basicParentSplit() { return this; } // basicSplayFor: matchingBranch // | parent grandParent newSelf | // parent := self parentSplit: matchingBranch. // parent isNil ifTrue: [^self]. // grandParent := parent parentSplit: matchingBranch. // newSelf := parent left == self // ifTrue: // [(grandParent notNil and: [parent = grandParent left]) // ifTrue: [parent := grandParent singleRotateLeftFor: matchingBranch]. // parent singleRotateLeftFor: matchingBranch] // ifFalse: // [(grandParent notNil and: [parent = grandParent right]) // ifTrue: [parent := grandParent singleRotateRightFor: matchingBranch]. // parent singleRotateRightFor: matchingBranch]. // newSelf basicSplayFor: matchingBranch. // ^newSelf! // // childFor: position do: duadic // | child dsp dspPosition | // (self isLeft: position) // ifTrue: // [child := self left. // dsp := self leftDsp] // ifFalse: // [child := self right. // dsp := self rightDsp]. // dspPosition := self applyDsp: dsp to: position. // ^duadic value: child value: dspPosition.! // public List children() { List list = new ArrayList(2); list.add(left); list.add(right); return list; } // children // ^Array with: self left with: self right // ! // // contents // | minElement position | // minElement := self minNode. // position := minElement globalPositionFor: self branch // to: (self singleParentFor: self branch). // ^self contentsFrom: position extent: self count! // // contentsAt: position // ^self childFor: position do: [:child :dspPosition | child contentsAt: dspPosition]! // // contentsFrom: position extent: extent do: operation // position < self split ifTrue: [self left contentsFrom: (self applyDsp: self leftDsp to: position) extent: extent do: operation]. // position + extent > self split ifTrue: [self right contentsFrom: (self applyDsp: self rightDsp to: position) extent: extent do: operation]. // ! // // contentsFrom: position extent: extent into: stream // position < self split ifTrue: [self left contentsFrom: (self applyDsp: self leftDsp to: position) extent: extent into: stream]. // position + extent > self split ifTrue: [self right contentsFrom: (self applyDsp: self rightDsp to: position) extent: extent into: stream]. // ! public int count() { return left.count() + right.count(); } protected void setDspForChild(int dsp, EntNode node) { assertIsChild(node); if (left == node) { leftDsp = dsp; } else if (right == node) { rightDsp = dsp; } } // dsp: dsp forChild: node // self assertIsChild: node. // left == node ifTrue: [self leftDsp: dsp]. // right == node ifTrue: [self rightDsp: dsp]! // // dspForChild: node // self assertIsChild: node. // left == node ifTrue: [^self leftDsp]. // right == node ifTrue: [^self rightDsp]! protected int dspForChild(EntNode node) { if (left == node) { return getLeftDsp(); } else if (right == node) { return getRightDsp(); } else { throw new IllegalArgumentException("unknown child"); } } // // duplicateFor: duplicateBranch // "Answer a copy of the receiver if self isn't of the required revision. // The duplicate connects to self children, but has no parents set." // // self ensureCompatibleFor: duplicateBranch. // #todo. "perhaps ^self in this case?" // (self isSameFor: duplicateBranch) ifTrue: [^self]. // ^self class // branch: duplicateBranch // split: self split // left: self left // leftDsp: self leftDsp // right: self right // rightDsp: self rightDsp! public EntNode duplicateFor(SequenceNumber duplicateBranch) throws NonCompatibleBranchException { ensureCompatibleFor(duplicateBranch); //TODO perhaps return this; in this case? if (isSameFor(duplicateBranch)) { return this; } return new SplitNode(duplicateBranch, getSplit(), getLeft(), getLeftDsp(), getRight(), getRightDsp()); } // // firstNodeFrom: position extent: extent shouldSplit: shouldSplit // ^self childFor: position // do: // [:child :dspPosition | // child // firstNodeFrom: dspPosition // extent: extent // shouldSplit: shouldSplit]! // // insert: dataLeaf at: position root: root // ^self childFor: position do: [:child :dspPosition | child insert: dataLeaf at: dspPosition root: root].! // // invertDsp: dsp // ^dsp negated! protected int invertDsp(int dsp) { return -dsp; } // // isLeft: position // ^position < self split! public EntNode getLeft() { return left; } public void setLeft(EntNode node) { if (left == node) return; if (left != null) left.removeFromParent(this); left = node; node.addToParent(this); } public int getLeftDsp() { return leftDsp; } // nodeAt: position // ^self childFor: position do: [:child :dspPosition | child nodeAt: dspPosition]! // // removeChild: existingChild branch: newBranch // | parent duplicate | // self assertIsChild: existingChild. // (right == existingChild and: [(right isMaxNodeFor: newBranch) not]) // ifTrue: // ["Splay to toggle childs location into one that we can handle" // // ^existingChild removeFor: newBranch]. // "duplicate self and parents chain to root, then just use the duplicates" // #todo. " only need to duplicate if node is being shared with outher versions" // duplicate := (self isSameFor: newBranch) // ifTrue: // ["existingChild" // // self] // ifFalse: [self duplicateWithParentsFor: newBranch]. // parent := duplicate singleParentFor: newBranch. // duplicate left == existingChild // ifTrue: // [parent == (duplicate rootFor: newBranch) // ifTrue: [duplicate removeChildLeftFromRoot] // ifFalse: [duplicate removeChildLeft]] // ifFalse: // [self assert: [duplicate right == existingChild]. // duplicate removeChildRightMaxElement]! protected void removeChildLeft() throws NonCompatibleBranchException { // Assumed to be a duplicate by this point EntNode parent = singleParentFor(branch); parent.setDspForChild(parent.dspForChild(this) + rightDsp, this); parent.replaceChild(this, right); right.removeFromParent(this); SplitNode rootChild = (SplitNode)right.rootFor(branch).getChild(); rootChild.rightDsp = rootChild.rightDsp - left.count(); } // removeChildLeft // "Assumed to be a duplicate by this point" // // | parent rootChild | // parent := self singleParentFor: self branch. // parent dsp: (parent dspForChild: self) + self rightDsp forChild: self. // parent replaceChild: self with: self right. // self right removeFromParent: self. // rootChild := (self right rootFor: self branch) child. // rootChild rightDsp: rootChild rightDsp - self left count! protected void removeChildLeftFromRoot() { EntNode parent = singleParentFor(branch); parent.setDspForChild(parent.dspForChild(this) + rightDsp - left.count(), this); parent.replaceChild(this, right); right.removeFromParent(this); } // removeChildLeftFromRoot // | parent | // parent := self singleParentFor: self branch. // parent dsp: (parent dspForChild: self) + self rightDsp - self left count forChild: self. // parent replaceChild: self with: self right. // self right removeFromParent: self! // removeChildRightMaxElement // | parent | // parent := self singleParentFor: self branch. // parent dsp: (parent dspForChild: self) + self leftDsp forChild: self. // parent replaceChild: self with: self left. // self left removeFromParent: self! // protected void replaceChild(EntNode existingChild, EntNode newChild) { assertIsChild(existingChild); if (left == existingChild) { setLeft(newChild); } else if (right == newChild) { setRight(newChild); } } // replaceChild: existingChild with: newChild // self assertIsChild: existingChild. // left == existingChild ifTrue: [self left: newChild]. // right == existingChild ifTrue: [self right: newChild]! // // right // ^right! public EntNode getRight() { return right; } public void setRight(EntNode node) { if (right == node) return; if (right != null) right.removeFromParent(this); right = node; node.addToParent(this); } // // right: aNode // right == aNode ifTrue: [^self]. // right notNil ifTrue: [right removeFromParent: self]. // right := aNode. // aNode addToParent: self! // // rightDsp // ^rightDsp! public int getRightDsp() { return rightDsp; } // // rightDsp: anObject // rightDsp := anObject! // // singleRotateLeftFor: matchingBranch // | node2 b c d e | // node2 := self left. // self assert: [node2 isMemberOf: self class]. // #todo. "mess!! + dont have to duplicate every different root branching off at this point" // (self isSameFor: node2 branch) // ifFalse: // [| myNewNode2 | // self assert: [node2 branch < self branch]. // node2 parents asArray do: // [:node2Parent | // | newNode2 | // node2Parent ~~ self // ifTrue: // [newNode2 := node2 duplicateFor: node2Parent branch. // node2Parent replaceChild: node2 with: newNode2]]. // myNewNode2 := node2 duplicateFor: self branch. // self replaceChild: node2 with: myNewNode2. // ^self singleRotateLeftFor: matchingBranch]. // node2 parents asArray do: // [:node2Parent | // | newNode2 | // node2Parent ~~ self // ifTrue: // [newNode2 := node2 duplicateFor: node2Parent branch. // node2Parent replaceChild: node2 with: newNode2]]. // "parent := self singleParent: matchingRevision." // "get existing dsp's" // "a := parent dspForChild: self." // b := self leftDsp. // c := self rightDsp. // d := self left leftDsp. // e := self left rightDsp. // "rotate left child (node2) to become parent of self" // self left: node2 right. // "parent replaceChild: self with: node2." // "update all dsps" // self parents do: // [:parent | // | a | // a := parent dspForChild: self. // parent replaceChild: self with: node2. // parent dsp: a + b forChild: node2]. // node2 right: self. // node2 leftDsp: d. // node2 rightDsp: b negated. // self leftDsp: b + e. // self rightDsp: c. // ^node2! // // singleRotateRightFor: matchingBranch // | node2 b c d e | // node2 := self right. // self assert: [node2 isMemberOf: self class]. // #todo. "mess!! + dont have to duplicate every different root branching off at this point" // (self isSameFor: node2 branch) // ifFalse: // [| myNewNode2 | // self assert: [node2 branch < self branch]. // node2 parents asArray do: // [:node2Parent | // | newNode2 | // node2Parent ~~ self // ifTrue: // [newNode2 := node2 duplicateFor: node2Parent branch. // node2Parent replaceChild: node2 with: newNode2]]. // myNewNode2 := node2 duplicateFor: self branch. // self replaceChild: node2 with: myNewNode2. // ^self singleRotateRightFor: matchingBranch]. // node2 parents asArray do: // [:node2Parent | // | newNode2 | // node2Parent ~~ self // ifTrue: // [newNode2 := node2 duplicateFor: node2Parent branch. // node2Parent replaceChild: node2 with: newNode2]]. // "parent := self singleParent: matchingRevision." // "get existing dsp's" // "a := parent dspForChild: self." // b := self leftDsp. // c := self rightDsp. // d := self right leftDsp. // e := self right rightDsp. // "rotate right child (node2) to become parent of self" // self right: node2 left. // "parent replaceChild: self with: node2." // "update all dsps" // self parents do: // [:parent | // | a | // a := parent dspForChild: self. // parent replaceChild: self with: node2. // parent dsp: a + c forChild: node2]. // node2 left: self. // node2 leftDsp: c negated. // node2 rightDsp: e. // node2 left leftDsp: b. // node2 left rightDsp: c + d. // ^node2! // // split // ^split! public int getSplit() { return split; } // // split: anObject // split := anObject! // // transclusionsFrom: position extent: extent found: transclusions // position < self split ifTrue: [self left transclusionsFrom: (self applyDsp: self leftDsp to: position) extent: extent found: transclusions]. // position + extent > self split ifTrue: [self right transclusionsFrom: (self applyDsp: self rightDsp to: position) extent: extent found: transclusions]. // ! ! // !SplitNode categoriesFor: #applyDsp:to:!private! ! // !SplitNode categoriesFor: #assertIsChild:!private! ! // !SplitNode categoriesFor: #basicParentSplit!accessing!private! ! // !SplitNode categoriesFor: #basicSplayFor:!private! ! // !SplitNode categoriesFor: #childFor:do:!private! ! // !SplitNode categoriesFor: #children!public! ! // !SplitNode categoriesFor: #contents!public! ! // !SplitNode categoriesFor: #contentsAt:!public! ! // !SplitNode categoriesFor: #contentsFrom:extent:do:!accessing!public! ! // !SplitNode categoriesFor: #contentsFrom:extent:into:!accessing!public! ! // !SplitNode categoriesFor: #count!accessing!public! ! // !SplitNode categoriesFor: #dsp:forChild:!private! ! // !SplitNode categoriesFor: #dspForChild:!private! ! // !SplitNode categoriesFor: #duplicateFor:!private! ! // !SplitNode categoriesFor: #firstNodeFrom:extent:shouldSplit:!public! ! // !SplitNode categoriesFor: #insert:at:root:!public! ! // !SplitNode categoriesFor: #invertDsp:!private! ! // !SplitNode categoriesFor: #isLeft:!public! ! // !SplitNode categoriesFor: #left!accessing!private! ! // !SplitNode categoriesFor: #left:!accessing!private! ! // !SplitNode categoriesFor: #leftDsp!accessing!private! ! // !SplitNode categoriesFor: #leftDsp:!accessing!private! ! // !SplitNode categoriesFor: #nodeAt:!public! ! // !SplitNode categoriesFor: #removeChild:branch:!public! ! // !SplitNode categoriesFor: #removeChildLeft!private! ! // !SplitNode categoriesFor: #removeChildLeftFromRoot!private! ! // !SplitNode categoriesFor: #removeChildRightMaxElement!private! ! // !SplitNode categoriesFor: #replaceChild:with:!public! ! // !SplitNode categoriesFor: #right!accessing!private! ! // !SplitNode categoriesFor: #right:!accessing!private! ! // !SplitNode categoriesFor: #rightDsp!accessing!private! ! // !SplitNode categoriesFor: #rightDsp:!accessing!private! ! // !SplitNode categoriesFor: #singleRotateLeftFor:!private! ! // !SplitNode categoriesFor: #singleRotateRightFor:!private! ! // !SplitNode categoriesFor: #split!accessing!private! ! // !SplitNode categoriesFor: #split:!accessing!private! ! // !SplitNode categoriesFor: #transclusionsFrom:extent:found:!accessing!public! ! // // // } --- NEW FILE: CollectionLeaf.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; import org.abora.ash.content.BeCollectionHolder; import org.abora.ash.space.IntegerRegion; /** */ public class CollectionLeaf extends LeafNode { protected BeCollectionHolder collectionHolder; protected IntegerRegion collectionRegion; public CollectionLeaf(SequenceNumber branch, int startPosition, byte[] contents) { super(branch, startPosition); //@todo } // "Filed out from Dolphin Smalltalk 2002 release 5.00"! // // LeafNode subclass: #CollectionLeaf // instanceVariableNames: 'collectionHolder collectionRegion' // classVariableNames: '' // poolDictionaries: '' // classInstanceVariableNames: ''! // CollectionLeaf guid: (GUID fromString: '{7CADDDFF-7DC3-488E-B703-E8B48954E7C9}')! // CollectionLeaf comment: ''! // !CollectionLeaf categoriesForClass!Kernel-Objects! ! // !CollectionLeaf methodsFor! // // collectionHolder // ^collectionHolder! // // collectionHolder: aBeCollectionHolder // collectionHolder notNil ifTrue: [collectionHolder removeParent: self]. // collectionHolder := aBeCollectionHolder. // collectionHolder notNil ifTrue: [collectionHolder addParent: self]! // // collectionRegion // ^collectionRegion! // // collectionRegion: anObject // collectionRegion := anObject! // // contentsAtConstrainedIndex: index // ^self collectionHolder collection at: index + collectionRegion startPosition - 1! // // contentsFrom: position extent: extent do: operation // | startIndex endIndex | // startIndex := position - self startPosition + 1. // endIndex := position - self startPosition + extent . // ^(startIndex max: 1) to: (endIndex min: self count) // do: [:index | operation value: (self contentsAtConstrainedIndex: index)]! // // contentsFrom: position extent: extent into: stream // | startIndex endIndex constrainedStartIndex constrainedEndIndex | // startIndex := position - self startPosition + 1. // endIndex := position - self startPosition + extent. // constrainedStartIndex := startIndex max: 1. // constrainedEndIndex := endIndex min: self count. // constrainedEndIndex >= constrainedStartIndex // ifTrue: // [stream // next: constrainedEndIndex - constrainedStartIndex + 1 // putAll: collectionHolder collection // startingAt: constrainedStartIndex + collectionRegion startPosition - 1]! // public int count() { return collectionRegion.getExtent(); } // count // ^self collectionRegion extent! // // elements // ^self collectionHolder collection copyFrom: collectionRegion startPosition // to: collectionRegion endPosition! // // replaceWithSplit: newSplit about: elementsPosition // "Answer a new SplitNode with two data children with elements before and equal and after elementsPosition. // NOTE: This operation is unusual in that all parent versions are redirected to point to the replacement split node." // // | splitNode | // splitNode := super replaceWithSplit: newSplit about: elementsPosition. // self collectionHolder: nil. // ^splitNode! // // sharedWith: anotherEdition for: forBranch mappings: mappings // | anotherEditionBranch | // anotherEditionBranch := anotherEdition branch. // self collectionHolder parents do: // [:leaf | // | root | // (leaf collectionRegion intersects: self collectionRegion) // ifTrue: // [root := leaf rootFor: anotherEditionBranch. // (root notNil and: [root edition == anotherEdition]) // ifTrue: // [| intersection selfRegion anotherRegion | // intersection := self collectionRegion intersection: leaf collectionRegion. // selfRegion := IntegerRegion // startPosition: (self globalPositionFor: forBranch) + intersection startPosition // - self collectionRegion startPosition // extent: intersection extent. // anotherRegion := IntegerRegion // startPosition: (leaf globalPositionFor: anotherEditionBranch) + intersection startPosition // - leaf collectionRegion startPosition // extent: intersection extent. // mappings add: (Array with: selfRegion with: anotherRegion)]]]! // // split: newSplit about: elementsPosition // "Private - Answer a new SplitNode with two data children with elements before and equal and after elementsPosition." // // | dataLeft dataRight | // (elementsPosition < 2 or: [elementsPosition > self count]) // ifTrue: [self errorSubscriptBounds: elementsPosition]. // dataLeft := self class // branch: self branch // startPosition: self startPosition // collection: self collectionHolder // region: (IntegerRegion startPosition: collectionRegion startPosition // endPosition: collectionRegion startPosition + elementsPosition - 2). // dataRight := self class // branch: self branch // startPosition: self startPosition + elementsPosition - 1 // collection: self collectionHolder // region: (IntegerRegion // startPosition: collectionRegion startPosition + elementsPosition - 1 // endPosition: collectionRegion endPosition). // ^SplitNode // branch: self branch // split: newSplit // left: dataLeft // right: dataRight! // // transclusions: transclusions // self collectionHolder parents do: // [:leaf | // (leaf collectionRegion intersects: self collectionRegion) // ifTrue: [transclusions addAll: leaf allEditions]]! // // transclusionsFrom: position extent: extent found: transclusions // | startIndex endIndex constrainedStartIndex constrainedEndIndex | // startIndex := position - self startPosition + 1. // endIndex := position - self startPosition + extent. // constrainedStartIndex := startIndex max: 1. // constrainedEndIndex := endIndex min: self count. // constrainedEndIndex >= constrainedStartIndex // ifTrue: // [self collectionHolder parents do: // [:transcluded | // (transcluded collectionRegion intersects: self collectionRegion) // ifTrue: [transclusions addAll: transcluded allEditions]]]! ! // !CollectionLeaf categoriesFor: #collectionHolder!accessing!private! ! // !CollectionLeaf categoriesFor: #collectionHolder:!accessing!private! ! // !CollectionLeaf categoriesFor: #collectionRegion!accessing!private! ! // !CollectionLeaf categoriesFor: #collectionRegion:!accessing!private! ! // !CollectionLeaf categoriesFor: #contentsAtConstrainedIndex:!private! ! // !CollectionLeaf categoriesFor: #contentsFrom:extent:do:!accessing!public! ! // !CollectionLeaf categoriesFor: #contentsFrom:extent:into:!accessing!public! ! // !CollectionLeaf categoriesFor: #count!public! ! // !CollectionLeaf categoriesFor: #elements!accessing!private! ! // !CollectionLeaf categoriesFor: #replaceWithSplit:about:!public! ! // !CollectionLeaf categoriesFor: #sharedWith:for:mappings:!public! ! // !CollectionLeaf categoriesFor: #split:about:!private! ! // !CollectionLeaf categoriesFor: #transclusions:!public! ! // !CollectionLeaf categoriesFor: #transclusionsFrom:extent:found:!accessing!public! ! // // !CollectionLeaf class methodsFor! // // branch: branch startPosition: startPosition collection: dataCollection // ^self // branch: branch // startPosition: startPosition // collection: dataCollection // region: (IntegerRegion startPosition: 1 endPosition: dataCollection collection size)! // // branch: branch startPosition: startPosition collection: dataCollection region: collectionRegion // ^(self basicNew) // branch: branch; // collectionHolder: dataCollection; // collectionRegion: collectionRegion; // startPosition: startPosition; // yourself! // // branch: branch startPosition: startPosition elements: array // self assert: [array isKindOf: Array]. // ^self // branch: branch // startPosition: startPosition // collection: (BeCollectionHolder collection: array)! // // branch: branch startPosition: startPosition from: collectionLeaf // ^self // branch: branch // startPosition: startPosition // collection: collectionLeaf collectionHolder // region: collectionLeaf collectionRegion! ! // !CollectionLeaf class categoriesFor: #branch:startPosition:collection:!public! ! // !CollectionLeaf class categoriesFor: #branch:startPosition:collection:region:!public! ! // !CollectionLeaf class categoriesFor: #branch:startPosition:elements:!public! ! // !CollectionLeaf class categoriesFor: #branch:startPosition:from:!public! ! // } --- NEW FILE: LeafNode.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; import java.util.Collections; import java.util.List; /** */ public abstract class LeafNode extends ChildNode { protected int startPosition = 0; protected LeafNode(SequenceNumber branch, int startPosition) { super(branch); this.startPosition = startPosition; } // "Filed out from Dolphin Smalltalk 2002 release 5.00"! // // ChildNode subclass: #LeafNode // instanceVariableNames: 'startPosition' // classVariableNames: '' // poolDictionaries: '' // classInstanceVariableNames: ''! // LeafNode guid: (GUID fromString: '{F64D0B1E-54DA-4D5C-97C8-665909D3718D}')! // LeafNode comment: ''! // !LeafNode categoriesForClass!Kernel-Objects! ! // !LeafNode methodsFor! // // basicInsert: dataLeaf at: position root: root // | index | // index := position - startPosition + 1. // (index < 1 or: [index > (self count + 1)]) ifTrue: [self errorSubscriptBounds: position]. // index = 1 // ifTrue: // ["insert before" // // (self isMinNodeFor: root branch) // ifTrue: [self basicInsertBeforeEnt: dataLeaf root: root] // ifFalse: // [^self // basicInsertBeforeSelf: dataLeaf // at: position // root: root]] // ifFalse: // [index <= self count // ifTrue: // ["split then insert" // // | splitNode | // splitNode := self replaceWithSplit: position about: index. // ^splitNode right // insert: dataLeaf // at: position // root: root] // ifFalse: // [(self isMaxNodeFor: root branch) // ifTrue: [self basicInsertAfterEnt: dataLeaf root: root] // ifFalse: [self errorSubscriptBounds: position]]]! // // basicInsertAfterEnt: dataLeaf root: root // "add to end of entire ent" // // | splitNode | // splitNode := SplitNode // branch: root branch // split: dataLeaf startPosition // left: root child // leftDsp: root dsp // right: dataLeaf. // root child: splitNode. // root dsp: 0! // // basicInsertBeforeEnt: dataLeaf root: root // "add to beginning of entire ent" // // | splitNode | // splitNode := SplitNode // branch: root branch // split: dataLeaf count + dataLeaf startPosition // left: dataLeaf // right: root child // rightDsp: dataLeaf count + root dsp. // root child: splitNode. // root dsp: 0! // // basicInsertBeforeSelf: dataLeaf at: position root: root // "add before self" // // | parent newBranch | // newBranch := root branch. // parent := self parentSplit: newBranch. // self assert: [parent notNil]. // self assert: [parent left == self or: [parent right == self]]. // (parent left == self or: [(parent parentSplit: newBranch) isNil]) // ifTrue: // [| a b | // (root child isSameFor: newBranch) ifTrue: [root child right removeFromParent: root child]. // a := SplitNode // branch: newBranch // split: dataLeaf startPosition + dataLeaf count // left: dataLeaf // right: root child right // rightDsp: dataLeaf count + root child rightDsp + root dsp. // (root child isSameFor: newBranch) ifTrue: [root child left removeFromParent: root child]. // b := SplitNode // branch: newBranch // split: dataLeaf startPosition // left: root child left // leftDsp: root child leftDsp + root dsp // right: a. // root child: b. // root dsp: 0] // ifFalse: // ["Force self to other side of parent by splaying" // // parent basicSplayFor: newBranch. // ^self // basicInsert: dataLeaf // at: position // root: root]! // // basicSplayFor: matchingBranch // ^(self singleParentFor: matchingBranch) basicSplayFor: matchingBranch! // // children // ^#() // ! public List children() { return Collections.EMPTY_LIST; } // // constrainedIndexFrom: position // | index | // index := position - self startPosition + 1. // (index < 1 or: [index > self count]) ifTrue: [self errorSubscriptBounds: index]. // ^index! // // contents // ^self contentsFrom: self startPosition extent: self count! // // contentsAt: position // | index | // index := self constrainedIndexFrom: position. // ^self contentsAtConstrainedIndex: index! // // contentsAtConstrainedIndex: index // self subclassResponsibility! // // duplicateFor: newBranch // self shouldNotImplement! public EntNode duplicateFor(SequenceNumber newBranch) { throw new UnsupportedOperationException("duplicateFor"); } // // firstNodeFrom: position extent: extent shouldSplit: shouldSplit // | index | // index := self constrainedIndexFrom: position. // shouldSplit ifFalse: [^self]. // ^index > 1 // ifTrue: // [| newParent | // newParent := self replaceWithSplit: position about: index. // newParent right // firstNodeFrom: position // extent: extent // shouldSplit: shouldSplit] // ifFalse: // [extent < self count // ifTrue: // [| newParent | // newParent := self replaceWithSplit: position + extent about: extent + 1. // newParent left] // ifFalse: [self]]! // // globalPositionFor: matchingBranch // ^self globalPositionFor: matchingBranch to: nil! // // globalPositionFor: matchingBranch to: topParent // | position node parent | // position := self startPosition. // node := self. // [(parent := node singleParentFor: matchingBranch) ~~ topParent] whileTrue: // [position := position + (parent dspForChild: node). // node := parent]. // ^position! // // globalRegionFor: forBranch // ^IntegerRegion startPosition: (self globalPositionFor: forBranch) extent: self count! // // insert: dataLeaf at: position root: root // | index | // index := position - startPosition + 1. // (index < 1 or: [index > (self count + 1)]) ifTrue: [self errorSubscriptBounds: position]. // self basicSplayFor: root branch. // self // basicInsert: dataLeaf // at: position // root: root! // // isMaxNodeFor: matchingBranch // | node parent | // node := self. // [(parent := node parentSplit: matchingBranch) notNil] whileTrue: // [parent right == node ifFalse: [^false]. // node := parent]. // ^true! // // isMinNodeFor: matchingBranch // | node parent | // node := self. // [(parent := node parentSplit: matchingBranch) notNil] whileTrue: // [parent left == node ifFalse: [^false]. // node := parent]. // ^true! // // maxNode // ^self! // // minNode // ^self! // // nodeAt: position // | index | // index := self constrainedIndexFrom: position. // ^self! // // removeFor: newBranch // self basicSplayFor: newBranch. // (self singleParentFor: newBranch) removeChild: self branch: newBranch! // // replaceWithSplit: newSplit about: elementsPosition // "Answer a new SplitNode with two data children with elements before and equal and after elementsPosition. // NOTE: This operation is unusual in that all parent versions are redirected to point to the replacement split node." // // | splitNode | // splitNode := self split: newSplit about: elementsPosition. // self parents do: [:parent | parent replaceChild: self with: splitNode]. // self assert: [self parents isEmpty]. // ^splitNode! // // split: newSplit about: elementsPosition // self subclassResponsibility! // // startPosition // ^startPosition! // // startPosition: anObject // startPosition := anObject! ! // !LeafNode categoriesFor: #basicInsert:at:root:!private! ! // !LeafNode categoriesFor: #basicInsertAfterEnt:root:!private! ! // !LeafNode categoriesFor: #basicInsertBeforeEnt:root:!private! ! // !LeafNode categoriesFor: #basicInsertBeforeSelf:at:root:!private! ! // !LeafNode categoriesFor: #basicSplayFor:!private! ! // !LeafNode categoriesFor: #children!public! ! // !LeafNode categoriesFor: #constrainedIndexFrom:!private! ! // !LeafNode categoriesFor: #contents!public! ! // !LeafNode categoriesFor: #contentsAt:!public! ! // !LeafNode categoriesFor: #contentsAtConstrainedIndex:!public! ! // !LeafNode categoriesFor: #duplicateFor:!public! ! // !LeafNode categoriesFor: #firstNodeFrom:extent:shouldSplit:!public! ! // !LeafNode categoriesFor: #globalPositionFor:!public! ! // !LeafNode categoriesFor: #globalPositionFor:to:!private! ! // !LeafNode categoriesFor: #globalRegionFor:!public! ! // !LeafNode categoriesFor: #insert:at:root:!public! ! // !LeafNode categoriesFor: #isMaxNodeFor:!public! ! // !LeafNode categoriesFor: #isMinNodeFor:!public! ! // !LeafNode categoriesFor: #maxNode!public! ! // !LeafNode categoriesFor: #minNode!public! ! // !LeafNode categoriesFor: #nodeAt:!public! ! // !LeafNode categoriesFor: #removeFor:!public! ! // !LeafNode categoriesFor: #replaceWithSplit:about:!public! ! // !LeafNode categoriesFor: #split:about:!public! ! // !LeafNode categoriesFor: #startPosition!accessing!private! ! // !LeafNode categoriesFor: #startPosition:!accessing!private! ! // // // protected void replaceChild(EntNode existingChild, EntNode newChild) { throw new UnsupportedOperationException("no children"); } protected void setDspForChild(int dsp, EntNode child) { throw new UnsupportedOperationException("no children"); } } --- NEW FILE: NonCompatibleBranchException.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; /** */ public class NonCompatibleBranchException extends EntException { /** * Constructor for NonCompatibleBranchException. */ public NonCompatibleBranchException() { super(); } /** * Constructor for NonCompatibleBranchException. * @param message */ public NonCompatibleBranchException(String message) { super(message); } /** * Constructor for NonCompatibleBranchException. * @param message * @param cause */ public NonCompatibleBranchException(String message, Throwable cause) { super(message, cause); } /** * Constructor for NonCompatibleBranchException. * @param cause */ public NonCompatibleBranchException(Throwable cause) { super(cause); } } --- NEW FILE: ContentLeaf.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; import org.abora.ash.content.BeContentElement; /** */ public class ContentLeaf extends LeafNode { protected BeContentElement contentElement; // "Filed out from Dolphin Smalltalk 2002 release 5.00"! // // LeafNode subclass: #ContentLeaf // instanceVariableNames: 'contentElement' // classVariableNames: '' // poolDictionaries: '' // classInstanceVariableNames: ''! // ContentLeaf guid: (GUID fromString: '{E75C38F4-C73A-4AF8-A275-2FD6FBDD3F4C}')! // ContentLeaf comment: ''! // !ContentLeaf categoriesForClass!Kernel-Objects! ! // !ContentLeaf methodsFor! // // contentElement // ^contentElement! // // contentElement: aContentElement // contentElement notNil ifTrue: [contentElement removeParent: self]. // contentElement := aContentElement. // contentElement notNil ifTrue: [contentElement addParent: self]. // ! // // contentsAtConstrainedIndex: index // self assert: [index = 1]. // ^self contentElement! // // contentsFrom: position extent: extent do: operation // | startIndex endIndex limitedExtent | // startIndex := position - self startPosition + 1 max: 1. // endIndex := position - self startPosition + extent min: self count. // limitedExtent := endIndex - startIndex + 1. // ^limitedExtent > 0 // ifTrue: // [operation value: self contentElement] // ifFalse: ['']! // // contentsFrom: position extent: extent into: stream // | startIndex endIndex limitedExtent | // startIndex := position - self startPosition + 1 max: 1. // endIndex := position - self startPosition + extent min: self count. // limitedExtent := endIndex - startIndex + 1. // limitedExtent > 0 ifTrue: [stream nextPut: self contentElement]! // // count // ^1! // public int count() { return 1; } // printOn: aStream // aStream nextPutAll: 'Data(branch='. // self branch displayOn: aStream. // aStream nextPutAll: ', pos='. // self startPosition printOn: aStream. // aStream nextPutAll: ', data='. // self contentElement printOn: aStream. // aStream nextPutAll: ')'! // // sharedWith: anotherEdition for: forBranch mappings: mappings // | anotherEditionRevision globalRegion | // anotherEditionRevision := anotherEdition branch. // self contentElement parents do: // [:leaf | // | root | // root := leaf rootFor: anotherEditionRevision. // (root notNil and: [root edition == anotherEdition]) // ifTrue: // [| anotherRegion | // globalRegion isNil ifTrue: [globalRegion := self globalRegionFor: forBranch]. // anotherRegion := leaf globalRegionFor: anotherEditionRevision. // mappings add: (Array with: globalRegion with: anotherRegion)]]! // // split: newSplit about: elementsPosition // "Private - Answer a new SplitNode with two data children with elements before and equal and after elementsPosition." // // self shouldNotImplement! // // transclusions: transclusions // self contentElement parents do: [:leaf | transclusions addAll: leaf allEditions]! // // transclusionsFrom: position extent: extent found: transclusions // | startIndex endIndex limitedExtent | // startIndex := position - self startPosition + 1 max: 1. // endIndex := position - self startPosition + extent min: self count. // limitedExtent := endIndex - startIndex + 1. // limitedExtent > 0 ifTrue: [self transclusions: transclusions]! ! // !ContentLeaf categoriesFor: #contentElement!accessing!private! ! // !ContentLeaf categoriesFor: #contentElement:!accessing!private! ! // !ContentLeaf categoriesFor: #contentsAtConstrainedIndex:!public! ! // !ContentLeaf categoriesFor: #contentsFrom:extent:do:!public! ! // !ContentLeaf categoriesFor: #contentsFrom:extent:into:!public! ! // !ContentLeaf categoriesFor: #count!public! ! // !ContentLeaf categoriesFor: #printOn:!public! ! // !ContentLeaf categoriesFor: #sharedWith:for:mappings:!public! ! // !ContentLeaf categoriesFor: #split:about:!private! ! // !ContentLeaf categoriesFor: #transclusions:!public! ! // !ContentLeaf categoriesFor: #transclusionsFrom:extent:found:!public! ! // // !ContentLeaf class methodsFor! // // branch: branch startPosition: startPosition contentElement: contentElement // ^(self basicNew) // branch: branch; // startPosition: startPosition; // contentElement: contentElement; // yourself! public ContentLeaf(SequenceNumber branch, int startPosition, BeContentElement contentElement) { super(branch, startPosition); this.contentElement = contentElement; } // // branch: branch startPosition: startPosition from: contentLeaf // ^self // branch: branch // startPosition: startPosition // contentElement: contentLeaf contentElement! // // icon // ^Character icon! ! // !ContentLeaf class categoriesFor: #branch:startPosition:contentElement:!public! ! // !ContentLeaf class categoriesFor: #branch:startPosition:from:!public! ! // !ContentLeaf class categoriesFor: #icon!public! ! // } --- NEW FILE: EntNode.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; import java.util.Iterator; import java.util.List; import org.abora.ash.engine.AboraObject; /** */ public abstract class EntNode extends AboraObject { protected SequenceNumber branch; public EntNode(SequenceNumber branch) { this.branch = branch; } protected abstract void addToParent(EntNode node); // allEditions // ^self allRoots collect: [:root | root edition]! // // allRoots // | roots | // roots := IdentitySet new. // self allRoots: roots. // ^roots! // // allRoots: allRoots // self parents do: [:parent | parent allRoots: allRoots]! // protected EntNode basicParentSplit() { return null; } // basicParentSplit // ^nil! // public SequenceNumber getBranch() { return branch; } // branch // ^branch! // // branch: anObject // branch := anObject! // protected abstract List children(); // children // self subclassResponsibility! // // contents // self subclassResponsibility! // // contentsAt: position // self subclassResponsibility! // // contentsFrom: position extent: extent // | contents | // #todo "max:". // extent < 0 ifTrue: [self halt]. // contents := WriteStream on: (Array new: (extent max: 0)). // self contentsFrom: position extent: extent into: contents. // " self contentsFrom: position extent: extent do: [:char | contents nextPut: char]." // ^contents contents! // // contentsFrom: position extent: extent into: stream // self subclassResponsibility! // public abstract int count(); // count // self subclassResponsibility! // //TODO shold this be in a ParentNode interface? protected abstract int dspForChild(EntNode node); protected abstract void setDspForChild(int dsp, EntNode child); protected abstract EntNode duplicateFor(SequenceNumber newBranch) throws EntException; // duplicateFor: newBranch // self subclassResponsibility! // // duplicateWithParentsFor: newBranch // | duplicate parent parentDuplicate | // duplicate := self duplicateFor: newBranch. // parent := self singleParentFor: newBranch. // parent notNil // ifTrue: // [parentDuplicate := parent duplicateWithParentsFor: newBranch. // parentDuplicate replaceChild: self with: duplicate]. // ^duplicate! // protected void ensureCompatibleFor(SequenceNumber matchingBranch) throws NonCompatibleBranchException { if (!isCompatibleFor(matchingBranch)) { throw new NonCompatibleBranchException(); } } // ensureCompatibleFor: matchingBranch // (self isCompatibleFor: matchingBranch) // ifFalse: [EntError signal: 'not-compatible ent revision']! // protected void ensureSameFor(SequenceNumber matchingBranch) throws NonSameBranchException { if (!isSameFor(matchingBranch)) { throw new NonSameBranchException(); } } // ensureSameFor: matchingBranch // (self isSameFor: matchingBranch) ifFalse: [EntError signal: 'not-same ent revision']! // protected EntNode findBestParentMatchFor(SequenceNumber matchingBranch) { EntNode singleParent = null; for (Iterator i = getParents().iterator(); i.hasNext(); ) { EntNode possibleParent = (EntNode) i.next(); if (possibleParent.isSameFor(matchingBranch)) { return possibleParent; } if (possibleParent.isCompatibleFor(matchingBranch) && ( singleParent == null || !possibleParent.getBranch().isBranchingAfter(singleParent.getBranch()))) { singleParent = possibleParent; } } return singleParent; } // findBestParentMatchFor: matchingBranch // | singleParent | // singleParent := nil. // self parents do: // [:possibleParent | // (possibleParent isSameFor: matchingBranch) ifTrue: [^possibleParent]. // ((possibleParent isCompatibleFor: matchingBranch) // and: [singleParent isNil or: [possibleParent branch > singleParent branch]]) // ifTrue: [singleParent := possibleParent]]. // ^singleParent! // protected boolean isCompatibleFor(SequenceNumber matchingBranch) { return branch.isBranchingBeforeOrEqual(matchingBranch); } // isCompatibleFor: matchingBranch // ^self branch isBeforeOrEqual: matchingBranch! // protected boolean isSameFor(SequenceNumber matchingBranch) { //@todo do we actually want something more specific - ie trailing zeros return branch.equals(matchingBranch); } // isSameFor: matchingBranch // ^self branch = matchingBranch! // protected EntNode maxNode() { List list = children(); return (EntNode)list.get(list.size() - 1); } // maxNode // ^self children last maxNode! // protected EntNode minNode() { return (EntNode)children().get(0); } // minNode // ^self children first minNode! // protected abstract List getParents(); // parents // self subclassResponsibility! // // parentSplit: matchingBranch // | parent | // parent := self singleParentFor: matchingBranch. // ^parent notNil ifTrue: [parent basicParentSplit] ifFalse: [nil]! // protected abstract void removeFromParent(EntNode oldParent); protected abstract void replaceChild(EntNode existingChild, EntNode newChild); protected RootNode rootFor(SequenceNumber matchingBranch) throws NonCompatibleBranchException { EntNode parent = singleParentFor(matchingBranch); //TODO null parent might indicate somethingt gone wrong if (parent != null) { return parent.rootFor(matchingBranch); } else { return null; } } // rootFor: matchingBranch // | parent | // parent := self singleParentFor: matchingBranch. // #todo. "null parent might indicate something gong wrong" // ^parent notNil ifTrue: [parent rootFor: matchingBranch] ifFalse: [nil]! // // sharedWith: anotherEdition for: forBranch mappings: mappings // self children do: // [:child | // child // sharedWith: anotherEdition // for: forBranch // mappings: mappings]! // protected EntNode singleParentFor(SequenceNumber matchingBranch) { if (getParents().isEmpty()) { return null; } else { EntNode singleParent = findBestParentMatchFor(matchingBranch); //TODO investigate the commenting out of the following //self assert: [singleParent notNil] return singleParent; } } // singleParentFor: matchingBranch // ^self parents isEmpty // ifTrue: [nil] // ifFalse: // [| singleParent | // singleParent := self findBestParentMatchFor: matchingBranch. // #todo. "investigate the commenting out of the following" // "self assert: [singleParent notNil]." // singleParent]! // // transclusions: transclusions // self children do: [:child | child transclusions: transclusions]! ! // !EntNode categoriesFor: #allEditions!public! ! // !EntNode categoriesFor: #allRoots!public! ! // !EntNode categoriesFor: #allRoots:!private! ! // !EntNode categoriesFor: #basicParentSplit!private! ! // !EntNode categoriesFor: #branch!accessing!public! ! // !EntNode categoriesFor: #branch:!accessing!private! ! // !EntNode categoriesFor: #children!public! ! // !EntNode categoriesFor: #contents!public! ! // !EntNode categoriesFor: #contentsAt:!public! ! // !EntNode categoriesFor: #contentsFrom:extent:!public! ! // !EntNode categoriesFor: #contentsFrom:extent:into:!public! ! // !EntNode categoriesFor: #count!public! ! // !EntNode categoriesFor: #duplicateFor:!private! ! // !EntNode categoriesFor: #duplicateWithParentsFor:!private! ! // !EntNode categoriesFor: #ensureCompatibleFor:!private! ! // !EntNode categoriesFor: #ensureSameFor:!private! ! // !EntNode categoriesFor: #findBestParentMatchFor:!private! ! // !EntNode categoriesFor: #isCompatibleFor:!private! ! // !EntNode categoriesFor: #isSameFor:!private! ! // !EntNode categoriesFor: #maxNode!public! ! // !EntNode categoriesFor: #minNode!public! ! // !EntNode categoriesFor: #parents!public! ! // !EntNode categoriesFor: #parentSplit:!private! ! // !EntNode categoriesFor: #rootFor:!private! ! // !EntNode categoriesFor: #sharedWith:for:mappings:!public! ! // !EntNode categoriesFor: #singleParentFor:!private! ! // !EntNode categoriesFor: #transclusions:!public! ! // // !EntNode class methodsFor! // // basicNew // ^(super basicNew) // initialize; // yourself! // // new // ^self shouldNotImplement! ! // !EntNode class categoriesFor: #basicNew!public! ! // !EntNode class categoriesFor: #new!public! ! // // } --- NEW FILE: SequenceNumber.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; import java.util.Iterator; /** */ public class SequenceNumber implements Comparable { private final int digits[]; public SequenceNumber(int digit1) { digits = new int[] {digit1}; } public SequenceNumber(int digit1, int digit2) { digits = new int[] {digit1, digit2}; } public SequenceNumber(int digit1, int digit2, int digit3) { digits = new int[] {digit1, digit2, digit3}; } public SequenceNumber(int[] uncapturedDigits) { //@todo support empty digits? digits = new int[uncapturedDigits.length]; for (int i = 0; i < uncapturedDigits.length; i++) { int digit = uncapturedDigits[i]; digits[i] = digit; } } /** * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(Object o) { return compareTo((SequenceNumber) o); } public int compareTo(SequenceNumber n) { if (this == n) return 0; for (DigitInterleavingIterator i = new DigitInterleavingIterator(digits, n.digits); i.hasNext();) { int digit1 = i.nextInt(); int digit2 = i.nextInt(); if (digit1 < digit2) return -1; else if (digit1 > digit2) return 1; } return 0; } public String toString() { StringBuffer buffer = new StringBuffer("SequenceNumber["); for (int i = 0; i < digits.length; i++) { if (i > 0) buffer.append(":"); buffer.append(digits[i]); } buffer.append("]"); return buffer.toString(); } protected class DigitInterleavingIterator implements Iterator { private final int[] digits1; private final int[] digits2; private int nextDigits1 = 0; private int nextDigits2 = 0; public DigitInterleavingIterator(int[] digits1, int[] digits2) { this.digits1 = digits1; this.digits2 = digits2; } public boolean hasNext() { return nextDigits1 < digits1.length || nextDigits2 < digits2.length; } public Object next() { return new Integer(nextInt()); } public int nextInt() { // if (!hasNext()) // throw new NoSuchElementException(); int value = 0; if (nextDigits1 <= nextDigits2) { if (nextDigits1 < digits1.length) { value = digits1[nextDigits1]; } nextDigits1++; } else { if (nextDigits2 < digits2.length) { value = digits2[nextDigits2]; } nextDigits2++; } return value; } public void remove() { throw new UnsupportedOperationException(); } } public SequenceNumber copyWith(int newDigit) { int[] newDigits = new int[digits.length + 1]; for (int i = 0; i < digits.length; i++) { int digit = digits[i]; newDigits[i] = digit; } newDigits[newDigits.length - 1] = newDigit; return new SequenceNumber(newDigits); } public boolean equals(Object o) { if (!(o instanceof SequenceNumber)) return false; return compareTo(o) == 0; } public int hashCode() { int value = 0; for (int i = 0; i < digits.length; i++) { int digit = digits[i]; if (digit != 0) { value = value << 1 | digit; } } return value; } public SequenceNumber incrementLast() { // @todo what if there are now digits? int[] newDigits = new int[digits.length]; for (int i = 0; i < digits.length; i++) { int digit = digits[i]; if (i == digits.length - 1) { digit++; } newDigits[i] = digit; } return new SequenceNumber(newDigits); } // subtractFromInteger: anInteger // "Private - Answer the result of subtracting the receiver from the known integer, // anInteger, by coercing the less general of it and the receiver. Overridden by // subclasses which can implement more efficiently." // // | newDigits | // newDigits := OrderedCollection new. // anInteger asSequenceMagnitude digitsWith: self do: [:digit1 :digit2 | newDigits add: digit1 - digit2]. // ^self class withAll: newDigits asArray. // ! public boolean isBranchingAfter(SequenceNumber n) { return n.isBranchingBefore(this); } public boolean isBranchingBefore(SequenceNumber n) { if (digits.length > n.digits.length) return false; for (int i = 0; i < digits.length - 1; i++) { int digit1 = digits[i]; int digit2 = n.digits[i]; if (digit1 != digit2) return false; } if (digits.length < n.digits.length) { return digits[digits.length - 1] <= n.digits[digits.length - 1]; } else { return digits[digits.length - 1] < n.digits[digits.length - 1]; } } public boolean isBranchingBeforeOrEqual(SequenceNumber n) { return isBranchingBefore(n) || equals(n); } public int[] toIntArray() { int[] newDigits = new int[digits.length]; for (int i = 0; i < digits.length; i++) { int digit = digits[i]; newDigits[i] = digit; } return newDigits; } } --- NEW FILE: ChildNode.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; import java.util.ArrayList; import java.util.List; /** */ public abstract class ChildNode extends EntNode { //@todo should this be a set? protected List parents = new ArrayList(); protected ChildNode(SequenceNumber branch) { super(branch); } protected void addToParent(EntNode node) { parents.add(node); } protected void removeFromParent(EntNode node) { parents.remove(node); } protected List getParents() { return parents; } protected int dspForChild(EntNode node) { throw new UnsupportedOperationException(); } } --- NEW FILE: RootNode.java --- /* * Abora-Ash * Part of the Abora hypermedia project: http://www.abora.org * Copyright 2003 David G Jones */ package org.abora.ash.ent; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.abora.ash.content.BeEdition; /** */ public class RootNode extends EntNode { private EntNode child = null; private int dsp = 0; private final BeEdition edition; public RootNode(BeEdition edition, SequenceNumber branch) { super(branch); this.edition = edition; } public RootNode(BeEdition edition, SequenceNumber branch, ChildNode child) { super(branch); this.edition = edition; this.child = child; } public RootNode(BeEdition edition, SequenceNumber branch, ChildNode child, int dsp) { super(branch); this.edition = edition; this.child = child; this.dsp = dsp; } // allRoots: allRoots // allRoots add: self! // // applyDspTo: position // | dspPosition | // dspPosition := position + (self invertDsp). // ^dspPosition! // protected void assertIsChild(EntNode node) { if (node != child) { throw new IllegalStateException("unknown child"); } } // assertIsChild: node // self child ~~ node ifTrue: [EntError signal: 'unknown child of root']! // // basicSplayFor: matchingBranch // "only called in the case of a single child" // // #todo. // self ensureSameFor: matchingBranch. // ^self child! // public EntNode getChild() { return child; } // child // ^child! // protected void setChild(EntNode node) { if (child == node) { return; } if (child != null) { child.removeFromParent(this); } child = node; //TODO reset dsp if nil? if (node != null) { node.addToParent(this); } } // child: aNodeOrNil // child == aNodeOrNil ifTrue: [^self]. // child notNil ifTrue: [child removeFromParent: self]. // child := aNodeOrNil. // #todo. "reset dsp if nil?" // aNodeOrNil notNil ifTrue: [aNodeOrNil addToParent: self]! // protected List children() { if (!hasChild()) return Collections.EMPTY_LIST; List children = new ArrayList(1); children.add(child); return children; } // children // ^self child notNil ifTrue: [Array with: self child] ifFalse: [#()]! // // contents // ^self contentsFrom: 1 extent: self count! // // contentsAt: position // child notNil // ifTrue: [^self child contentsAt: (self applyDspTo: position)] // ifFalse: [self errorSubscriptBounds: position]! // // contentsFrom: position extent: extent do: operation // ^self child notNil // ifTrue: // [self child // contentsFrom: (self applyDspTo: position) // extent: extent // do: operation] // ifFalse: ['']! // // contentsFrom: position extent: extent into: stream // self child notNil // ifTrue: // [self child // contentsFrom: (self applyDspTo: position) // extent: extent // into: stream] // ifFalse: []! public int count() { return hasChild() ? child.count() : 0; } // // count // ^child notNil ifTrue: [child count] ifFalse: [0]! // public int getDsp() { return dsp; } // dsp // ^dsp! // // dsp: anObject // dsp := anObject! // protected void setDspForChild(int dsp, EntNode node) { assertIsChild(node); this.dsp = dsp; } // dsp: newDsp forChild: node // self assertIsChild: node. // self dsp: newDsp! // protected int dspForChild(EntNode child) { assertIsChild(child); return dsp; } // dspForChild: node // self assertIsChild: node. // ^self dsp! // protected EntNode duplicateFor(SequenceNumber newBranch) throws NonSameBranchException { // Do not duplicate RootNodes at the moment, as this forced to happen on newEdition. // @todo is this the right behaviour - not duplicating? ensureSameFor(newBranch); return this; } // duplicateFor: newBranch // "Do not duplicate RootNodes at the moment, as this forced to happen on newEdition." // // "is this the right behaviour - not duplicating?" // // #todo. // self ensureSameFor: newBranch. // ^self! // public BeEdition getEdition() { return edition; } // edition // ^edition! // // edition: anObject // edition := anObject! // // firstNodeFrom: position extent: extent shouldSplit: shouldSplit // child notNil // ifTrue: // [^self child // firstNodeFrom: (self applyDspTo: position) // extent: extent // shouldSplit: shouldSplit] // ifFalse: [self errorSubscriptBounds: position]! // public boolean hasChild() { return child != null; } // hasChild // ^child notNil! // // initialize // super initialize. // // self dsp: 0.! // // insert: dataLeaf // "what about dsp?" // // #todo. // self ensureSameFor: dataLeaf branch. // child isNil // ifTrue: // [dataLeaf startPosition = 1 // ifTrue: // [child := dataLeaf. // dataLeaf addToParent: self] // ifFalse: [self errorSubscriptBounds: dataLeaf startPosition]] // ifFalse: // [self child // insert: dataLeaf // at: (self applyDspTo: dataLeaf startPosition) // root: self]! // // invertDsp // ^dsp negated! // // nodeAt: position // child notNil // ifTrue: [^self child nodeAt: (self applyDspTo: position)] // ifFalse: [self errorSubscriptBounds: position]! // // nodesFrom: startPosition extent: extent shouldSplit: shouldSplit // | position nodes extentLeft | // nodes := OrderedCollection new. // position := startPosition. // extentLeft := extent. // [extentLeft > 0] whileTrue: // [| node | // node := self // first... [truncated message content] |