All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xmlbeans.impl.store.Xobj Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*   Copyright 2004 The Apache Software Foundation
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.xmlbeans.impl.store;

import org.apache.xmlbeans.*;
import org.apache.xmlbeans.impl.common.QNameHelper;
import org.apache.xmlbeans.impl.common.ValidatorListener;
import org.apache.xmlbeans.impl.common.XmlLocale;
import org.apache.xmlbeans.impl.store.DomImpl.Dom;
import org.apache.xmlbeans.impl.values.TypeStore;
import org.apache.xmlbeans.impl.values.TypeStoreUser;
import org.apache.xmlbeans.impl.values.TypeStoreUserFactory;
import org.apache.xmlbeans.impl.values.TypeStoreVisitor;
import org.apache.xmlbeans.impl.xpath.XPathFactory;

import javax.xml.namespace.QName;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.apache.xmlbeans.impl.store.Cur.*;

// DOM Level 3

abstract class Xobj implements TypeStore {

    Xobj(Locale l, int kind, int domType) {
        assert kind == ROOT || kind == ELEM || kind == ATTR || kind == COMMENT || kind == PROCINST;

        _locale = l;
        _bits = (domType << 4) + kind;
    }

    final int kind() {
        return _bits & 0xF;
    }

    final int domType() {
        return (_bits & 0xF0) >> 4;
    }

    final boolean isRoot() {
        return kind() == ROOT;
    }

    final boolean isAttr() {
        return kind() == ATTR;
    }

    final boolean isElem() {
        return kind() == ELEM;
    }

    final boolean isProcinst() {
        return kind() == PROCINST;
    }

    final boolean isComment() {
        return kind() == COMMENT;
    }

    final boolean isContainer() {
        return Cur.kindIsContainer(kind());
    }

    final boolean isUserNode() {
        int k = kind();
        return k == ELEM || k == ROOT || (k == ATTR && !isXmlns());
    }

    final boolean isNormalAttr() {
        return isAttr() && !Locale.isXmlns(_name);
    }

    final boolean isXmlns() {
        return isAttr() && Locale.isXmlns(_name);
    }

    final int cchAfter() {
        return _cchAfter;
    }

    final int posAfter() {
        return 2 + _cchValue;
    }

    final int posMax() {
        return 2 + _cchValue + _cchAfter;
    }

    final String getXmlnsPrefix() {
        return Locale.xmlnsPrefix(_name);
    }

    final String getXmlnsUri() {
        return getValueAsString();
    }

    final boolean hasTextEnsureOccupancy() {
        ensureOccupancy();
        return hasTextNoEnsureOccupancy();
    }

    final boolean hasTextNoEnsureOccupancy() {
        if (_cchValue > 0) {
            return true;
        }

        Xobj lastAttr = lastAttr();

        return lastAttr != null && lastAttr._cchAfter > 0;
    }

    final boolean hasAttrs() {
        return _firstChild != null && _firstChild.isAttr();
    }

    final boolean hasChildren() {
        return _lastChild != null && !_lastChild.isAttr();
    }


    /**
     * this method is to speed up DomImpl
     * when underlying obj is an Xobj
     *
     * @return 0 or 1 dom children; val 2 indicates that DomImpl needs to
     * compute the result itself
     */
    final protected int getDomZeroOneChildren() {
        if (_firstChild == null &&
            _srcValue == null &&
            _charNodesValue == null) {
            return 0;
        }

        if (_lastChild != null &&
            _lastChild.isAttr() &&
            _lastChild._charNodesAfter == null &&
            _lastChild._srcAfter == null &&
            _srcValue == null &&
            _charNodesValue == null
        ) {
            return 0;
        }

        if (_firstChild == _lastChild &&
            _firstChild != null &&
            !_firstChild.isAttr() &&
            _srcValue == null &&
            _charNodesValue == null &&
            _firstChild._srcAfter == null
        ) {
            return 1;
        }

        if (_firstChild == null &&
            _srcValue != null &&
            (_charNodesValue == null ||
             (_charNodesValue._next == null &&
              _charNodesValue._cch == _cchValue))
        ) {
            return 1;
        }
        //single elem after an attr
        Xobj lastAttr = lastAttr();
        Xobj node = lastAttr == null ?
            null : lastAttr._nextSibling;
        if (lastAttr != null &&
            lastAttr._srcAfter == null &&
            node != null &&
            node._srcAfter == null &&
            node._nextSibling == null) {
            return 1;
        }

        return 2;
    }

    /**
     * can one use the _firstChild pointer to retrieve
     * the first DOM child
     */
    final protected boolean isFirstChildPtrDomUsable() {
        if (_firstChild == null &&
            _srcValue == null &&
            _charNodesValue == null) {
            return true;
        }

        if (_firstChild != null &&
            !_firstChild.isAttr() &&
            _srcValue == null &&
            _charNodesValue == null) {
            assert (_firstChild instanceof NodeXobj) :
                "wrong node type";
            return true;
        }
        return false;
    }

    /**
     * can one use the _nextSibling pointer to retrieve
     * the next DOM sibling
     */
    final protected boolean isNextSiblingPtrDomUsable() {
        if (_charNodesAfter == null &&
            _srcAfter == null) {
            assert (_nextSibling == null ||
                    _nextSibling instanceof NodeXobj) :
                "wrong node type";
            return true;
        }
        return false;
    }

    /**
     * can one use the _charNodesValue pointer to retrieve
     * the next DOM sibling
     */
    final protected boolean isExistingCharNodesValueUsable() {
        if (_srcValue == null) {
            return false;
        }
        return _charNodesValue != null && _charNodesValue._next == null
               && _charNodesValue._cch == _cchValue;
    }

    final protected boolean isCharNodesValueUsable() {
        return isExistingCharNodesValueUsable() ||
               (_charNodesValue =
                   Cur.updateCharNodes(_locale, this,
                       _charNodesValue, _cchValue)) != null;
    }

    /**
     * can one use the _charNodesAfter pointer to retrieve
     * the next DOM sibling
     */
    final protected boolean isCharNodesAfterUsable() {
        if (_srcAfter == null) {
            return false;
        }
        if (_charNodesAfter != null && _charNodesAfter._next == null
            && _charNodesAfter._cch == this._cchAfter) {
            return true;
        }
        return (_charNodesAfter =
            Cur.updateCharNodes(_locale, this,
                _charNodesAfter, _cchAfter)) != null;
    }


    final Xobj lastAttr() {
        if (_firstChild == null || !_firstChild.isAttr()) {
            return null;
        }

        Xobj lastAttr = _firstChild;

        while (lastAttr._nextSibling != null && lastAttr._nextSibling.isAttr()) {
            lastAttr = lastAttr._nextSibling;
        }

        return lastAttr;
    }

