static void moveNodeContents()

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();
        }
    }