in src/main/java/org/apache/xmlbeans/impl/store/Cur.java [1989:2242]
static void moveNodeContents(Xobj x, Cur to, boolean moveAttrs) {
// TODO - should assert that is an attr is being moved, it is ok there
assert to == null || !to.isRoot();
// Collect a bit of information about the contents to move first. Note that the collection
// of this info must not cause a vacant value to become occupied.
boolean hasAttrs = x.hasAttrs();
boolean noSubNodesToMove = !x.hasChildren() && (!moveAttrs || !hasAttrs);
// Deal with the cases where only text is involved in the move
if (noSubNodesToMove) {
// If we're vacant and there is no place to move a potential value, then I can avoid
// acquiring the text from the TypeStoreUser. Otherwise, there may be text here I
// need to move somewhere else.
if (x.isVacant() && to == null) {
x.clearBit(Xobj.VACANT);
x.invalidateUser();
x.invalidateSpecialAttr(null);
x._locale._versionAll++;
} else if (x.hasTextEnsureOccupancy()) {
Cur c = x.tempCur();
c.next();
c.moveChars(to, -1);
c.release();
}
return;
}
// Here I check to see if "to" is just inside x. In this case this is a no-op. Note that
// the value of x may still be vacant.
if (to != null) {
// Quick check of the right edge. If it is there, I need to move "to" to the left edge
// so that it is positioned at the beginning of the "moved" content.
if (x == to._xobj && to._pos == END_POS) {
// TODO - shuffle interior curs?
to.moveTo(x);
to.next(moveAttrs && hasAttrs);
return;
}
// Here I need to see if to is at the left edge. I push to's current position and
// then navigate it to the left edge then compare it to the pushed position...
// Note: gotta be careful to make sure to and x are not in different locales, curs
// may not go to a different locale.
boolean isAtLeftEdge = false;
if (to._locale == x._locale) {
to.push();
to.moveTo(x);
to.next(moveAttrs && hasAttrs);
isAtLeftEdge = to.isAtLastPush();
to.pop();
}
// TODO - shuffle interior curs?
if (isAtLeftEdge) {
return;
}
// Now, after dealing with the edge condition, I can assert that to is not inside x
assert !x.contains(to);
// So, at this point, I've taken case of the no-op cases and the movement of just text.
// Also, to must be occupied because I took care of the text only and nothing to move
// cases.
assert to.getParent().isOccupied();
}
// TODO - did I forget to put a changeNotification here? Look more closely ...
// Deal with the value text of x which is either on x or the last attribute of x.
// I need to get it out of the way to properly deal with the walk of the contents.
// In order to reposition "to" properly later, I need to record how many chars were moved.
int valueMovedCch = 0;
if (x.hasTextNoEnsureOccupancy()) {
Cur c = x.tempCur();
c.next();
c.moveChars(to, -1);
c.release();
if (to != null) {
to.nextChars(valueMovedCch = c._cchSrc);
}
}
// Now, walk all the contents, invalidating special attrs, reportioning cursors,
// disconnecting users and relocating to a potentially different locale. Because I moved
// the value text above, no top level attrs should have any text.
x._locale.embedCurs();
Xobj firstToMove = x.walk(x, true);
boolean sawBookmark = false;
for (Xobj y = firstToMove; y != null; y = y.walk(x, true)) {
if (y._parent == x && y.isAttr()) {
assert y._cchAfter == 0;
if (!moveAttrs) {
firstToMove = y._nextSibling;
continue;
}
y.invalidateSpecialAttr(to == null ? null : to.getParent());
}
for (Cur c; (c = y._embedded) != null; ) {
c.moveTo(x, END_POS);
}
y.disconnectUser();
if (to != null) {
y._locale = to._locale;
}
sawBookmark = sawBookmark || y._bookmarks != null;
}
Xobj lastToMove = x._lastChild;
// If there were any bookmarks in the tree to remove, to preserve the content that these
// bookmarks reference, move the contents to a new root. Note that I already moved the
// first piece of text above elsewhere. Note: this has the effect of keeping all of the
// contents alive even if there is one bookmark deep into the tree. I should really
// disband all the content, except for the pieces which are bookmarked.
Cur surragateTo = null;
if (sawBookmark && to == null) {
surragateTo = to = x._locale.tempCur();
to.createRoot();
to.next();
}
// Perform the rest of the invalidations. If only attrs are moving, then no user
// invalidation needed. If I've move text to "to" already, no need to invalidate
// again.
if (!lastToMove.isAttr()) {
x.invalidateUser();
}
x._locale._versionAll++;
x._locale._versionSansText++;
if (to != null && valueMovedCch == 0) {
to.getParent().invalidateUser();
to._locale._versionAll++;
to._locale._versionSansText++;
}
// Remove the children and, if needed, move them
x.removeXobjs(firstToMove, lastToMove);
if (to != null) {
// To know where I should insert/append the contents to move, I need to see where "to"
// would be if there were no text after it.
Xobj here = to._xobj;
boolean append = to._pos != 0;
int cchRight = to.cchRight();
if (cchRight > 0) {
to.push();
to.next();
here = to._xobj;
append = to._pos != 0;
to.pop();
}
// Now, I have to shuffle the text around "to" in special ways. A complication is
// the insertion of attributes. First, if I'm inserting attrs here then, logically,
// there can be no text to the left because attrs can only live after another attr
// or just inside a container. So, If attrs are being inserted and there is value
// text on the target container, I will need to move this value text to be after
// the lew last attribute. Note that this value text may already live on a current
// last attr (before the inserting). Also, I need to figure this all out before I
// move the text after "to" because this text may end up being sent to the same place
// as the containers value text when the last new node being inserted is an attr!
// Whew!
if (firstToMove.isAttr()) {
Xobj lastNewAttr = firstToMove;
while (lastNewAttr._nextSibling != null && lastNewAttr._nextSibling.isAttr()) {
lastNewAttr = lastNewAttr._nextSibling;
}
// Get to's parnet now before I potentially move him with the next transfer
Xobj y = to.getParent();
if (cchRight > 0) {
transferChars(to._xobj, to._pos, lastNewAttr, lastNewAttr.posMax(), cchRight);
}
if (y.hasTextNoEnsureOccupancy()) {
int p, cch;
if (y._cchValue > 0) {
p = 1;
cch = y._cchValue;
} else {
y = y.lastAttr();
assert (y != null);
p = y.posAfter();
cch = y._cchAfter;
}
transferChars(y, p, lastNewAttr, lastNewAttr.posAfter(), cch);
}
} else if (cchRight > 0) {
transferChars(to._xobj, to._pos, lastToMove, lastToMove.posMax(), cchRight);
}
// After mucking with the text, splice the new tree in
if (append) {
here.appendXobjs(firstToMove, lastToMove);
} else {
here.insertXobjs(firstToMove, lastToMove);
}
// Position "to" to be at the beginning of the newly inserted contents
to.moveTo(firstToMove);
to.prevChars(valueMovedCch);
}
// If I consed up a to, release it here
if (surragateTo != null) {
surragateTo.release();
}
}