    abstract Dom getDom();

    abstract Xobj newNode(Locale l);

    final int cchLeft(int p) {
        if (isRoot() && p == 0) {
            return 0;
        }

        Xobj x = getDenormal(p);

        p = posTemp();
        int pa = x.posAfter();

        return p - (p < pa ? 1 : pa);
    }

    final int cchRight(int p) {
        assert p < posMax();

        if (p <= 0) {
            return 0;
        }

        int pa = posAfter();

        return p < pa ? pa - p - 1 : posMax() - p;
    }

    //
    // Dom interface
    //

    public final Locale locale() {
        return _locale;
    }

    public final int nodeType() {
        return domType();
    }

    public final QName getQName() {
        return _name;
    }

    public final Cur tempCur() {
        Cur c = _locale.tempCur();
        c.moveTo(this);
        return c;
    }

    public void dump(PrintStream o, Object ref) {
        Cur.dump(o, this, ref);
    }

    public void dump(PrintStream o) {
        Cur.dump(o, this, this);
    }

    public void dump() {
        dump(System.out);
    }

    //
    //
    //

    final Cur getEmbedded() {
        _locale.embedCurs();

        return _embedded;
    }

    // Incoming p must be at text (implicitly denormalized)

    final boolean inChars(int p, Xobj xIn, int pIn, int cch, boolean includeEnd) {
        assert p > 0 && p < posMax() && p != posAfter() - 1 && cch > 0;
        assert xIn.isNormal(pIn);

        // No need to denormalize "in" if the right hand side is excluded.  Denormalizing deals
        // with the case where p is END_POS.

        int offset;

        if (includeEnd) {
            // Can't denormalize at the beginning of the document

            if (xIn.isRoot() && pIn == 0) {
                return false;
            }

            xIn = xIn.getDenormal(pIn);
            pIn = xIn.posTemp();

            offset = 1;
        } else {
            offset = 0;
        }

        return xIn == this && pIn >= p && pIn < p + cch + offset;
    }

    // Is x/p just after the end of this

    final boolean isJustAfterEnd(Xobj x, int p) {
        assert x.isNormal(p);

        // Get denormalize at the beginning of the doc

        if (x.isRoot() && p == 0) {
            return false;
        }

        return
            x == this
                ? p == posAfter()
                : x.getDenormal(p) == this && x.posTemp() == posAfter();
    }

    final boolean isInSameTree(Xobj x) {
        if (_locale != x._locale) {
            return false;
        }

        for (Xobj y = this; ; y = y._parent) {
            if (y == x) {
                return true;
            }

            if (y._parent == null) {
                for (; ; x = x._parent) {
                    if (x == this) {
                        return true;
                    }

                    if (x._parent == null) {
                        return x == y;
                    }
                }
            }
        }
    }

    final boolean contains(Cur c) {
        assert c.isNormal();

        return contains(c._xobj, c._pos);
    }

    final boolean contains(Xobj x, int p) {
        assert x.isNormal(p);

        if (this == x) {
            return p == END_POS || (p > 0 && p < posAfter());
        }

        if (_firstChild == null) {
            return false;
        }

        for (; x != null; x = x._parent) {
            if (x == this) {
                return true;
            }
        }

        return false;
    }

    final Bookmark setBookmark(int p, Object key, Object value) {
        assert isNormal(p);

        for (Bookmark b = _bookmarks; b != null; b = b._next) {
            if (p == b._pos && key == b._key) {
                if (value == null) {
                    _bookmarks = b.listRemove(_bookmarks);
                    return null;
                }

                b._value = value;

                return b;
            }
        }

        if (value == null) {
            return null;
        }

        Bookmark b = new Bookmark();

        b._xobj = this;
        b._pos = p;
        b._key = key;
        b._value = value;

        _bookmarks = b.listInsert(_bookmarks);

        return b;
    }

    final boolean hasBookmark(Object key, int pos) {
        for (Bookmark b = _bookmarks; b != null; b = b._next) {
            if (b._pos == pos && key == b._key) {
                //System.out.println("hasCDataBookmark  pos: " + pos + " xobj: " + getQName() + " b._pos: " + _bookmarks._pos);
                return true;
            }
        }

        return false;
    }

    final Xobj findXmlnsForPrefix(String prefix) {
        assert isContainer() && prefix != null;

        for (Xobj c = this; c != null; c = c._parent) {
            for (Xobj a = c.firstAttr(); a != null; a = a.nextAttr()) {
                if (a.isXmlns() && a.getXmlnsPrefix().equals(prefix)) {
                    return a;
                }
            }
        }

        return null;
    }

    final boolean removeAttr(QName name) {
        assert isContainer();

        Xobj a = getAttr(name);

        if (a == null) {
            return false;
        }

        Cur c = a.tempCur();

        for (; ; ) {
            c.moveNode(null);

            a = getAttr(name);

            if (a == null) {
                break;
            }

            c.moveTo(a);
        }

        c.release();

        return true;
    }

    final Xobj setAttr(QName name, String value) {
        assert isContainer();

        Cur c = tempCur();

        if (c.toAttr(name)) {
            c.removeFollowingAttrs();
        } else {
            c.next();
            c.createAttr(name);
        }

        c.setValue(value);

        Xobj a = c._xobj;

        c.release();

        return a;
    }

    final void setName(QName newName) {
        assert isAttr() || isElem() || isProcinst();
        assert newName != null;

        if (!_name.equals(newName) || !_name.getPrefix().equals(newName.getPrefix())) {
// TODO - this is not a structural change .... perhaps should not issue a change here?
            _locale.notifyChange();

            QName oldName = _name;

            _name = newName;
            if (this instanceof NamedNodeXobj) {
                NamedNodeXobj me = (NamedNodeXobj) this;
                me._canHavePrefixUri = true;
            }

            if (!isProcinst()) {
                Xobj disconnectFromHere = this;

                if (isAttr() && _parent != null) {
                    if (oldName.equals(Locale._xsiType) || newName.equals(Locale._xsiType)) {
                        disconnectFromHere = _parent;
                    }

                    if (oldName.equals(Locale._xsiNil) || newName.equals(Locale._xsiNil)) {
                        _parent.invalidateNil();
                    }
                }

                disconnectFromHere.disconnectNonRootUsers();
            }

            _locale._versionAll++;
            _locale._versionSansText++;
        }
    }

    final Xobj ensureParent() {
        assert _parent != null || (!isRoot() && cchAfter() == 0);
        return _parent == null ? new DocumentFragXobj(_locale).appendXobj(this) : _parent;
    }

    final Xobj firstAttr() {
        return _firstChild == null || !_firstChild.isAttr() ? null : _firstChild;
    }

