| /* 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 javax.xml.namespace.QName; |
| import java.io.PrintStream; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| |
| // DOM Level 3 |
| |
| abstract class Xobj implements TypeStore |
| { |
| static final int TEXT = Cur.TEXT; |
| static final int ROOT = Cur.ROOT; |
| static final int ELEM = Cur.ELEM; |
| static final int ATTR = Cur.ATTR; |
| static final int COMMENT = Cur.COMMENT; |
| static final int PROCINST = Cur.PROCINST; |
| |
| static final int END_POS = Cur.END_POS; |
| static final int NO_POS = Cur.NO_POS; |
| |
| 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 boolean entered ( ) { return _locale.entered(); } |
| |
| 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 cchValue ( ) { return _cchValue; } |
| 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 |
| * |
| * @return |
| */ |
| 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 |
| * @return |
| */ |
| 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 |
| * |
| * @return |
| */ |
| final protected boolean isExistingCharNodesValueUsable() |
| { |
| if (_srcValue == null) return false; |
| if (_charNodesValue != null && _charNodesValue._next == null |
| && _charNodesValue._cch == _cchValue) |
| return true; |
| return false; |
| } |
| 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 |
| * |
| * @return |
| */ |
| 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, (Xobj) 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 < 0 ? cchRight( 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 ( ) |
| { |
| if (isVacant() && (_cchValue != 0 || _user == null)) |
| return false; |
| |
| return true; |
| } |
| |
| 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 END_POS == -1; |
| 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; |
| } |
| |
| if (p == posAfter() - 1) |
| return false; |
| |
| return true; |
| } |
| |
| 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 Xobj 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; |
| } |
| |
| return this; |
| } |
| |
| final Xobj 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; |
| |
| return this; |
| } |
| |
| 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; |
| } |
| |
| static final void disbandXobjs ( Xobj first, Xobj last ) |
| { |
| assert last._locale == first._locale; |
| assert first._parent == null && last._parent == null; |
| assert first._prevSibling == null; |
| assert last._nextSibling == null; |
| assert !first.isRoot(); |
| |
| while ( first != null ) |
| { |
| Xobj next = first._nextSibling; |
| first._nextSibling = first._prevSibling = null; |
| first = next; |
| } |
| } |
| |
| // 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 ; ) |
| { |
| Bookmark next = b._next; |
| |
| // 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 == Locale.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, |
| Locale.WS_PRESERVE); |
| } |
| String getCharsValueAsString ( int off, int cch ) |
| { |
| return getCharsAsString(off + 1, cch, |
| Locale.WS_PRESERVE); |
| } |
| String getValueAsString ( int wsr ) |
| { |
| if (!hasChildren()) |
| { |
| Object src = getFirstChars(); |
| |
| if (wsr == Locale.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( Locale.WS_PRESERVE ); |
| } |
| |
| String getString ( int p, int cch ) |
| { |
| int cchRight = cchRight( p ); |
| |
| if (cchRight == 0) |
| return ""; |
| |
| if (cch < 0 || cch > cchRight) |
| cch = cchRight; |
| |
| int pa = posAfter(); |
| |
| assert p > 0; |
| |
| String s; |
| |
| if (p >= pa) |
| { |
| s = CharUtil.getString( _srcAfter, _offAfter + p - pa, cch ); |
| |
| if (p == pa && cch == _cchAfter) |
| { |
| _srcAfter = s; |
| _offAfter = 0; |
| } |
| } |
| else |
| { |
| s = CharUtil.getString( _srcValue, _offValue + p - 1, cch ); |
| |
| if (p == 1 && cch == _cchValue) |
| { |
| _srcValue = s; |
| _offValue = 0; |
| } |
| } |
| |
| return s; |
| } |
| |
| // 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. |
| * <p> |
| * 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. |
| * <p> |
| * 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. |
| * <p> |
| * 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().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( Locale.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(); |
| Validate validate = 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( Locale.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; |
| } |
| |
| 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( x.getUser() ); |
| } |
| |
| 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( 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(); |
| Xobj destination = null; |
| options = XmlOptions.maskNull(options); |
| SchemaType sType = (SchemaType) options.get(XmlOptions.DOCUMENT_TYPE); |
| |
| if (sType == null) |
| sType = type == null ? XmlObject.type : type; |
| |
| Locale locale = this.locale(); |
| if ( Boolean.TRUE.equals(options.get(XmlOptions.COPY_USE_NEW_SYNC_DOMAIN)) ) |
| locale = Locale.getLocale(stl, options); |
| |
| if (sType.isDocumentType() || (sType.isNoType() && (this instanceof DocumentXobj))) |
| destination = Cur.createDomDocumentRootXobj(locale, false); |
| else |
| destination = Cur.createDomDocumentRootXobj(locale, true); |
| |
| |
| locale.enter(); |
| try |
| { |
| Cur c = destination.tempCur(); |
| c.setType(type); |
| c.release(); |
| } |
| finally |
| { |
| locale.exit(); |
| } |
| |
| TypeStoreUser tsu = destination.copy_contents_from(this); |
| return tsu; |
| } |
| |
| 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; |
| |
| ArrayList copies = new ArrayList(); |
| ArrayList types = new ArrayList(); |
| |
| for ( int i = 0 ; i < m ; i++ ) |
| { |
| // TODO - deal with null sources[ i ] here -- what to do? |
| |
| if (sources[ i ] == null) |
| throw new IllegalArgumentException( "Array element null" ); |
| |
| else if (sources[ i ].isImmutable()) |
| { |
| copies.add( null ); |
| types.add( null ); |
| } |
| else |
| { |
| Xobj x = ((Xobj) ((TypeStoreUser) sources[ i ]).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( sources[ i ].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; |
| |
| ArrayList elements = new ArrayList(); |
| |
| find_all_element_users( elementName, elements ); |
| |
| for ( int i = 0 ; i < elements.size() ; i++ ) |
| elements.set( i, (Xobj) ((TypeStoreUser) elements.get( i )).get_store() ); |
| |
| assert elements.size() == n; |
| |
| Cur c = tempCur(); |
| |
| for ( int i = 0 ; i < n ; i++ ) |
| { |
| Xobj x = (Xobj) 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( (Xobj) copies.get( i ), c, true ); |
| |
| x.change_type( (SchemaType) 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 ) throws XmlException |
| { |
| _locale.enter(); |
| |
| try |
| { |
| Cur c = tempCur(); |
| |
| XmlObject[] result = Query.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; |
| } |