    final Xobj nextAttr() {
        if (_firstChild != null && _firstChild.isAttr()) {
            return _firstChild;
        }

        if (_nextSibling != null && _nextSibling.isAttr()) {
            return _nextSibling;
        }

        return null;
    }

    final boolean isValid() {
        return !isVacant() || (_cchValue == 0 && _user != null);
    }

    final int posTemp() {
        return _locale._posTemp;
    }

    final Xobj getNormal(int p) {
        assert p == END_POS || (p >= 0 && p <= posMax());

        Xobj x = this;

        if (p == x.posMax()) {
            if (x._nextSibling != null) {
                x = x._nextSibling;
                p = 0;
            } else {
                x = x.ensureParent();
                p = END_POS;
            }
        } else if (p == x.posAfter() - 1) {
            p = END_POS;
        }

        _locale._posTemp = p;

        return x;
    }

    // Can't denormalize a position at the very beginning of the document.  No where to go to the
    // left!

    final Xobj getDenormal(int p) {
        assert !isRoot() || p == END_POS || p > 0;

        Xobj x = this;

        if (p == 0) {
            if (x._prevSibling == null) {
                x = x.ensureParent();
                p = x.posAfter() - 1;
            } else {
                x = x._prevSibling;
                p = x.posMax();
            }
        } else if (p == END_POS) {
            if (x._lastChild == null) {
                p = x.posAfter() - 1;
            } else {
                x = x._lastChild;
                p = x.posMax();
            }
        }

        _locale._posTemp = p;

        return x;
    }

    final boolean isNormal(int p) {
        if (!isValid()) {
            return false;
        }

        if (p == END_POS || p == 0) {
            return true;
        }

        if (p < 0 || p >= posMax()) {
            return false;
        }

        if (p >= posAfter()) {
            if (isRoot()) {
                return false;
            }

            if (_nextSibling != null && _nextSibling.isAttr()) {
                return false;
            }

            if (_parent == null || !_parent.isContainer()) {
                return false;
            }
        }

        return p != posAfter() - 1;
    }

    final Xobj walk(Xobj root, boolean walkChildren) {
        if (_firstChild != null && walkChildren) {
            return _firstChild;
        }

        for (Xobj x = this; x != root; x = x._parent) {
            if (x._nextSibling != null) {
                return x._nextSibling;
            }
        }

        return null;
    }

    final void removeXobj() {
        if (_parent != null) {
            if (_parent._firstChild == this) {
                _parent._firstChild = _nextSibling;
            }

            if (_parent._lastChild == this) {
                _parent._lastChild = _prevSibling;
            }

            if (_prevSibling != null) {
                _prevSibling._nextSibling = _nextSibling;
            }

            if (_nextSibling != null) {
                _nextSibling._prevSibling = _prevSibling;
            }

            _parent = null;
            _prevSibling = null;
            _nextSibling = null;
        }

    }

    final void insertXobj(Xobj s) {
        assert _locale == s._locale;
        assert !s.isRoot() && !isRoot();
        assert s._parent == null;
        assert s._prevSibling == null;
        assert s._nextSibling == null;

        ensureParent();

        s._parent = _parent;
        s._prevSibling = _prevSibling;
        s._nextSibling = this;

        if (_prevSibling != null) {
            _prevSibling._nextSibling = s;
        } else {
            _parent._firstChild = s;
        }

        _prevSibling = s;

    }

    final Xobj appendXobj(Xobj c) {
        assert _locale == c._locale;
        assert !c.isRoot();
        assert c._parent == null;
        assert c._prevSibling == null;
        assert c._nextSibling == null;
        assert _lastChild == null || _firstChild != null;

        c._parent = this;
        c._prevSibling = _lastChild;

        if (_lastChild == null) {
            _firstChild = c;
        } else {
            _lastChild._nextSibling = c;
        }

        _lastChild = c;

        return this;
    }

    final void removeXobjs(Xobj first, Xobj last) {
        assert last._locale == first._locale;
        assert first._parent == this;
        assert last._parent == this;

        if (_firstChild == first) {
            _firstChild = last._nextSibling;
        }

        if (_lastChild == last) {
            _lastChild = first._prevSibling;
        }

        if (first._prevSibling != null) {
            first._prevSibling._nextSibling = last._nextSibling;
        }

        if (last._nextSibling != null) {
            last._nextSibling._prevSibling = first._prevSibling;
        }

        // Leave the children linked together

        first._prevSibling = null;
        last._nextSibling = null;

        for (; first != null; first = first._nextSibling) {
            first._parent = null;
        }
    }

    final void insertXobjs(Xobj first, Xobj last) {
        assert _locale == first._locale;
        assert last._locale == first._locale;
        assert first._parent == null && last._parent == null;
        assert first._prevSibling == null;
        assert last._nextSibling == null;

        first._prevSibling = _prevSibling;
        last._nextSibling = this;

        if (_prevSibling != null) {
            _prevSibling._nextSibling = first;
        } else {
            _parent._firstChild = first;
        }

        _prevSibling = last;

        for (; first != this; first = first._nextSibling) {
            first._parent = _parent;
        }
    }

    final void appendXobjs(Xobj first, Xobj last) {
        assert _locale == first._locale;
        assert last._locale == first._locale;
        assert first._parent == null && last._parent == null;
        assert first._prevSibling == null;
        assert last._nextSibling == null;
        assert !first.isRoot();

        first._prevSibling = _lastChild;

        if (_lastChild == null) {
            _firstChild = first;
        } else {
            _lastChild._nextSibling = first;
        }

        _lastChild = last;

        for (; first != null; first = first._nextSibling) {
            first._parent = this;
        }
    }

    // Potential attr is going to be moved/removed, invalidate parent if it is a special attr

    final void invalidateSpecialAttr(Xobj newParent) {
        if (isAttr()) {
            if (_name.equals(Locale._xsiType)) {
                if (_parent != null) {
                    _parent.disconnectNonRootUsers();
                }

                if (newParent != null) {
                    newParent.disconnectNonRootUsers();
                }
            }

            if (_name.equals(Locale._xsiNil)) {
                if (_parent != null) {
                    _parent.invalidateNil();
                }

                if (newParent != null) {
                    newParent.invalidateNil();
                }
            }
        }
    }

    // Move or remove chars.  Incoming p is denormalized.  Incoming xTo and pTo are denormalized.
    // Option to move curs with text.  Option to perform invalidations.
    //
    // Important note: this fcn must operate under the assumption that the tree may be in an
    // invalid state.  Most likely, there may be text on two different nodes which should belong
    // on the same node.  Assertion of cursor normalization usually detects this problem.  Any of
    // the fcns it calls must also deal with these invalid conditions.  Try not to call so many
    // fcns from here.

    final void removeCharsHelper(
        int p, int cchRemove, Xobj xTo, int pTo, boolean moveCurs, boolean invalidate) {
        assert p > 0 && p < posMax() && p != posAfter() - 1;
        assert cchRemove > 0;
        assert cchRight(p) >= cchRemove;
        assert !moveCurs || xTo != null;

        // Here I check the span of text to be removed for cursors.  If xTo/pTo is not specified,
        // then the caller wants these cursors to collapse to be after the text being removed.  If
        // the caller specifies moveCurs, then the caller has arranged for the text being removed
        // to have been copied to xTp/pTo and wants the cursors to be moved there as well.
        // Note that I call nextChars here.  I do this because trying to shift the cursor to the
        // end of the text to be removed with a moveTo could cause the improper placement of the
        // cursor just before an end tag, instead of placing it just before the first child.  Also,
        // I adjust all positions of curs after the text to be removed to account for the removal.

        for (Cur c = getEmbedded(); c != null; ) {
            Cur next = c._next;

            // Here I test to see if the Cur c is in the range of chars to be removed.  Normally
            // I would call inChars, but it can't handle the invalidity of the tree, so I heve
            // inlined the inChars logic here (includeEnd is false, makes it much simpler).
            // Note that I also call moveToNoCheck because the destination may have afterText
            // and no parent which will cause normaliztion checks in MoveTo to fail.  I don't think
            // that nextChars will be called under such circumstnaces.

            assert c._xobj == this;

            if (c._pos >= p && c._pos < p + cchRemove) {
                if (moveCurs) {
                    c.moveToNoCheck(xTo, pTo + c._pos - p);
                } else {
                    c.nextChars(cchRemove - c._pos + p);
                }
            }

            // If c is still on this Xobj and it's to the right of the chars to remove, adjust
            // it to adapt to the removal of the cars.  I don't have to worry about END_POS
            // here, just curs in text.

            if (c._xobj == this && c._pos >= p + cchRemove) {
                c._pos -= cchRemove;
            }

            c = next;
        }

        // Here I move bookmarks in this text to the span of text at xTo/pTo.  The text at this/p
        // is going away, but a caller of this fcn who specifies xTo/pTo has copied the text to
        // xTo/pTo.  The caller has to make sure that if xTo/pTo is not specified, then there are
        // no bookmarks in the span of text to be removed.

        for (Bookmark b = _bookmarks; b != null; ) {
            // Similarly, as above, I can't call inChars here

            assert b._xobj == this;

            if (b._pos >= p && b._pos < p + cchRemove) {
                assert xTo != null;
                b.moveTo(xTo, pTo + b._pos - p);
            }

            if (b._xobj == this && b._pos >= p + cchRemove) {
                b._pos -= cchRemove;
            }

            b = b._next;
        }

        // Now, remove the actual chars

        int pa = posAfter();
        CharUtil cu = _locale.getCharUtil();

        if (p < pa) {
            _srcValue = cu.removeChars(p - 1, cchRemove, _srcValue, _offValue, _cchValue);
            _offValue = cu._offSrc;
            _cchValue = cu._cchSrc;

            if (invalidate) {
                invalidateUser();
                invalidateSpecialAttr(null);
            }
        } else {
            _srcAfter = cu.removeChars(p - pa, cchRemove, _srcAfter, _offAfter, _cchAfter);
            _offAfter = cu._offSrc;
            _cchAfter = cu._cchSrc;

            if (invalidate && _parent != null) {
                _parent.invalidateUser();
            }
        }
    }

    // Insert chars into this xobj.  Incoming p is denormalized.  Update bookmarks and cursors.
    // This fcn does not deal with occupation of the value, this needs to be handled by the
    // caller.

    final void insertCharsHelper(int p, Object src, int off, int cch, boolean invalidate) {
        assert p > 0;
        assert p >= posAfter() || isOccupied();

        int pa = posAfter();

        // Here I shuffle bookmarks and cursors affected by the insertion of the new text.  Because
        // getting the embedded cursors is non-trivial, I avoid getting them if I don't need to.
        // Basically, I need to know if p is before any text in the node as a whole.  If it is,
        // then there may be cursors/marks I need to shift right.

        if (p - (p < pa ? 1 : 2) < _cchValue + _cchAfter) {
            for (Cur c = getEmbedded(); c != null; c = c._next) {
                if (c._pos >= p) {
                    c._pos += cch;
                }
            }

            for (Bookmark b = _bookmarks; b != null; b = b._next) {
                if (b._pos >= p) {
                    b._pos += cch;
                }
            }
        }

        // Now, stuff the new characters in!  Also invalidate the proper container and if the
        // value of an attribute is changing, check for special attr invalidation.  Note that
        // I do not assume that inserting after text will have a parent.  There are use cases
        // from moveNodesContents which excersize this.

        CharUtil cu = _locale.getCharUtil();

        if (p < pa) {
            _srcValue = cu.insertChars(p - 1, _srcValue, _offValue, _cchValue, src, off, cch);
            _offValue = cu._offSrc;
            _cchValue = cu._cchSrc;

            if (invalidate) {
                invalidateUser();
                invalidateSpecialAttr(null);
            }
        } else {
            _srcAfter = cu.insertChars(p - pa, _srcAfter, _offAfter, _cchAfter, src, off, cch);
            _offAfter = cu._offSrc;
            _cchAfter = cu._cchSrc;

            if (invalidate && _parent != null) {
                _parent.invalidateUser();
            }
        }
    }

    Xobj copyNode(Locale toLocale) {
        Xobj newParent = null;
        Xobj copy = null;

        for (Xobj x = this; ; ) {
            x.ensureOccupancy();

            Xobj newX = x.newNode(toLocale);

            newX._srcValue = x._srcValue;
            newX._offValue = x._offValue;
            newX._cchValue = x._cchValue;

            newX._srcAfter = x._srcAfter;
            newX._offAfter = x._offAfter;
            newX._cchAfter = x._cchAfter;

            for (Bookmark b = x._bookmarks; b != null; b = b._next) {
                if (x.hasBookmark(CDataBookmark.CDATA_BOOKMARK.getKey(), b._pos)) {
                    newX.setBookmark(b._pos, CDataBookmark.CDATA_BOOKMARK.getKey(), CDataBookmark.CDATA_BOOKMARK);
                }
            }
            // TODO - strange to have charNode stuff inside here .....
            // newX._charNodesValue = CharNode.copyNodes( x._charNodesValue, newX._srcValue );
            // newX._charNodesAfter = CharNode.copyNodes( x._charNodesAfter, newX._srcAfter );

            if (newParent == null) {
                copy = newX;
            } else {
                newParent.appendXobj(newX);
            }

            // Walk to the next in-order xobj.  Record the current (y) to compute newParent

            Xobj y = x;

            if ((x = x.walk(this, true)) == null) {
                break;
            }

            if (y == x._parent) {
                newParent = newX;
            } else {
                for (; y._parent != x._parent; y = y._parent) {
                    newParent = newParent._parent;
                }
            }
        }

        copy._srcAfter = null;
        copy._offAfter = 0;
        copy._cchAfter = 0;

        return copy;
    }

    // Rturns all the chars, even if there is text intermixed with children

    String getCharsAsString(int p, int cch, int wsr) {
        if (cchRight(p) == 0) {
            return "";
        }

        Object src = getChars(p, cch);

        if (wsr == WS_PRESERVE) {
            return CharUtil.getString(src, _locale._offSrc, _locale._cchSrc);
        }

        Locale.ScrubBuffer scrub = Locale.getScrubBuffer(wsr);

        scrub.scrub(src, _locale._offSrc, _locale._cchSrc);

        return scrub.getResultAsString();
    }

    String getCharsAfterAsString(int off, int cch) {
        int offset = off + _cchValue + 2;
        if (offset == posMax()) {
            offset = -1;
        }
        return getCharsAsString(offset, cch, WS_PRESERVE);
    }

    String getCharsValueAsString(int off, int cch) {
        return getCharsAsString(off + 1, cch, WS_PRESERVE);
    }

    String getValueAsString(int wsr) {
        if (!hasChildren()) {
            Object src = getFirstChars();

            if (wsr == WS_PRESERVE) {
                String s = CharUtil.getString(src, _locale._offSrc, _locale._cchSrc);

                // Cache string to be able to use it later again

                int cch = s.length();

                if (cch > 0) {
                    Xobj lastAttr = lastAttr();

                    assert (lastAttr == null ? _cchValue : lastAttr._cchAfter) == cch;

                    if (lastAttr != null) {
                        lastAttr._srcAfter = s;
                        lastAttr._offAfter = 0;
                    } else {
                        _srcValue = s;
                        _offValue = 0;
                    }
                }

                return s;
            }

            Locale.ScrubBuffer scrub = Locale.getScrubBuffer(wsr);

            scrub.scrub(src, _locale._offSrc, _locale._cchSrc);

            return scrub.getResultAsString();
        }

        Locale.ScrubBuffer scrub = Locale.getScrubBuffer(wsr);

        Cur c = tempCur();

        c.push();

        for (c.next(); !c.isAtEndOfLastPush(); ) {
            if (c.isText()) {
                scrub.scrub(c.getChars(-1), c._offSrc, c._cchSrc);
            }

            if (c.isComment() || c.isProcinst()) {
                c.skip();
            } else {
                c.next();
            }
        }

        String s = scrub.getResultAsString();

        c.release();

        return s;
    }

    String getValueAsString() {
        return getValueAsString(WS_PRESERVE);
    }

    // Returns just chars just after the begin tag ... does not get all the text if there are
    // children

    Object getFirstChars() {
        ensureOccupancy();

        if (_cchValue > 0) {
            return getChars(1, -1);
        }

        Xobj lastAttr = lastAttr();

        if (lastAttr == null || lastAttr._cchAfter <= 0) {
            _locale._offSrc = 0;
            _locale._cchSrc = 0;

            return null;
        }

        return lastAttr.getChars(lastAttr.posAfter(), -1);
    }

    Object getChars(int pos, int cch, Cur c) {
        Object src = getChars(pos, cch);

        c._offSrc = _locale._offSrc;
        c._cchSrc = _locale._cchSrc;

        return src;
    }

    // These return the remainder of the char triple that getChars starts

    Object getChars(int pos, int cch) {
        assert isNormal(pos);

        int cchRight = cchRight(pos);

        if (cch < 0 || cch > cchRight) {
            cch = cchRight;
        }

        if (cch == 0) {
            _locale._offSrc = 0;
            _locale._cchSrc = 0;

            return null;
        }

        return getCharsHelper(pos, cch);
    }

    // Assumes that there are chars to return, does not assume normal x/p

    Object getCharsHelper(int pos, int cch) {
        assert cch > 0 && cchRight(pos) >= cch;

        int pa = posAfter();

        Object src;

        if (pos >= pa) {
            src = _srcAfter;
            _locale._offSrc = _offAfter + pos - pa;
        } else {
            src = _srcValue;
            _locale._offSrc = _offValue + pos - 1;
        }

        _locale._cchSrc = cch;

        return src;
    }

    //
    //
    //

    final void setBit(int mask) {
        _bits |= mask;
    }

    final void clearBit(int mask) {
        _bits &= ~mask;
    }

    final boolean bitIsSet(int mask) {
        return (_bits & mask) != 0;
    }

    final boolean bitIsClear(int mask) {
        return (_bits & mask) == 0;
    }

    static final int VACANT = 0x100;
    static final int STABLE_USER = 0x200;
    static final int INHIBIT_DISCONNECT = 0x400;

    final boolean isVacant() {
        return bitIsSet(VACANT);
    }

    final boolean isOccupied() {
        return bitIsClear(VACANT);
    }

    final boolean inhibitDisconnect() {
        return bitIsSet(INHIBIT_DISCONNECT);
    }

    final boolean isStableUser() {
        return bitIsSet(STABLE_USER);
    }

    void invalidateNil() {
        if (_user != null) {
            _user.invalidate_nilvalue();
        }
    }

    void setStableType(SchemaType type) {
        setStableUser(((TypeStoreUserFactory) type).createTypeStoreUser());
    }

    void setStableUser(TypeStoreUser user) {
        disconnectNonRootUsers();
        disconnectUser();

        assert _user == null;

        _user = user;

        _user.attach_store(this);

        setBit(STABLE_USER);
    }

    void disconnectUser() {
        if (_user != null && !inhibitDisconnect()) {
            ensureOccupancy();
            _user.disconnect_store();
            _user = null;
        }
    }

    // If a node does not have a user, then I don't need to walk its descendents.  NOte that
    // the doconnect happens in document order.  This may be a problem ... not sure ... May want
    // to disconnect in a bottom up manner.

    void disconnectNonRootUsers() {
        Xobj next;

        for (Xobj x = this; x != null; x = next) {
            next = x.walk(this, x._user != null);

            if (!x.isRoot()) {
                x.disconnectUser();
            }
        }
    }

    void disconnectChildrenUsers() {
        Xobj next;

        for (Xobj x = walk(this, _user == null); x != null; x = next) {
            next = x.walk(this, x._user != null);

            x.disconnectUser();
        }
    }

    /**
     * Given a prefix, returns the namespace corresponding to
     * the prefix at this location, or null if there is no mapping
     * for this prefix.
     * 

* prefix="" indicates the absence of a prefix. A return value * of "" indicates the no-namespace, and should not be confused * with a return value of null, which indicates an illegal * state, where there is no mapping for the given prefix. *

* If the the default namespace is not explicitly mapped in the xml, * the xml spec says that it should be mapped to the no-namespace. * When the 'defaultAlwaysMapped' parameter is true, the default namepsace * will return the no-namespace even if it is not explicity * mapped, otherwise the default namespace will return null. *

* This function intercepts the built-in prefixes "xml" and * "xmlns" and returns their well-known namespace URIs. * * @param prefix The prefix to look up. * @param defaultAlwaysMapped If true, return the no-namespace for the default namespace if not set. * @return The mapped namespace URI ("" if no-namespace), or null if no mapping. */ final String namespaceForPrefix(String prefix, boolean defaultAlwaysMapped) { if (prefix == null) { prefix = ""; } // handle built-in prefixes if (prefix.equals("xml")) { return Locale._xml1998Uri; } if (prefix.equals("xmlns")) { return Locale._xmlnsUri; } for (Xobj x = this; x != null; x = x._parent) { for (Xobj a = x._firstChild; a != null && a.isAttr(); a = a._nextSibling) { if (a.isXmlns() && a.getXmlnsPrefix().equals(prefix)) { return a.getXmlnsUri(); } } } return defaultAlwaysMapped && prefix.length() == 0 ? "" : null; } final String prefixForNamespace(String ns, String suggestion, boolean createIfMissing) { if (ns == null) { ns = ""; } // special cases if (ns.equals(Locale._xml1998Uri)) { return "xml"; } if (ns.equals(Locale._xmlnsUri)) { return "xmlns"; } // Get the closest container for the spot we're on Xobj base = this; while (!base.isContainer()) { base = base.ensureParent(); } // Special handling for the no-namespace case if (ns.length() == 0) { // Search for a namespace decl which defines the default namespace Xobj a = base.findXmlnsForPrefix(""); // If I did not find a default decl or the decl maps to the no namespace, then // the default namespace is mapped to "" if (a == null || a.getXmlnsUri().length() == 0) { return ""; } // At this point, I've found a default namespace which is *not* the no-namespace. // If I can't modify the document to mape the desired no-namespace, I must fail. if (!createIfMissing) { return null; } // Ok, I need to make the default namespace on the nearest container map to "" base.setAttr(_locale.createXmlns(null), ""); return ""; } // Look for an exisiting mapping for the desired uri which has a visible prefix for (Xobj c = base; c != null; c = c._parent) { for (Xobj a = c.firstAttr(); a != null; a = a.nextAttr()) { if (a.isXmlns() && a.getXmlnsUri().equals(ns)) { if (base.findXmlnsForPrefix(a.getXmlnsPrefix()) == a) { return a.getXmlnsPrefix(); } } } } // No exisiting xmlns I can use, need to create one. See if I can first if (!createIfMissing) { return null; } // Sanitize the suggestion. if (suggestion != null && (suggestion.length() == 0 || suggestion.toLowerCase(java.util.Locale.ROOT).startsWith("xml") || base.findXmlnsForPrefix(suggestion) != null)) { suggestion = null; } // If no suggestion, make one up if (suggestion == null) { String prefixBase = QNameHelper.suggestPrefix(ns); suggestion = prefixBase; for (int i = 1; ; suggestion = prefixBase + i++) { if (base.findXmlnsForPrefix(suggestion) == null) { break; } } } // Add a new namespace decl at the top elem if one exists, otherwise at root Xobj c = base; while (!c.isRoot() && !c.ensureParent().isRoot()) { c = c._parent; } base.setAttr(_locale.createXmlns(suggestion), ns); return suggestion; } final QName getValueAsQName() { assert !hasChildren(); // TODO - // caching the QName value in this object would prevent one from having // to repeat all this string arithmatic over and over again. Perhaps // when I make the store capable of handling strong simple types this // can be done ... String value = getValueAsString(WS_COLLAPSE); String prefix, localname; int firstcolon = value.indexOf(':'); if (firstcolon >= 0) { prefix = value.substring(0, firstcolon); localname = value.substring(firstcolon + 1); } else { prefix = ""; localname = value; } String uri = namespaceForPrefix(prefix, true); if (uri == null) { return null; // no prefix definition found - that's illegal } return new QName(uri, localname); } final Xobj getAttr(QName name) { for (Xobj x = _firstChild; x != null && x.isAttr(); x = x._nextSibling) { if (x._name.equals(name)) { return x; } } return null; } final QName getXsiTypeName() { assert isContainer(); Xobj a = getAttr(Locale._xsiType); return a == null ? null : a.getValueAsQName(); } final XmlObject getObject() { return isUserNode() ? (XmlObject) getUser() : null; } final TypeStoreUser getUser() { assert isUserNode(); assert _user != null || (!isRoot() && !isStableUser()); if (_user == null) { // BUGBUG - this is recursive TypeStoreUser parentUser = _parent == null ? ((TypeStoreUserFactory) XmlBeans.NO_TYPE).createTypeStoreUser() : _parent.getUser(); _user = isElem() ? parentUser.create_element_user(_name, getXsiTypeName()) : parentUser.create_attribute_user(_name); _user.attach_store(this); } return _user; } final void invalidateUser() { assert isValid(); assert _user == null || isUserNode(); if (_user != null) { _user.invalidate_value(); } } final void ensureOccupancy() { assert isValid(); if (isVacant()) { assert isUserNode(); // In order to use Cur to set the value, I mark the // value as occupied and remove the user to prohibit // further user invalidations clearBit(VACANT); TypeStoreUser user = _user; _user = null; String value = user.build_text(this); long saveVersion = _locale._versionAll; long saveVersionSansText = _locale._versionSansText; setValue(value); assert saveVersionSansText == _locale._versionSansText; _locale._versionAll = saveVersion; assert _user == null; _user = user; } } private void setValue(String val) { assert CharUtil.isValid(val, 0, val.length()); // Check for nothing to insert if (val.length() <= 0) { return; } _locale.notifyChange(); Xobj lastAttr = lastAttr(); int startPos = 1; Xobj charOwner = this; if (lastAttr != null) { charOwner = lastAttr; startPos = charOwner.posAfter(); } charOwner.insertCharsHelper(startPos, val, 0, val.length(), true); } // // TypeStore // public SchemaTypeLoader get_schematypeloader() { return _locale._schemaTypeLoader; } public XmlLocale get_locale() { return _locale; } // TODO - remove this when I've replaced the old store public Object get_root_object() { return _locale; } public boolean is_attribute() { assert isValid(); return isAttr(); } public boolean validate_on_set() { assert isValid(); return _locale._validateOnSet; } public void invalidate_text() { _locale.enter(); try { assert isValid(); if (isOccupied()) { if (hasTextNoEnsureOccupancy() || hasChildren()) { TypeStoreUser user = _user; _user = null; Cur c = tempCur(); c.moveNodeContents(null, false); c.release(); assert _user == null; _user = user; } setBit(VACANT); } assert isValid(); } finally { _locale.exit(); } } public String fetch_text(int wsr) { _locale.enter(); try { assert isValid() && isOccupied(); return getValueAsString(wsr); } finally { _locale.exit(); } } public XmlCursor new_cursor() { _locale.enter(); try { Cur c = tempCur(); XmlCursor xc = new Cursor(c); c.release(); return xc; } finally { _locale.exit(); } } public SchemaField get_schema_field() { assert isValid(); if (isRoot()) { return null; } TypeStoreUser parentUser = ensureParent().getUser(); if (isAttr()) { return parentUser.get_attribute_field(_name); } assert isElem(); TypeStoreVisitor visitor = parentUser.new_visitor(); if (visitor == null) { return null; } for (Xobj x = _parent._firstChild; ; x = x._nextSibling) { if (x.isElem()) { visitor.visit(x._name); if (x == this) { return visitor.get_schema_field(); } } } } public void validate(ValidatorListener eventSink) { _locale.enter(); try { Cur c = tempCur(); new Validate(c, eventSink); c.release(); } finally { _locale.exit(); } } public TypeStoreUser change_type(SchemaType type) { _locale.enter(); try { Cur c = tempCur(); c.setType(type, false); c.release(); } finally { _locale.exit(); } return getUser(); } public TypeStoreUser substitute(QName name, SchemaType type) { _locale.enter(); try { Cur c = tempCur(); c.setSubstitution(name, type); c.release(); } finally { _locale.exit(); } return getUser(); } public QName get_xsi_type() { return getXsiTypeName(); } public void store_text(String text) { _locale.enter(); TypeStoreUser user = _user; _user = null; try { Cur c = tempCur(); c.moveNodeContents(null, false); if (text != null && text.length() > 0) { c.next(); c.insertString(text); } c.release(); } finally { assert _user == null; _user = user; _locale.exit(); } } public int compute_flags() { if (isRoot()) { return 0; } TypeStoreUser parentUser = ensureParent().getUser(); if (isAttr()) { return parentUser.get_attributeflags(_name); } int f = parentUser.get_elementflags(_name); if (f != -1) { return f; } TypeStoreVisitor visitor = parentUser.new_visitor(); if (visitor == null) { return 0; } for (Xobj x = _parent._firstChild; ; x = x._nextSibling) { if (x.isElem()) { visitor.visit(x._name); if (x == this) { return visitor.get_elementflags(); } } } } public String compute_default_text() { if (isRoot()) { return null; } TypeStoreUser parentUser = ensureParent().getUser(); if (isAttr()) { return parentUser.get_default_attribute_text(_name); } String result = parentUser.get_default_element_text(_name); if (result != null) { return result; } TypeStoreVisitor visitor = parentUser.new_visitor(); if (visitor == null) { return null; } for (Xobj x = _parent._firstChild; ; x = x._nextSibling) { if (x.isElem()) { visitor.visit(x._name); if (x == this) { return visitor.get_default_text(); } } } } public boolean find_nil() { if (isAttr()) { return false; } _locale.enter(); try { Xobj a = getAttr(Locale._xsiNil); if (a == null) { return false; } String value = a.getValueAsString(WS_COLLAPSE); return value.equals("true") || value.equals("1"); } finally { _locale.exit(); } } public void invalidate_nil() { if (isAttr()) { return; } _locale.enter(); try { if (!_user.build_nil()) { removeAttr(Locale._xsiNil); } else { setAttr(Locale._xsiNil, "true"); } } finally { _locale.exit(); } } public int count_elements(QName name) { return _locale.count(this, name, null); } public int count_elements(QNameSet names) { return _locale.count(this, null, names); } public TypeStoreUser find_element_user(QName name, int i) { for (Xobj x = _firstChild; x != null; x = x._nextSibling) { if (x.isElem() && x._name.equals(name) && --i < 0) { return x.getUser(); } } return null; } public TypeStoreUser find_element_user(QNameSet names, int i) { for (Xobj x = _firstChild; x != null; x = x._nextSibling) { if (x.isElem() && names.contains(x._name) && --i < 0) { return x.getUser(); } } return null; } @SuppressWarnings("unchecked") @Override public void find_all_element_users(QName name, List fillMeUp) { for (Xobj x = _firstChild; x != null; x = x._nextSibling) { if (x.isElem() && x._name.equals(name)) { fillMeUp.add((T) x.getUser()); } } } @SuppressWarnings("unchecked") @Override public void find_all_element_users(QNameSet names, List fillMeUp) { for (Xobj x = _firstChild; x != null; x = x._nextSibling) { if (x.isElem() && names.contains(x._name)) { fillMeUp.add((T) x.getUser()); } } } private static TypeStoreUser insertElement(QName name, Xobj x, int pos) { x._locale.enter(); try { Cur c = x._locale.tempCur(); c.moveTo(x, pos); c.createElement(name); TypeStoreUser user = c.getUser(); c.release(); return user; } finally { x._locale.exit(); } } public TypeStoreUser insert_element_user(QName name, int i) { if (i < 0) { throw new IndexOutOfBoundsException(); } if (!isContainer()) { throw new IllegalStateException(); } Xobj x = _locale.findNthChildElem(this, name, null, i); if (x == null) { if (i > _locale.count(this, name, null) + 1) { throw new IndexOutOfBoundsException(); } return add_element_user(name); } return insertElement(name, x, 0); } public TypeStoreUser insert_element_user(QNameSet names, QName name, int i) { if (i < 0) { throw new IndexOutOfBoundsException(); } if (!isContainer()) { throw new IllegalStateException(); } Xobj x = _locale.findNthChildElem(this, null, names, i); if (x == null) { if (i > _locale.count(this, null, names) + 1) { throw new IndexOutOfBoundsException(); } return add_element_user(name); } return insertElement(name, x, 0); } public TypeStoreUser add_element_user(QName name) { if (!isContainer()) { throw new IllegalStateException(); } QNameSet endSet = null; boolean gotEndSet = false; Xobj candidate = null; for (Xobj x = _lastChild; x != null; x = x._prevSibling) { if (x.isContainer()) { if (x._name.equals(name)) { break; } if (!gotEndSet) { endSet = _user.get_element_ending_delimiters(name); gotEndSet = true; } if (endSet == null || endSet.contains(x._name)) { candidate = x; } } } return candidate == null ? insertElement(name, this, END_POS) : insertElement(name, candidate, 0); } private static void removeElement(Xobj x) { if (x == null) { throw new IndexOutOfBoundsException(); } x._locale.enter(); try { Cur c = x.tempCur(); c.moveNode(null); c.release(); } finally { x._locale.exit(); } } public void remove_element(QName name, int i) { if (i < 0) { throw new IndexOutOfBoundsException(); } if (!isContainer()) { throw new IllegalStateException(); } Xobj x; for (x = _firstChild; x != null; x = x._nextSibling) { if (x.isElem() && x._name.equals(name) && --i < 0) { break; } } removeElement(x); } public void remove_element(QNameSet names, int i) { if (i < 0) { throw new IndexOutOfBoundsException(); } if (!isContainer()) { throw new IllegalStateException(); } Xobj x; for (x = _firstChild; x != null; x = x._nextSibling) { if (x.isElem() && names.contains(x._name) && --i < 0) { break; } } removeElement(x); } public TypeStoreUser find_attribute_user(QName name) { Xobj a = getAttr(name); return a == null ? null : a.getUser(); } public TypeStoreUser add_attribute_user(QName name) { if (getAttr(name) != null) { throw new IndexOutOfBoundsException(); } _locale.enter(); try { return setAttr(name, "").getUser(); } finally { _locale.exit(); } } public void remove_attribute(QName name) { _locale.enter(); try { if (!removeAttr(name)) { throw new IndexOutOfBoundsException(); } } finally { _locale.exit(); } } public TypeStoreUser copy_contents_from(TypeStore source) { Xobj xSrc = (Xobj) source; if (xSrc == this) { return getUser(); } _locale.enter(); try { xSrc._locale.enter(); Cur c = tempCur(); try { Cur cSrc1 = xSrc.tempCur(); Map sourceNamespaces = Locale.getAllNamespaces(cSrc1, null); cSrc1.release(); if (isAttr()) { Cur cSrc = xSrc.tempCur(); String value = Locale.getTextValue(cSrc); cSrc.release(); c.setValue(value); } else { // Here I save away the user of this node so that it does not get whacked // in the following operations. disconnectChildrenUsers(); assert !inhibitDisconnect(); setBit(INHIBIT_DISCONNECT); QName xsiType = isContainer() ? getXsiTypeName() : null; Xobj copy = xSrc.copyNode(_locale); Cur.moveNodeContents(this, null, true); c.next(); Cur.moveNodeContents(copy, c, true); c.moveTo(this); if (xsiType != null) { c.setXsiType(xsiType); } assert inhibitDisconnect(); clearBit(INHIBIT_DISCONNECT); } if (sourceNamespaces != null) { if (!c.isContainer()) { c.toParent(); } Locale.applyNamespaces(c, sourceNamespaces); } } finally { c.release(); xSrc._locale.exit(); } } finally { _locale.exit(); } return getUser(); } public TypeStoreUser copy(SchemaTypeLoader stl, SchemaType type, XmlOptions options) { //do not use a user's Factory method for copying. //XmlFactoryHook hook = XmlFactoryHook.ThreadContext.getHook(); options = XmlOptions.maskNull(options); SchemaType sType = options.getDocumentType(); if (sType == null) { sType = type == null ? XmlObject.type : type; } Locale locale = this.locale(); if (options.isCopyUseNewSynchronizationDomain()) { locale = Locale.getLocale(stl, options); } boolean isFragment = !sType.isDocumentType() && !(sType.isNoType() && (this instanceof DocumentXobj)); Xobj destination = Cur.createDomDocumentRootXobj(locale, isFragment); locale.enter(); try { Cur c = destination.tempCur(); c.setType(type); c.release(); } finally { locale.exit(); } return destination.copy_contents_from(this); } public void array_setter(XmlObject[] sources, QName elementName) { _locale.enter(); try { // TODO - this is the quick and dirty implementation, make this faster int m = sources.length; List copies = new ArrayList<>(); List types = new ArrayList<>(); for (XmlObject source : sources) { // TODO - deal with null sources[ i ] here -- what to do? if (source == null) { throw new IllegalArgumentException("Array element null"); } else if (source.isImmutable()) { copies.add(null); types.add(null); } else { Xobj x = ((Xobj) ((TypeStoreUser) source).get_store()); if (x._locale == _locale) { copies.add(x.copyNode(_locale)); } else { x._locale.enter(); try { copies.add(x.copyNode(_locale)); } finally { x._locale.exit(); } } types.add(source.schemaType()); } } int n = count_elements(elementName); for (; n > m; n--) { remove_element(elementName, m); } for (; m > n; n++) { add_element_user(elementName); } assert m == n; List elementsUser = new ArrayList<>(); find_all_element_users(elementName, elementsUser); List elements = elementsUser.stream() .map(x -> (TypeStoreUser) x) .map(TypeStoreUser::get_store) .map(x -> (Xobj) x) .collect(Collectors.toList()); assert elements.size() == n; Cur c = tempCur(); for (int i = 0; i < n; i++) { Xobj x = elements.get(i); if (sources[i].isImmutable()) { x.getObject().set(sources[i]); } else { Cur.moveNodeContents(x, null, true); c.moveTo(x); c.next(); Cur.moveNodeContents(copies.get(i), c, true); x.change_type(types.get(i)); } } c.release(); } finally { _locale.exit(); } } public void visit_elements(TypeStoreVisitor visitor) { throw new RuntimeException("Not implemeneted"); } public XmlObject[] exec_query(String queryExpr, XmlOptions options) { _locale.enter(); try { Cur c = tempCur(); XmlObject[] result = XPathFactory.objectExecQuery(c, queryExpr, options); c.release(); return result; } finally { _locale.exit(); } } public String find_prefix_for_nsuri(String nsuri, String suggested_prefix) { _locale.enter(); try { return prefixForNamespace(nsuri, suggested_prefix, true); } finally { _locale.exit(); } } public String getNamespaceForPrefix(String prefix) { return namespaceForPrefix(prefix, true); } Locale _locale; QName _name; Cur _embedded; Bookmark _bookmarks; int _bits; Xobj _parent; Xobj _nextSibling; Xobj _prevSibling; Xobj _firstChild; Xobj _lastChild; Object _srcValue, _srcAfter; int _offValue, _offAfter; int _cchValue, _cchAfter; // TODO - put this in a ptr off this node CharNode _charNodesValue; CharNode _charNodesAfter; // TODO - put this in a ptr off this node TypeStoreUser _user; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy