blob: fcb8bfe261b0f7a0848d5568f9b53fd45b4b6c37 [file] [log] [blame]
/* 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.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
import org.w3c.dom.DOMImplementation;
// DOM Level 3
import org.w3c.dom.UserDataHandler;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.TypeInfo;
import javax.xml.transform.Source;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
import org.apache.xmlbeans.impl.soap.Detail;
import org.apache.xmlbeans.impl.soap.DetailEntry;
import org.apache.xmlbeans.impl.soap.MimeHeaders;
import org.apache.xmlbeans.impl.soap.Name;
import org.apache.xmlbeans.impl.soap.SOAPBody;
import org.apache.xmlbeans.impl.soap.SOAPBodyElement;
import org.apache.xmlbeans.impl.soap.SOAPElement;
import org.apache.xmlbeans.impl.soap.SOAPEnvelope;
import org.apache.xmlbeans.impl.soap.SOAPException;
import org.apache.xmlbeans.impl.soap.SOAPFactory;
import org.apache.xmlbeans.impl.soap.SOAPFault;
import org.apache.xmlbeans.impl.soap.SOAPFaultElement;
import org.apache.xmlbeans.impl.soap.SOAPHeader;
import org.apache.xmlbeans.impl.soap.SOAPHeaderElement;
import org.apache.xmlbeans.impl.soap.SOAPPart;
import org.apache.xmlbeans.impl.store.Xobj.Bookmark;
import org.apache.xmlbeans.impl.store.Locale.LoadContext;
import org.apache.xmlbeans.impl.store.DomImpl.Dom;
import org.apache.xmlbeans.impl.store.DomImpl.CharNode;
import org.apache.xmlbeans.impl.store.DomImpl.TextNode;
import org.apache.xmlbeans.impl.store.DomImpl.CdataNode;
import org.apache.xmlbeans.impl.store.DomImpl.SaajTextNode;
import org.apache.xmlbeans.impl.store.DomImpl.SaajCdataNode;
import org.apache.xmlbeans.CDataBookmark;
import org.apache.xmlbeans.XmlBeans;
import org.apache.xmlbeans.XmlLineNumber;
import org.apache.xmlbeans.SchemaField;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.SchemaTypeLoader;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.QNameSet;
import org.apache.xmlbeans.XmlDocumentProperties;
import org.apache.xmlbeans.XmlCursor.XmlBookmark;
import org.apache.xmlbeans.impl.values.TypeStore;
import org.apache.xmlbeans.impl.values.TypeStoreUser;
import org.apache.xmlbeans.impl.values.TypeStoreVisitor;
import org.apache.xmlbeans.impl.values.TypeStoreUserFactory;
import javax.xml.namespace.QName;
import org.apache.xmlbeans.impl.common.ValidatorListener;
import org.apache.xmlbeans.impl.common.XmlLocale;
import org.apache.xmlbeans.impl.common.QNameHelper;
final class Cur
{
static final int TEXT = 0; // Must be 0
static final int ROOT = 1;
static final int ELEM = 2;
static final int ATTR = 3;
static final int COMMENT = 4;
static final int PROCINST = 5;
static final int POOLED = 0;
static final int REGISTERED = 1;
static final int EMBEDDED = 2;
static final int DISPOSED = 3;
static final int END_POS = -1;
static final int NO_POS = -2;
Cur ( Locale l )
{
_locale = l;
_pos = NO_POS;
_tempFrame = -1;
_state = POOLED;
_stackTop = Locations.NULL;
_selectionFirst = -1;
_selectionN = -1;
_selectionLoc = Locations.NULL;
_selectionCount = 0;
}
boolean isPositioned ( ) { assert isNormal(); return _xobj != null; }
static boolean kindIsContainer ( int k ) { return k == ELEM || k == ROOT; }
static boolean kindIsFinish ( int k ) { return k == -ELEM || k == -ROOT; }
int kind ( )
{
assert isPositioned();
int kind = _xobj.kind();
return _pos == 0 ? kind : (_pos == END_POS ? - kind : TEXT);
}
boolean isRoot ( ) { assert isPositioned(); return _pos == 0 && _xobj.kind() == ROOT; }
boolean isElem ( ) { assert isPositioned(); return _pos == 0 && _xobj.kind() == ELEM; }
boolean isAttr ( ) { assert isPositioned(); return _pos == 0 && _xobj.kind() == ATTR; }
boolean isComment ( ) { assert isPositioned(); return _pos == 0 && _xobj.kind() == COMMENT; }
boolean isProcinst ( ) { assert isPositioned(); return _pos == 0 && _xobj.kind() == PROCINST; }
boolean isText ( ) { assert isPositioned(); return _pos > 0; }
boolean isEnd ( ) { assert isPositioned(); return _pos == END_POS && _xobj.kind() ==ELEM;}
boolean isEndRoot ( ) { assert isPositioned(); return _pos == END_POS && _xobj.kind() ==ROOT;}
boolean isNode ( ) { assert isPositioned(); return _pos == 0; }
boolean isContainer ( ) { assert isPositioned(); return _pos == 0 && kindIsContainer( _xobj.kind() ); }
boolean isFinish ( ) { assert isPositioned(); return _pos == END_POS && kindIsContainer( _xobj.kind() ); }
boolean isUserNode ( ) { assert isPositioned(); int k = kind(); return k == ELEM || k == ROOT || (k == ATTR && !isXmlns()); }
boolean isContainerOrFinish ( )
{
assert isPositioned();
if (_pos!=0 && _pos!= END_POS)
return false;
int kind = _xobj.kind();
return kind == ELEM || kind == -ELEM || kind == ROOT || kind == -ROOT;
}
boolean isNormalAttr ( ) { return isNode() && _xobj.isNormalAttr(); }
boolean isXmlns ( ) { return isNode() && _xobj.isXmlns(); }
boolean isTextCData ( ) { return _xobj.hasBookmark(CDataBookmark.class, _pos); }
QName getName ( ) { assert isNode() || isEnd(); return _xobj._name; }
String getLocal ( ) { return getName().getLocalPart(); }
String getUri ( ) { return getName().getNamespaceURI(); }
String getXmlnsPrefix ( ) { assert isXmlns(); return _xobj.getXmlnsPrefix(); }
String getXmlnsUri ( ) { assert isXmlns(); return _xobj.getXmlnsUri(); }
boolean isDomDocRoot ( ) { return isRoot() && _xobj.getDom() instanceof Document; }
boolean isDomFragRoot ( ) { return isRoot() && _xobj.getDom() instanceof DocumentFragment; }
int cchRight ( ) { assert isPositioned(); return _xobj.cchRight( _pos ); }
int cchLeft ( ) { assert isPositioned(); return _xobj.cchLeft ( _pos ); }
//
// Creation methods
//
void createRoot ( )
{
createDomDocFragRoot();
}
void createDomDocFragRoot ( )
{
moveTo( new Xobj.DocumentFragXobj( _locale ) );
}
void createDomDocumentRoot ( )
{
moveTo( createDomDocumentRootXobj( _locale ) );
}
void createAttr ( QName name )
{
createHelper( new Xobj.AttrXobj( _locale, name ) );
}
void createComment ( )
{
createHelper( new Xobj.CommentXobj( _locale ) );
}
void createProcinst ( String target )
{
createHelper( new Xobj.ProcInstXobj( _locale, target ) );
}
void createElement ( QName name )
{
createElement( name, null );
}
void createElement ( QName name, QName parentName )
{
createHelper( createElementXobj( _locale, name, parentName ) );
}
static Xobj createDomDocumentRootXobj ( Locale l )
{
return createDomDocumentRootXobj(l, false);
}
static Xobj createDomDocumentRootXobj ( Locale l , boolean fragment)
{
Xobj xo;
if (l._saaj == null)
if (fragment)
xo = new Xobj.DocumentFragXobj( l );
else
xo = new Xobj.DocumentXobj( l );
else
xo = new Xobj.SoapPartDocXobj( l );
if (l._ownerDoc == null)
l._ownerDoc = xo.getDom();
return xo;
}
static Xobj createElementXobj ( Locale l, QName name, QName parentName )
{
if (l._saaj == null)
return new Xobj.ElementXobj( l, name );
Class c = l._saaj.identifyElement( name, parentName );
if (c == SOAPElement.class) return new Xobj.SoapElementXobj ( l, name );
if (c == SOAPBody.class) return new Xobj.SoapBodyXobj ( l, name );
if (c == SOAPBodyElement.class) return new Xobj.SoapBodyElementXobj ( l, name );
if (c == SOAPEnvelope.class) return new Xobj.SoapEnvelopeXobj ( l, name );
if (c == SOAPHeader.class) return new Xobj.SoapHeaderXobj ( l, name );
if (c == SOAPHeaderElement.class) return new Xobj.SoapHeaderElementXobj ( l, name );
if (c == SOAPFaultElement.class) return new Xobj.SoapFaultElementXobj ( l, name );
if (c == Detail.class) return new Xobj.DetailXobj ( l, name );
if (c == DetailEntry.class) return new Xobj.DetailEntryXobj ( l, name );
if (c == SOAPFault.class) return new Xobj.SoapFaultXobj ( l, name );
throw new IllegalStateException( "Unknown SAAJ element class: " + c );
}
private void createHelper ( Xobj x )
{
assert x._locale == _locale;
// insert the new Xobj into an exisiting tree.
if (isPositioned())
{
Cur from = tempCur( x, 0 );
from.moveNode( this );
from.release();
}
moveTo( x );
}
//
// General operations
//
boolean isSamePos ( Cur that )
{
assert isNormal() && (that == null || that.isNormal());
return _xobj == that._xobj && _pos == that._pos;
}
// is this just after the end of that (that must be the start of a node)
boolean isJustAfterEnd ( Cur that )
{
assert isNormal() && that != null && that.isNormal() && that.isNode();
return that._xobj.isJustAfterEnd( _xobj, _pos );
}
boolean isJustAfterEnd ( Xobj x )
{
return x.isJustAfterEnd( _xobj, _pos );
}
boolean isAtEndOf ( Cur that )
{
assert that != null && that.isNormal() && that.isNode();
return _xobj == that._xobj && _pos == END_POS;
}
boolean isInSameTree ( Cur that )
{
assert isPositioned() && that.isPositioned();
return _xobj.isInSameTree( that._xobj );
}
// Retunr -1, 0 or 1 for relative cursor positions. Return 2 is not in sames trees.
int comparePosition ( Cur that )
{
assert isPositioned() && that.isPositioned();
// If in differnet locales, then can't comapre
if (_locale != that._locale)
return 2;
// No need to denormalize, but I want positions which I can compare (no END_POS)
Xobj xThis = _xobj;
int pThis = _pos == END_POS ? xThis.posAfter() - 1 : _pos;
Xobj xThat = that._xobj;
int pThat = that._pos == END_POS ? xThat.posAfter() - 1 : that._pos;
// There are several cases:
//
// 1. Cursors are on the same xobj
// 2. One cursor is a child of the other
// 3. Cursors share a common parent
// 4. Cursors are not in the same trees
//
// Check for the first, trivial, case. Then, compute the depths of the nodes the
// cursors are on, checkin for case 2
//
if (xThis == xThat)
return pThis < pThat ? -1 : pThis == pThat ? 0 : 1;
// Compute the depth of xThis. See if I hit xThat (case 2)
int dThis = 0;
for ( Xobj x = xThis._parent ; x != null ; x = x._parent )
{
dThis++;
if (x == xThat)
return pThat < xThat.posAfter() - 1 ? 1 : -1;
}
// Compute the depth of xThat. See if I hit xThis (case 2)
int dThat = 0;
for ( Xobj x = xThat._parent ; x != null ; x = x._parent )
{
dThat++;
if (x == xThis)
return pThis < xThis.posAfter() - 1 ? -1 : 1;
}
// Must be case 3 or 4 now. Find a common parent. If none, then it's case 4
while ( dThis > dThat ) { dThis--; xThis = xThis._parent; }
while ( dThat > dThis ) { dThat--; xThat = xThat._parent; }
assert dThat == dThis;
if (dThat == 0)
return 2;
assert xThis._parent != null && xThat._parent != null;
while ( xThis._parent != xThat._parent )
{
if ((xThis = xThis._parent) == null)
return 2;
xThat = xThat._parent;
}
// Now, see where xThis and XThat are relative to eachother in the childlist. Apply
// some quick common checks before iterating.
if (xThis._prevSibling == null || xThat._nextSibling == null)
return -1;
if (xThis._nextSibling == null || xThat._prevSibling == null)
return 1;
while ( xThis != null )
if ((xThis = xThis._prevSibling) == xThat)
return 1;
return -1;
}
void setName ( QName newName )
{
assert isNode() && newName != null;
_xobj.setName( newName );
}
void moveTo ( Xobj x )
{
moveTo( x, 0 );
}
void moveTo ( Xobj x, int p )
{
// This cursor may not be normalized upon entry, don't assert isNormal() here
assert x == null || _locale == x._locale;
assert x != null || p == NO_POS;
assert x == null || x.isNormal( p ) || ( x.isVacant() && x._cchValue==0 && x._user == null );
assert _state == REGISTERED || _state == EMBEDDED;
assert _state == EMBEDDED || (_xobj == null || !isOnList( _xobj._embedded ));
assert _state == REGISTERED || (_xobj != null && isOnList( _xobj._embedded ));
moveToNoCheck( x, p );
assert isNormal() || ( _xobj.isVacant() && _xobj._cchValue==0 && _xobj._user == null );
}
void moveToNoCheck ( Xobj x, int p )
{
if (_state == EMBEDDED && x != _xobj)
{
_xobj._embedded = listRemove( _xobj._embedded );
_locale._registered = listInsert( _locale._registered );
_state = REGISTERED;
}
_xobj = x;
_pos = p;
}
void moveToCur ( Cur to )
{
assert isNormal() && (to == null || to.isNormal());
if (to == null)
moveTo( null, NO_POS );
else
moveTo( to._xobj, to._pos );
}
void moveToDom ( Dom d )
{
assert _locale == d.locale();
assert d instanceof Xobj || d instanceof Xobj.SoapPartDom;
moveTo( d instanceof Xobj ? (Xobj) d : ((Xobj.SoapPartDom) d)._docXobj );
}
static final class Locations
{
private static final int NULL = -1;
Locations ( Locale l )
{
_locale = l;
_xobjs = new Xobj [ _initialSize ];
_poses = new int [ _initialSize ];
_curs = new Cur [ _initialSize ];
_next = new int [ _initialSize ];
_prev = new int [ _initialSize ];
_nextN = new int [ _initialSize ];
_prevN = new int [ _initialSize ];
for ( int i = _initialSize - 1 ; i >= 0 ; i-- )
{
assert _xobjs[ i ] == null;
_poses [ i ] = NO_POS;
_next [ i ] = i + 1;
_prev [ i ] = NULL;
_nextN [ i ] = NULL;
_prevN [ i ] = NULL;
}
_next [ _initialSize - 1 ] = NULL;
_free = 0;
_naked = NULL;
}
boolean isSamePos ( int i, Cur c )
{
if (_curs[ i ] == null)
return c._xobj == _xobjs[ i ] && c._pos == _poses[ i ];
else
return c.isSamePos( _curs[ i ] );
}
boolean isAtEndOf ( int i, Cur c )
{
assert _curs[ i ] != null || _poses[ i ] == 0;
assert _curs[ i ] == null || _curs[ i ].isNode();
if (_curs[ i ] == null)
return c._xobj == _xobjs[ i ] && c._pos == END_POS;
else
return c.isAtEndOf( _curs[ i ] );
}
void moveTo ( int i, Cur c )
{
if (_curs[ i ] == null)
c.moveTo( _xobjs[ i ], _poses[ i ] );
else
c.moveToCur( _curs[ i ] );
}
int insert ( int head, int before, int i )
{
return insert( head, before, i, _next, _prev );
}
int remove ( int head, int i )
{
Cur c = _curs[ i ];
assert c != null || _xobjs[ i ] != null;
assert c != null || _xobjs[ i ] != null;
if (c != null)
{
_curs[ i ].release();
_curs[ i ] = null;
assert _xobjs[ i ] == null;
assert _poses [ i ] == NO_POS;
}
else
{
assert _xobjs[ i ] != null && _poses[ i ] != NO_POS;
_xobjs[ i ] = null;
_poses[ i ] = NO_POS;
_naked = remove( _naked, i, _nextN, _prevN );
}
head = remove( head, i, _next, _prev );
_next[ i ] = _free;
_free = i;
return head;
}
int allocate ( Cur addThis )
{
assert addThis.isPositioned();
if (_free == NULL)
makeRoom();
int i = _free;
_free = _next [ i ];
_next [ i ] = NULL;
assert _prev [ i ] == NULL;
assert _curs [ i ] == null;
assert _xobjs[ i ] == null;
assert _poses[ i ] == NO_POS;
_xobjs [ i ] = addThis._xobj;
_poses [ i ] = addThis._pos;
_naked = insert( _naked, NULL, i, _nextN, _prevN );
return i;
}
private static int insert ( int head, int before, int i, int[] next, int[] prev )
{
if (head == NULL)
{
assert before == NULL;
prev[ i ] = i;
head = i;
}
else if (before != NULL)
{
prev[ i ] = prev[ before ];
next[ i ] = before;
prev[ before ] = i;
if (head == before)
head = i;
}
else
{
prev[ i ] = prev[ head ];
assert next[ i ] == NULL;
next[ prev[ head ] ] = i;
prev[ head ] = i;
}
return head;
}
private static int remove ( int head, int i, int[] next, int[] prev )
{
if (prev[ i ] == i)
{
assert head == i;
head = NULL;
}
else
{
if (head == i)
head = next[ i ];
else
next[ prev [ i ] ] = next[ i ];
if (next[ i ] == NULL)
prev[ head ] = prev[ i ];
else
{
prev[ next[ i ] ] = prev[ i ];
next[ i ] = NULL;
}
}
prev[ i ] = NULL;
assert next[ i ] == NULL;
return head;
}
void notifyChange ( )
{
for ( int i ; (i = _naked) != NULL ; )
{
assert _curs[ i ] == null && _xobjs[ i ] != null && _poses[ i ] != NO_POS;
_naked = remove( _naked, i, _nextN, _prevN );
_curs[ i ] = _locale.getCur();
_curs[ i ].moveTo( _xobjs[ i ], _poses[ i ] );
_xobjs[ i ] = null;
_poses[ i ] = NO_POS;
}
}
int next ( int i ) { return _next[ i ]; }
int prev ( int i ) { return _prev[ i ]; }
private void makeRoom ( )
{
assert _free == NULL;
int l = _xobjs.length;
Xobj [] oldXobjs = _xobjs;
int [] oldPoses = _poses;
Cur [] oldCurs = _curs;
int [] oldNext = _next;
int [] oldPrev = _prev;
int [] oldNextN = _nextN;
int [] oldPrevN = _prevN;
_xobjs = new Xobj [ l * 2 ];
_poses = new int [ l * 2 ];
_curs = new Cur [ l * 2 ];
_next = new int [ l * 2 ];
_prev = new int [ l * 2 ];
_nextN = new int [ l * 2 ];
_prevN = new int [ l * 2 ];
System.arraycopy( oldXobjs, 0, _xobjs, 0, l );
System.arraycopy( oldPoses, 0, _poses, 0, l );
System.arraycopy( oldCurs, 0, _curs, 0, l );
System.arraycopy( oldNext, 0, _next, 0, l );
System.arraycopy( oldPrev, 0, _prev, 0, l );
System.arraycopy( oldNextN, 0, _nextN, 0, l );
System.arraycopy( oldPrevN, 0, _prevN, 0, l );
for ( int i = l * 2 - 1 ; i >= l ; i-- )
{
_next [ i ] = i + 1;
_prev [ i ] = NULL;
_nextN [ i ] = NULL;
_prevN [ i ] = NULL;
_poses [ i ] = NO_POS;
}
_next [ l * 2 - 1 ] = NULL;
_free = l;
}
private static final int _initialSize = 32;
private Locale _locale;
private Xobj [] _xobjs;
private int [] _poses;
private Cur [] _curs;
private int [] _next;
private int [] _prev;
private int [] _nextN;
private int [] _prevN;
private int _free; // Unused entries
private int _naked; // Entries without Curs
}
void push ( )
{
assert isPositioned();
int i = _locale._locations.allocate( this );
_stackTop = _locale._locations.insert( _stackTop, _stackTop, i );
}
void pop ( boolean stay )
{
if (stay)
popButStay();
else
pop();
}
void popButStay ( )
{
if (_stackTop != Locations.NULL)
_stackTop = _locale._locations.remove( _stackTop, _stackTop );
}
boolean pop ( )
{
if (_stackTop == Locations.NULL)
return false;
_locale._locations.moveTo( _stackTop, this );
_stackTop = _locale._locations.remove( _stackTop, _stackTop );
return true;
}
boolean isAtLastPush ( )
{
assert _stackTop != Locations.NULL;
return _locale._locations.isSamePos( _stackTop, this );
}
boolean isAtEndOfLastPush ( )
{
assert _stackTop != Locations.NULL;
return _locale._locations.isAtEndOf( _stackTop, this );
}
void addToSelection ( Cur that )
{
assert that != null && that.isNormal();
assert isPositioned() && that.isPositioned();
int i = _locale._locations.allocate( that );
_selectionFirst = _locale._locations.insert( _selectionFirst, Locations.NULL, i );
_selectionCount++;
}
void addToSelection ( )
{
assert isPositioned();
int i = _locale._locations.allocate( this );
_selectionFirst = _locale._locations.insert( _selectionFirst, Locations.NULL, i );
_selectionCount++;
}
private int selectionIndex ( int i )
{
assert _selectionN >= -1 && i >= 0 && i < _selectionCount;
if (_selectionN == -1)
{
_selectionN = 0;
_selectionLoc = _selectionFirst;
}
while ( _selectionN < i )
{
_selectionLoc = _locale._locations.next( _selectionLoc );
_selectionN++;
}
while ( _selectionN > i )
{
_selectionLoc = _locale._locations.prev( _selectionLoc );
_selectionN--;
}
return _selectionLoc;
}
void removeSelection ( int i )
{
assert i >= 0 && i < _selectionCount;
int j = selectionIndex( i );
// Update the nth selection indices to accomodate the deletion
if (i < _selectionN)
_selectionN--;
else if (i == _selectionN)
{
_selectionN--;
if (i == 0)
_selectionLoc = Locations.NULL;
else
_selectionLoc = _locale._locations.prev( _selectionLoc );
}
_selectionFirst = _locale._locations.remove( _selectionFirst, j );
_selectionCount--;
}
int selectionCount ( )
{
return _selectionCount;
}
void moveToSelection ( int i )
{
assert i >= 0 && i < _selectionCount;
_locale._locations.moveTo( selectionIndex( i ), this );
}
void clearSelection ( )
{
assert _selectionCount >= 0;
while ( _selectionCount > 0 )
removeSelection( 0 );
}
boolean toParent ( ) { return toParent( false ); }
boolean toParentRaw ( ) { return toParent( true ); }
Xobj getParent ( ) { return getParent( false ); }
Xobj getParentRaw ( ) { return getParent( true ); }
boolean hasParent ( )
{
assert isPositioned();
if (_pos == END_POS || (_pos >= 1 && _pos < _xobj.posAfter()))
return true;
assert _pos == 0 || _xobj._parent != null;
return _xobj._parent != null;
}
Xobj getParentNoRoot()
{
assert isPositioned();
if (_pos == END_POS || (_pos >= 1 && _pos < _xobj.posAfter()))
return _xobj;
assert _pos == 0 || _xobj._parent != null;
if (_xobj._parent != null)
return _xobj._parent;
return null;
}
Xobj getParent ( boolean raw )
{
assert isPositioned();
if (_pos == END_POS || (_pos >= 1 && _pos < _xobj.posAfter()))
return _xobj;
assert _pos == 0 || _xobj._parent != null;
if (_xobj._parent != null)
return _xobj._parent;
if (raw || _xobj.isRoot())
return null;
Cur r = _locale.tempCur();
r.createRoot();
Xobj root = r._xobj;
r.next();
moveNode( r );
r.release();
return root;
}
boolean toParent ( boolean raw )
{
Xobj parent = getParent( raw );
if (parent == null)
return false;
moveTo( parent );
return true;
}
void toRoot ()
{
Xobj xobj = _xobj;
while (!xobj.isRoot())
{
if (xobj._parent==null)
{
Cur r = _locale.tempCur();
r.createRoot();
Xobj root = r._xobj;
r.next();
moveNode( r );
r.release();
xobj = root;
break;
}
xobj = xobj._parent;
}
moveTo(xobj);
}
boolean hasText ( )
{
assert isNode();
return _xobj.hasTextEnsureOccupancy();
}
boolean hasAttrs ( )
{
assert isNode();
return _xobj.hasAttrs();
}
boolean hasChildren ( )
{
assert isNode();
return _xobj.hasChildren();
}
boolean toFirstChild ( )
{
assert isNode();
if (!_xobj.hasChildren())
return false;
for ( Xobj x = _xobj._firstChild ; ; x = x._nextSibling )
{
if (!x.isAttr())
{
moveTo( x );
return true;
}
}
}
protected boolean toLastChild ( )
{
assert isNode();
if (!_xobj.hasChildren())
return false;
moveTo( _xobj._lastChild );
return true;
}
boolean toNextSibling ( )
{
assert isNode();
if (_xobj.isAttr())
{
if (_xobj._nextSibling != null && _xobj._nextSibling.isAttr())
{
moveTo( _xobj._nextSibling );
return true;
}
}
else if (_xobj._nextSibling != null)
{
moveTo( _xobj._nextSibling );
return true;
}
return false;
}
void setValueAsQName ( QName qname )
{
assert isNode();
String value = qname.getLocalPart();
String ns = qname.getNamespaceURI();
String prefix =
prefixForNamespace(
ns, qname.getPrefix().length() > 0 ? qname.getPrefix() : null, true );
if (prefix.length() > 0)
value = prefix + ":" + value;
setValue( value );
}
void setValue ( String value )
{
assert isNode();
moveNodeContents( null, false );
next();
insertString( value );
toParent();
}
void removeFollowingAttrs ( )
{
assert isAttr();
QName attrName = getName();
push();
if (toNextAttr())
{
while ( isAttr() )
{
if (getName().equals( attrName ))
moveNode( null );
else if (!toNextAttr())
break;
}
}
pop();
}
String getAttrValue ( QName name )
{
String s = null;
push();
if (toAttr( name ))
s = getValueAsString();
pop();
return s;
}
void setAttrValueAsQName ( QName name, QName value )
{
assert isContainer();
if (value == null)
{
_xobj.removeAttr( name );
}
else
{
if (toAttr( name ))
{
removeFollowingAttrs();
}
else
{
next();
createAttr( name );
}
setValueAsQName( value );
toParent();
}
}
boolean removeAttr ( QName name )
{
assert isContainer();
return _xobj.removeAttr( name );
}
void setAttrValue ( QName name, String value )
{
assert isContainer();
_xobj.setAttr( name, value );
}
boolean toAttr ( QName name )
{
assert isNode();
Xobj a = _xobj.getAttr( name );
if (a == null)
return false;
moveTo( a );
return true;
}
boolean toFirstAttr ( )
{
assert isNode();
Xobj firstAttr = _xobj.firstAttr();
if (firstAttr == null)
return false;
moveTo( firstAttr );
return true;
}
boolean toLastAttr ( )
{
assert isNode();
if (!toFirstAttr())
return false;
while ( toNextAttr() )
;
return true;
}
boolean toNextAttr ( )
{
assert isAttr() || isContainer();
Xobj nextAttr = _xobj.nextAttr();
if (nextAttr == null)
return false;
moveTo( nextAttr );
return true;
}
boolean toPrevAttr ( )
{
if (isAttr())
{
if (_xobj._prevSibling == null)
moveTo( _xobj.ensureParent() );
else
moveTo( _xobj._prevSibling );
return true;
}
prev();
if (!isContainer())
{
next();
return false;
}
return toLastAttr();
}
boolean skipWithAttrs ( )
{
assert isNode();
if (skip())
return true;
if (_xobj.isRoot())
return false;
assert _xobj.isAttr();
toParent();
next();
return true;
}
boolean skip ( )
{
assert isNode();
if (_xobj.isRoot())
return false;
if (_xobj.isAttr())
{
if (_xobj._nextSibling == null || !_xobj._nextSibling.isAttr())
return false;
moveTo( _xobj._nextSibling, 0 );
}
else
moveTo( getNormal( _xobj, _xobj.posAfter() ), _posTemp );
return true;
}
void toEnd ( )
{
assert isNode();
moveTo( _xobj, END_POS );
}
void moveToCharNode ( CharNode node )
{
assert node.getDom() != null && node.getDom().locale() == _locale;
moveToDom( node.getDom() );
CharNode n;
_xobj.ensureOccupancy();
n = _xobj._charNodesValue =
updateCharNodes( _locale, _xobj, _xobj._charNodesValue, _xobj._cchValue );
for ( ; n != null ; n = n._next )
{
if (node == n)
{
moveTo( getNormal( _xobj, n._off + 1 ), _posTemp );
return;
}
}
n = _xobj._charNodesAfter =
updateCharNodes( _locale, _xobj, _xobj._charNodesAfter, _xobj._cchAfter );
for ( ; n != null ; n = n._next )
{
if (node == n)
{
moveTo( getNormal( _xobj, n._off + _xobj._cchValue + 2 ), _posTemp );
return;
}
}
assert false;
}
boolean prevWithAttrs ( )
{
if (prev())
return true;
if (!isAttr())
return false;
toParent();
return true;
}
boolean prev ( )
{
assert isPositioned();
if (_xobj.isRoot() && _pos == 0)
return false;
if (_xobj.isAttr() && _pos == 0 && _xobj._prevSibling == null)
return false;
Xobj x = getDenormal();
int p = _posTemp;
assert p > 0 && p != END_POS;
int pa = x.posAfter();
if (p > pa)
p = pa;
else if (p == pa)
{
// Text after an attr is allowed only on the last attr,
// and that text belongs to the parent container..
//
// If we're a thte end of the last attr, then we were just
// inside the container, and we need to skip the attrs.
if (x.isAttr() &&
(x._cchAfter > 0 || x._nextSibling == null || !x._nextSibling.isAttr()))
{
x = x.ensureParent();
p = 0;
}
else
p = END_POS;
}
else if (p == pa - 1)
{
x.ensureOccupancy();
p = x._cchValue > 0 ? 1 : 0;
}
else if (p > 1)
p = 1;
else
{
assert p == 1;
p = 0;
}
moveTo( getNormal( x, p ), _posTemp );
return true;
}
boolean next ( boolean withAttrs )
{
return withAttrs ? nextWithAttrs() : next();
}
boolean nextWithAttrs ( )
{
int k = kind();
if (kindIsContainer( k ))
{
if (toFirstAttr())
return true;
}
else if (k == -ATTR)
{
if (next())
return true;
toParent();
if (!toParentRaw())
return false;
}
return next();
}
boolean next ( )
{
assert isNormal();
Xobj x = _xobj;
int p = _pos;
int pa = x.posAfter();
if (p >= pa)
p = _xobj.posMax();
else if (p == END_POS)
{
if (x.isRoot() || (x.isAttr() && (x._nextSibling == null || !x._nextSibling.isAttr())))
return false;
p = pa;
}
else if (p > 0)
{
assert x._firstChild == null || !x._firstChild.isAttr();
if (x._firstChild != null)
{
x = x._firstChild;
p = 0;
}
else
p = END_POS;
}
else
{
assert p == 0;
x.ensureOccupancy();
p = 1;
if (x._cchValue == 0)
{
if (x._firstChild != null)
{
if (x._firstChild.isAttr())
{
Xobj a = x._firstChild;
while ( a._nextSibling != null && a._nextSibling.isAttr() )
a = a._nextSibling;
if (a._cchAfter > 0)
{
x = a;
p = a.posAfter();
}
else if (a._nextSibling != null)
{
x = a._nextSibling;
p = 0;
}
}
else
{
x = x._firstChild;
p = 0;
}
}
}
}
moveTo( getNormal( x, p ), _posTemp );
return true;
}
int prevChars ( int cch )
{
assert isPositioned();
int cchLeft = cchLeft();
if (cch < 0 || cch > cchLeft)
cch = cchLeft;
// Dang, I love this stmt :-)
if (cch != 0)
moveTo( getNormal( getDenormal(), _posTemp - cch ), _posTemp );
return cch;
}
int nextChars ( int cch )
{
assert isPositioned();
int cchRight = cchRight();
if (cchRight == 0)
return 0;
if (cch < 0 || cch >= cchRight)
{
// Use next to not skip over children
next();
return cchRight;
}
moveTo( getNormal( _xobj, _pos + cch ), _posTemp );
return cch;
}
void setCharNodes ( CharNode nodes )
{
assert nodes == null || _locale == nodes.locale();
assert isPositioned();
Xobj x = getDenormal();
int p = _posTemp;
assert !x.isRoot() || (p > 0 && p < x.posAfter());
if (p >= x.posAfter())
x._charNodesAfter = nodes;
else
x._charNodesValue = nodes;
for ( ; nodes != null ; nodes = nodes._next )
nodes.setDom( (Dom) x );
// No Need to notify text change or alter version, text nodes are
// not part of the infoset
}
CharNode getCharNodes ( )
{
assert isPositioned();
assert !isRoot();
Xobj x = getDenormal();
CharNode nodes;
if (_posTemp >= x.posAfter())
{
nodes = x._charNodesAfter =
updateCharNodes( _locale, x, x._charNodesAfter, x._cchAfter );
}
else
{
x.ensureOccupancy();
nodes = x._charNodesValue =
updateCharNodes( _locale, x, x._charNodesValue, x._cchValue );
}
return nodes;
}
// private
static CharNode updateCharNodes ( Locale l, Xobj x, CharNode nodes, int cch )
{
assert nodes == null || nodes.locale() == l;
CharNode node = nodes;
int i = 0;
while ( node != null && cch > 0 )
{
assert node.getDom() == x;
if (node._cch > cch)
node._cch = cch;
node._off = i;
i += node._cch;
cch -= node._cch;
node = node._next;
}
if (cch <= 0)
{
for ( ; node != null ; node = node._next )
{
assert node.getDom() == x;
if (node._cch != 0)
node._cch = 0;
node._off = i;
}
}
else
{
node = l.createTextNode();
node.setDom( (Dom) x );
node._cch = cch;
node._off = i;
nodes = CharNode.appendNode( nodes, node );
}
return nodes;
}
final QName getXsiTypeName ( )
{
assert isNode();
return _xobj.getXsiTypeName();
}
final void setXsiType ( QName value )
{
assert isContainer();
setAttrValueAsQName( Locale._xsiType, value );
}
final QName valueAsQName ( )
{
throw new RuntimeException( "Not implemented" );
}
final String namespaceForPrefix ( String prefix, boolean defaultAlwaysMapped )
{
return _xobj.namespaceForPrefix( prefix, defaultAlwaysMapped );
}
final String prefixForNamespace ( String ns, String suggestion, boolean createIfMissing )
{
return
(isContainer() ? _xobj : getParent()).
prefixForNamespace( ns, suggestion, createIfMissing );
}
// Does the node at this cursor properly contain the position specified by the argument
boolean contains ( Cur that )
{
assert isNode();
assert that != null && that.isPositioned();
return _xobj.contains( that );
}
void insertString ( String s )
{
if (s != null)
insertChars( s, 0, s.length() );
}
void insertChars ( Object src, int off, int cch )
{
assert isPositioned() && !isRoot();
assert CharUtil.isValid( src, off, cch );
// Check for nothing to insert
if (cch <= 0)
return;
_locale.notifyChange();
// The only situation where I need to ensure occupancy is when I'm at the end of a node.
// All other positions will require occupancy. For example, if I'm at the beginning of a
// node, then I will either insert in the after text of the previous sibling, or I will
// insert in the value of the parent. In the latter case, because the parent has a child,
// it cannot be vacant.
if (_pos == END_POS)
_xobj.ensureOccupancy();
// Get the denormailized Xobj and pos. This is the Xobj which will actually receive
// the new chars. Note that a denormalized position can never be <= 0.
Xobj x = getDenormal();
int p = _posTemp;
assert p > 0;
// This will move "this" cursor to be after the inserted text. No worries, I'll update its
// position after. This insertChars takes care of all the appropriate invalidations
// (passing true as last arg).
x.insertCharsHelper( p, src, off, cch, true );
// Reposition the cursor to be just before the newly inserted text. It's current
// position could have been shifted, or it may have been just before the end tag, or
// normalized on another Xobj.
moveTo( x, p );
_locale._versionAll++;
}
// Move the chars just after this Cur to the "to" Cur. If no "to" Cur is specified,
// then remove the chars.
Object moveChars ( Cur to, int cchMove )
{
assert isPositioned();
assert cchMove <= 0 || cchMove <= cchRight();
assert to == null || (to.isPositioned() && !to.isRoot());
if (cchMove < 0)
cchMove = cchRight();
// If we're instructed to move 0 characters, then return the null triple.
if (cchMove == 0)
{
_offSrc = 0;
_cchSrc = 0;
return null;
}
// Here I record the triple of the chars to move. I will return this. No need to save
// cch 'cause cchMove will be that value.
Object srcMoved = getChars( cchMove );
int offMoved = _offSrc;
// Either I'm moving text from the value or the after text. If after, then the container
// must be occupied. If in the value, because we're just before text, it must be occupied.
assert isText() && (_pos >= _xobj.posAfter() ? _xobj._parent : _xobj).isOccupied();
if (to == null)
{
// In this case, I'm removing chars vs moving them. Normally I would like to blow
// them away entirely, but if there are any references to those chars via a bookmark
// I need to keep them alive. I do this by moving these chars to a new root. Note
// that because Curs will stay behind, I don't have to check for them.
for ( Bookmark b = _xobj._bookmarks ; b != null ; b = b._next )
{
if (inChars( b, cchMove, false ))
{
Cur c = _locale.tempCur();
c.createRoot();
c.next();
Object chars = moveChars( c, cchMove );
c.release();
return chars;
}
}
}
else
{
// If the target, "to", is inside or on the edge of the text to be moved, then this
// is a no-op. In this case, I still want to return the text "moved".
//
// Note how I move "to" and this cur around. I move "to" to be at the beginning of the
// chars moved and "this" to be at the end. If the text were really moving to a
// different location, then "to" would be at the beginning of the newly moved chars,
// and "this" would be at the gap left by the newly removed chars.
if (inChars( to, cchMove, true ))
{
// BUGBUG - may want to consider shuffling the interior cursors to the right just
// like I move "this" to the right...
to.moveToCur( this );
nextChars( cchMove );
_offSrc = offMoved;
_cchSrc = cchMove;
return srcMoved;
}
// Copy the chars here, I'll remove the originals next
to.insertChars( srcMoved, offMoved, cchMove );
}
// Notice that I can delay the general change notification to this point because any
// modifications up to this point are made by calling other high level operations which
// generate this notification themselves. Also, no need to notify of general change in
// the "to" locale because the insertion of chars above handles that.
_locale.notifyChange();
//
//if ( _xobj != null )
{
if (to == null)
_xobj.removeCharsHelper( _pos, cchMove, null, NO_POS, false, true );
else
_xobj.removeCharsHelper( _pos, cchMove, to._xobj, to._pos, false, true );
}
// Need to update the position of this cursor even though it did not move anywhere. This
// needs to happen because it may not be properly normalized anymore. Note that because
// of the removal of the text, this cur may not be normal any more, thus I call moveTo
// which does not assume this.
_locale._versionAll++;
_offSrc = offMoved;
_cchSrc = cchMove;
return srcMoved;
}
void moveNode ( Cur to )
{
assert isNode() && !isRoot();
assert to == null || to.isPositioned();
assert to == null || !contains( to );
assert to == null || !to.isRoot();
// TODO - should assert that is an attr is being moved, it is ok there
// Record the node to move and skip this cur past it. This moves this cur to be after
// the move to move/remove -- it's final resting place. The only piece of information
// about the source of the move is the node itself.
Xobj x = _xobj;
skip();
// I call another function here to move the node. I do this because I don't have to
// worry about messing with "this" here given that it not should be treated like any other
// cursor after this point.
moveNode( x, to );
}
// Moves text from one place to another in a low-level way, used as a helper for the higher
// level functions. Takes care of moving bookmarks and cursors. In the high level content
// manipulation functions, cursors do not follow content, but this helper moves them. The
// arguments are denormalized. The Xobj's must be different from eachother but from the same
// locale. The destination must not be not be vacant.
private static void transferChars ( Xobj xFrom, int pFrom, Xobj xTo, int pTo, int cch )
{
assert xFrom != xTo;
assert xFrom._locale == xTo._locale;
assert pFrom > 0 && pFrom < xFrom.posMax();
assert pTo > 0 && pTo <= xTo .posMax();
assert cch > 0 && cch <= xFrom.cchRight( pFrom );
assert pTo >= xTo.posAfter() || xTo.isOccupied();
// Copy the chars from -> to without performing any invalidations. This will scoot curs
// and marks around appropriately. Note that I get the cars with getCharsHelper which
// does not check for normalization because the state of the tree at this moment may not
// exactly be "correct" here.
xTo.insertCharsHelper(
pTo, xFrom.getCharsHelper( pFrom, cch ),
xFrom._locale._offSrc, xFrom._locale._cchSrc, false );
xFrom.removeCharsHelper( pFrom, cch, xTo, pTo, true, false );
}
// Moves the node x to "to", or removes it if to is null.
static void moveNode ( Xobj x, Cur to )
{
assert x != null && !x.isRoot();
assert to == null || to.isPositioned();
assert to == null || !x.contains( to );
assert to == null || !to.isRoot();
if (to != null)
{
// Before I go much further, I want to make sure that if "to" is in the container of
// a vacant node, I get it occupied. I do not need to worry about the source being
// vacant.
if (to._pos == END_POS)
to._xobj.ensureOccupancy();
// See if the destination is on the edge of the node to be moved (a no-op). It is
// illegal to call this fcn when to is contained within the node to be moved. Note
// that I make sure that to gets oved to the beginning of the node. The position of
// to in all operations should leave to just before the content moved/inserted.
if ((to._pos == 0 && to._xobj == x) || to.isJustAfterEnd( x ))
{
// TODO - should shuffle contained curs to the right???
to.moveTo( x );
return;
}
}
// Notify the locale(s) about the change I am about to make.
x._locale.notifyChange();
x._locale._versionAll++;
x._locale._versionSansText++;
if (to != null && to._locale != x._locale)
{
to._locale.notifyChange();
to._locale._versionAll++;
to._locale._versionSansText++;
}
// Node is going away. Invalidate the parent (the text around the node is merging).
// Also, this node may be an attribute -- invalidate special attrs ...
if (x.isAttr())
x.invalidateSpecialAttr( to == null ? null : to.getParentRaw() );
else
{
if (x._parent != null)
x._parent.invalidateUser();
if (to != null && to.hasParent())
to.getParent().invalidateUser();
}
// If there is any text after x, I move it to be before x. This frees me to extract x
// and it's contents with out this text coming along for the ride. Note that if this
// node is the last attr and there is text after it, transferText will move the text
// to a potential previous attr. This is an invalid state for a short period of time.
// I need to move this text away here so that when I walk the tree next, *all* curs
// embedded in this node or deeper will be moved off this node.
if (x._cchAfter > 0)
transferChars( x, x.posAfter(), x.getDenormal( 0 ), x.posTemp(), x._cchAfter );
assert x._cchAfter == 0;
// Walk the node tree, moving curs out, disconnecting users and relocating to a, possibly,
// new locale. I embed the cursors in this locale before itersting to just cause the
// embed to happen once.
x._locale.embedCurs();
for ( Xobj y = x ; y != null ; y = y.walk( x, true ) )
{
while ( y._embedded != null )
y._embedded.moveTo( x.getNormal( x.posAfter() ) );
y.disconnectUser();
if (to != null)
y._locale = to._locale;
}
// Now, actually remove the node
x.removeXobj();
// Now, if there is a destination, insert the node there and shuffle the text in the
// vicinity of the destination appropriately.
if (to != null)
{
// To know where I should insert/append the node to move, I need to see where "to"
// would be if there were no text after it. However, I need to keep "to" where it
// is when I move the text after it later.
Xobj here = to._xobj;
boolean append = to._pos != 0;
int cchRight = to.cchRight();
if (cchRight > 0)
{
to.push();
to.next();
here = to._xobj;
append = to._pos != 0;
to.pop();
}
if (append)
here.appendXobj( x );
else
here.insertXobj( x );
// The only text I need to move is that to the right of "to". Even considering all
// the cases where an attribute is involed!
if (cchRight > 0)
transferChars( to._xobj, to._pos, x, x.posAfter(), cchRight );
to.moveTo( x );
}
}
void moveNodeContents ( Cur to, boolean moveAttrs )
{
assert _pos==0;
assert to == null || !to.isRoot();
// By calling this helper, I do not have to deal with this Cur any longer. Basically,
// this Cur is out of the picture, it behaves like any other cur at this point.
moveNodeContents( _xobj, to, moveAttrs );
}
static void moveNodeContents ( Xobj x, Cur to, boolean moveAttrs )
{
// TODO - should assert that is an attr is being moved, it is ok there
assert to == null || !to.isRoot();
// Collect a bit of information about the contents to move first. Note that the collection
// of this info must not cause a vacant value to become occupied.
boolean hasAttrs = x.hasAttrs();
boolean noSubNodesToMove = !x.hasChildren() && (!moveAttrs || !hasAttrs);
// Deal with the cases where only text is involved in the move
if (noSubNodesToMove)
{
// If we're vacant and there is no place to move a potential value, then I can avoid
// acquiring the text from the TypeStoreUser. Otherwise, there may be text here I
// need to move somewhere else.
if (x.isVacant() && to == null)
{
x.clearBit( Xobj.VACANT );
x.invalidateUser();
x.invalidateSpecialAttr( null );
x._locale._versionAll++;
}
else if (x.hasTextEnsureOccupancy())
{
Cur c = x.tempCur();
c.next();
c.moveChars( to, -1 );
c.release();
}
return;
}
// Here I check to see if "to" is just inside x. In this case this is a no-op. Note that
// the value of x may still be vacant.
if (to != null)
{
// Quick check of the right edge. If it is there, I need to move "to" to the left edge
// so that it is positioned at the beginning of the "moved" content.
if (x == to._xobj && to._pos == END_POS)
{
// TODO - shuffle interior curs?
to.moveTo( x );
to.next( moveAttrs && hasAttrs );
return;
}
// Here I need to see if to is at the left edge. I push to's current position and
// then navigate it to the left edge then compare it to the pushed position...
// Note: gotta be careful to make sure to and x are not in different locales, curs
// may not go to a different locale.
boolean isAtLeftEdge = false;
if (to._locale == x._locale)
{
to.push();
to.moveTo( x );
to.next( moveAttrs && hasAttrs );
isAtLeftEdge = to.isAtLastPush();
to.pop();
}
// TODO - shuffle interior curs?
if (isAtLeftEdge)
return;
// Now, after dealing with the edge condition, I can assert that to is not inside x
assert !x.contains( to );
// So, at this point, I've taken case of the no-op cases and the movement of just text.
// Also, to must be occupied because I took care of the text only and nothing to move
// cases.
assert to.getParent().isOccupied();
}
// TODO - did I forget to put a changeNotification here? Look more closely ...
// Deal with the value text of x which is either on x or the last attribute of x.
// I need to get it out of the way to properly deal with the walk of the contents.
// In order to reposition "to" properly later, I need to record how many chars were moved.
int valueMovedCch = 0;
if (x.hasTextNoEnsureOccupancy())
{
Cur c = x.tempCur();
c.next();
c.moveChars( to, -1 );
c.release();
if (to != null)
to.nextChars( valueMovedCch = c._cchSrc );
}
// Now, walk all the contents, invalidating special attrs, reportioning cursors,
// disconnecting users and relocating to a potentially different locale. Because I moved
// the value text above, no top level attrs should have any text.
x._locale.embedCurs();
Xobj firstToMove = x.walk( x, true );
boolean sawBookmark = false;
for ( Xobj y = firstToMove ; y != null ; y = y.walk( x, true ) )
{
if (y._parent == x && y.isAttr())
{
assert y._cchAfter == 0;
if (!moveAttrs)
{
firstToMove = y._nextSibling;
continue;
}
y.invalidateSpecialAttr( to == null ? null : to.getParent() );
}
for ( Cur c ; (c = y._embedded) != null ; )
c.moveTo( x, END_POS );
y.disconnectUser();
if (to != null)
y._locale = to._locale;
sawBookmark = sawBookmark || y._bookmarks != null;
}
Xobj lastToMove = x._lastChild;
// If there were any bookmarks in the tree to remove, to preserve the content that these
// bookmarks reference, move the contents to a new root. Note that I already moved the
// first piece of text above elsewhere. Note: this has the effect of keeping all of the
// contents alive even if there is one bookmark deep into the tree. I should really
// disband all the content, except for the pieces which are bookmarked.
Cur surragateTo = null;
if (sawBookmark && to == null)
{
surragateTo = to = x._locale.tempCur();
to.createRoot();
to.next();
}
// Perform the rest of the invalidations. If only attrs are moving, then no user
// invalidation needed. If I've move text to "to" already, no need to invalidate
// again.
if (!lastToMove.isAttr())
x.invalidateUser();
x._locale._versionAll++;
x._locale._versionSansText++;
if (to != null && valueMovedCch == 0)
{
to.getParent().invalidateUser();
to._locale._versionAll++;
to._locale._versionSansText++;
}
// Remove the children and, if needed, move them
x.removeXobjs( firstToMove, lastToMove );
if (to != null)
{
// To know where I should insert/append the contents to move, I need to see where "to"
// would be if there were no text after it.
Xobj here = to._xobj;
boolean append = to._pos != 0;
int cchRight = to.cchRight();
if (cchRight > 0)
{
to.push();
to.next();
here = to._xobj;
append = to._pos != 0;
to.pop();
}
// Now, I have to shuffle the text around "to" in special ways. A complication is
// the insertion of attributes. First, if I'm inserting attrs here then, logically,
// there can be no text to the left because attrs can only live after another attr
// or just inside a container. So, If attrs are being inserted and there is value
// text on the target container, I will need to move this value text to be after
// the lew last attribute. Note that this value text may already live on a current
// last attr (before the inserting). Also, I need to figure this all out before I
// move the text after "to" because this text may end up being sent to the same place
// as the containers value text when the last new node being inserted is an attr!
// Whew!
if (firstToMove.isAttr())
{
Xobj lastNewAttr = firstToMove;
while ( lastNewAttr._nextSibling != null && lastNewAttr._nextSibling.isAttr() )
lastNewAttr = lastNewAttr._nextSibling;
// Get to's parnet now before I potentially move him with the next transfer
Xobj y = to.getParent();
if (cchRight > 0)
transferChars( to._xobj, to._pos, lastNewAttr, lastNewAttr.posMax(), cchRight );
if (y.hasTextNoEnsureOccupancy())
{
int p, cch;
if (y._cchValue > 0)
{
p = 1;
cch = y._cchValue;
}
else
{
y = y.lastAttr();
p = y.posAfter();
cch = y._cchAfter;
}
transferChars( y, p, lastNewAttr, lastNewAttr.posAfter(), cch );
}
}
else if (cchRight > 0)
transferChars( to._xobj, to._pos, lastToMove, lastToMove.posMax(), cchRight );
// After mucking with the text, splice the new tree in
if (append)
here.appendXobjs( firstToMove, lastToMove );
else
here.insertXobjs( firstToMove, lastToMove );
// Position "to" to be at the beginning of the newly inserted contents
to.moveTo( firstToMove );
to.prevChars( valueMovedCch );
}
// If I consed up a to, release it here
if (surragateTo != null)
surragateTo.release();
}
protected final Bookmark setBookmark ( Object key, Object value )
{
assert isNormal();
assert key != null;
return _xobj.setBookmark( _pos, key, value );
}
Object getBookmark ( Object key )
{
assert isNormal();
assert key != null;
for ( Bookmark b = _xobj._bookmarks ; b != null ; b = b._next )
if (b._pos == _pos && b._key == key)
return b._value;
return null;
}
int firstBookmarkInChars ( Object key, int cch )
{
assert isNormal();
assert key != null;
assert cch > 0;
assert cch <= cchRight();
int d = -1;
if (isText())
{
for ( Bookmark b = _xobj._bookmarks ; b != null ; b = b._next )
if (b._key == key && inChars( b, cch, false ))
d = (d == -1 || b._pos - _pos < d) ? b._pos - _pos : d;
}
return d;
}
int firstBookmarkInCharsLeft ( Object key, int cch )
{
assert isNormal();
assert key != null;
assert cch > 0;
assert cch <= cchLeft();
int d = -1;
if (cchLeft() > 0)
{
Xobj x = getDenormal();
int p = _posTemp - cch;
for ( Bookmark b = x._bookmarks ; b != null ; b = b._next )
if (b._key == key && x.inChars( p, b._xobj, b._pos, cch, false ))
d = (d == -1 || b._pos - p < d) ? b._pos - p : d;
}
return d;
}
String getCharsAsString ( int cch )
{
assert isNormal() && _xobj != null;
return getCharsAsString( cch, Locale.WS_PRESERVE );
}
String getCharsAsString ( int cch, int wsr )
{
return _xobj.getCharsAsString( _pos, cch, wsr );
}
String getValueAsString ( int wsr )
{
assert isNode();
return _xobj.getValueAsString( wsr );
}
String getValueAsString ( )
{
assert isNode();
assert ! hasChildren();
return _xobj.getValueAsString();
}
Object getChars ( int cch )
{
assert isPositioned();
return _xobj.getChars( _pos, cch, this );
}
Object getFirstChars ( )
{
assert isNode();
Object src = _xobj.getFirstChars();
_offSrc = _locale._offSrc;
_cchSrc = _locale._cchSrc;
return src;
}
void copyNode ( Cur to )
{
assert to != null;
assert isNode();
Xobj copy = _xobj.copyNode( to._locale );
// TODO - in the moveNode case, I would not have to walk the tree for cursors ... optimize
if (to.isPositioned())
Cur.moveNode( copy, to );
else
to.moveTo( copy );
}
Cur weakCur ( Object o )
{
Cur c = _locale.weakCur( o );
c.moveToCur( this );
return c;
}
Cur tempCur ( )
{
return tempCur( null );
}
Cur tempCur ( String id )
{
Cur c = _locale.tempCur( id );
c.moveToCur( this );
return c;
}
private Cur tempCur ( Xobj x, int p )
{
assert _locale == x._locale;
assert x != null || p == NO_POS;
Cur c = _locale.tempCur();
if (x != null)
c.moveTo( getNormal( x, p ), _posTemp );
return c;
}
// Is a cursor (c) in the chars defined by cch chars after where this Cur is positioned.
// Is inclusive on the left, and inclusive/exclusive on the right depending on the value
// of includeEnd.
boolean inChars ( Cur c, int cch, boolean includeEnd )
{
assert isPositioned() && isText() && cchRight() >= cch;
assert c.isNormal();
return _xobj.inChars( _pos, c._xobj, c._pos, cch, includeEnd );
}
boolean inChars ( Bookmark b, int cch, boolean includeEnd )
{
assert isPositioned() && isText() && cchRight() >= cch;
assert b._xobj.isNormal( b._pos );
return _xobj.inChars( _pos, b._xobj, b._pos, cch, includeEnd );
}
// Can't be static because I need to communicate pos in _posTemp :-(
// I wish I had multiple return vars ...
private Xobj getNormal ( Xobj x, int p )
{
Xobj nx = x.getNormal( p );
_posTemp = x._locale._posTemp;
return nx;
}
private Xobj getDenormal ( )
{
assert isPositioned();
return getDenormal( _xobj, _pos );
}
private Xobj getDenormal ( Xobj x, int p )
{
Xobj dx = x.getDenormal( p );
_posTemp = x._locale._posTemp;
return dx;
}
// May throw IllegalArgumentException if can't change the type
void setType ( SchemaType type )
{
setType( type, true );
}
void setType ( SchemaType type, boolean complain )
{
assert type != null;
assert isUserNode();
TypeStoreUser user = peekUser();
if (user != null && user.get_schema_type() == type)
return;
if (isRoot())
{
_xobj.setStableType( type );
return;
}
// Gotta get the parent user to make sure this type is ok here
TypeStoreUser parentUser = _xobj.ensureParent().getUser();
// One may only set the type of an attribute to its 'natural' type because
// attributes cannot take advantage of the xsiType attribute.
if (isAttr())
{
if (complain && parentUser.get_attribute_type( getName() ) != type)
{
throw
new IllegalArgumentException(
"Can't set type of attribute to " + type.toString() );
}
return;
}
assert isElem();
// First check to see if this type can be here sans xsi:type.
// If so, make sure there is no xsi:type
if (parentUser.get_element_type( getName(), null ) == type)
{
removeAttr( Locale._xsiType );
return;
}
// If the desired type has no name, then it cannot be
// referenced via xsi:type
QName typeName = type.getName();
if (typeName == null)
{
if (complain)
throw new IllegalArgumentException( "Can't set type of element, type is un-named" );
else
return;
}
// See if setting xsiType would result in the target type
if (parentUser.get_element_type( getName(), typeName ) != type)
{
if (complain)
throw new IllegalArgumentException( "Can't set type of element, invalid type" );
else
return;
}
setAttrValueAsQName( Locale._xsiType, typeName );
}
void setSubstitution ( QName name, SchemaType type )
{
setSubstitution( name, type, true );
}
void setSubstitution ( QName name, SchemaType type, boolean complain )
{
assert name != null;
assert type != null;
assert isUserNode();
TypeStoreUser user = peekUser();
if (user != null && user.get_schema_type() == type && name.equals(getName()))
return;
if (isRoot())
{
// If this is the root node, we can't set its name, so the whole
// operation is aborted
return;
}
// Gotta get the parent user to make sure this type is ok here
TypeStoreUser parentUser = _xobj.ensureParent().getUser();
// One may only set the type of an attribute to its 'natural' type because
// attributes cannot take advantage of the xsiType attribute.
if (isAttr())
{
if (complain)
{
throw
new IllegalArgumentException(
"Can't use substitution with attributes");
}
return;
}
assert isElem();
// First check to see if this type can be here sans xsi:type.
// If so, make sure there is no xsi:type
if (parentUser.get_element_type( name, null ) == type)
{
setName( name );
removeAttr( Locale._xsiType );
return;
}
// If the desired type has no name, then it cannot be
// referenced via xsi:type
QName typeName = type.getName();
if (typeName == null)
{
if (complain)
throw new IllegalArgumentException( "Can't set xsi:type on element, type is un-named" );
else
return;
}
// See if setting xsiType would result in the target type
if (parentUser.get_element_type( name, typeName ) != type)
{
if (complain)
throw new IllegalArgumentException( "Can't set xsi:type on element, invalid type" );
else
return;
}
setName( name );
setAttrValueAsQName( Locale._xsiType, typeName );
}
TypeStoreUser peekUser ( )
{
assert isUserNode();
return _xobj._user;
}
XmlObject getObject ( )
{
return isUserNode() ? (XmlObject) getUser() : null;
}
TypeStoreUser getUser ( )
{
assert isUserNode();
return _xobj.getUser();
}
Dom getDom ( )
{
assert isNormal();
assert isPositioned();
if (isText())
{
int cch = cchLeft();
for ( CharNode cn = getCharNodes() ; ; cn = cn._next )
if ((cch -= cn._cch) < 0)
return cn;
}
return _xobj.getDom();
}
static void release ( Cur c )
{
if (c != null)
c.release();
}
void release ( )
{
if (_tempFrame >= 0)
{
if (_nextTemp != null)
_nextTemp._prevTemp = _prevTemp;
if (_prevTemp == null)
_locale._tempFrames[ _tempFrame ] = _nextTemp;
else
_prevTemp._nextTemp = _nextTemp;
_prevTemp = _nextTemp = null;
_tempFrame = -1;
}
if (_state != POOLED && _state != DISPOSED)
{
// Clean up any state
while ( _stackTop != -1 )
popButStay();
clearSelection();
_id = null;
// Unposition
moveToCur( null );
assert isNormal();
assert _xobj == null;
assert _pos == NO_POS;
// Release weak reference and attacked value
if (_ref != null)
{
_ref.clear();
_ref._cur = null;
}
_ref = null;
// Unregister and either diapose of cursor or add it back to pool
assert _state == REGISTERED;
_locale._registered = listRemove( _locale._registered );
if (_locale._curPoolCount < 16)
{
_locale._curPool = listInsert( _locale._curPool );
_state = POOLED;
_locale._curPoolCount++;
}
else
{
_locale = null;
_state = DISPOSED;
}
}
}
boolean isOnList ( Cur head )
{
for ( ; head != null ; head = head._next )
if (head == this)
return true;
return false;
}
Cur listInsert ( Cur head )
{
assert _next == null && _prev == null;
if (head == null)
head = _prev = this;
else
{
_prev = head._prev;
head._prev = head._prev._next = this;
}
return head;
}
Cur listRemove ( Cur head )
{
assert _prev != null && isOnList( head );
if (_prev == this)
head = null;
else
{
if (head == this)
head = _next;
else
_prev._next = _next;
if (_next == null)
head._prev = _prev;
else
{
_next._prev = _prev;
_next = null;
}
}
_prev = null;
assert _next == null;
return head;
}
// boolean isNormal ( Cur that )
// {
// return isNormal() && (that == null || (_locale == that._locale && that.isNormal()));
// }
boolean isNormal ( )
{
if (_state == POOLED || _state == DISPOSED)
return false;
if (_xobj == null)
return _pos == NO_POS;
if (!_xobj.isNormal( _pos ))
return false;
if (_state == EMBEDDED)
return isOnList( _xobj._embedded );
assert _state == REGISTERED;
return isOnList( _locale._registered );
}
static final String LOAD_USE_LOCALE_CHAR_UTIL = "LOAD_USE_LOCALE_CHAR_UTIL";
static final class CurLoadContext extends LoadContext
{
CurLoadContext ( Locale l, XmlOptions options )
{
options = XmlOptions.maskNull( options );
_locale = l;
_charUtil =
options.hasOption( LOAD_USE_LOCALE_CHAR_UTIL )
? _locale.getCharUtil()
: CharUtil.getThreadLocalCharUtil();
_frontier = createDomDocumentRootXobj( _locale );
_after = false;
_lastXobj = _frontier;
_lastPos = 0;
if (options.hasOption( XmlOptions.LOAD_REPLACE_DOCUMENT_ELEMENT ))
{
_replaceDocElem = (QName) options.get( XmlOptions.LOAD_REPLACE_DOCUMENT_ELEMENT );
_discardDocElem = true;
}
_stripWhitespace = options.hasOption( XmlOptions.LOAD_STRIP_WHITESPACE );
_stripComments = options.hasOption( XmlOptions.LOAD_STRIP_COMMENTS );
_stripProcinsts = options.hasOption( XmlOptions.LOAD_STRIP_PROCINSTS );
_substituteNamespaces = (Map) options.get( XmlOptions.LOAD_SUBSTITUTE_NAMESPACES );
_additionalNamespaces = (Map) options.get( XmlOptions.LOAD_ADDITIONAL_NAMESPACES );
_locale._versionAll++;
_locale._versionSansText++;
}
//
// Really primitive load context operations
//
private void start ( Xobj xo )
{
assert _frontier != null;
assert !_after || _frontier._parent != null;
flushText();
if (_after)
{
_frontier = _frontier._parent;
_after = false;
}
_frontier.appendXobj( xo );
_frontier = xo;
_lastXobj = xo;
_lastPos = 0;
}
private void end ( )
{
assert _frontier != null;
assert !_after || _frontier._parent != null;
flushText();
if (_after)
_frontier = _frontier._parent;
else
_after = true;
_lastXobj = _frontier;
_lastPos = END_POS;
}
private void text ( Object src, int off, int cch )
{
if (cch <= 0)
return;
_lastXobj = _frontier;
_lastPos = _frontier._cchValue + 1;
if (_after)
{
_lastPos += _frontier._cchAfter + 1;
_frontier._srcAfter =
_charUtil.saveChars(
src, off, cch,
_frontier._srcAfter, _frontier._offAfter, _frontier._cchAfter );
_frontier._offAfter = _charUtil._offSrc;
_frontier._cchAfter = _charUtil._cchSrc;
}
else
{
_frontier._srcValue =
_charUtil.saveChars(
src, off, cch,
_frontier._srcValue, _frontier._offValue, _frontier._cchValue );
_frontier._offValue = _charUtil._offSrc;
_frontier._cchValue = _charUtil._cchSrc;
}
}
private void flushText ( )
{
if (_stripWhitespace)
{
if (_after)
{
_frontier._srcAfter =
_charUtil.stripRight(
_frontier._srcAfter, _frontier._offAfter, _frontier._cchAfter );
_frontier._offAfter = _charUtil._offSrc;
_frontier._cchAfter = _charUtil._cchSrc;
}
else
{
_frontier._srcValue =
_charUtil.stripRight(
_frontier._srcValue, _frontier._offValue, _frontier._cchValue );
_frontier._offValue = _charUtil._offSrc;
_frontier._cchValue = _charUtil._cchSrc;
}
}
}
private Xobj parent ( )
{
return _after ? _frontier._parent : _frontier;
}
private QName checkName ( QName name, boolean local )
{
if (_substituteNamespaces != null && (!local || name.getNamespaceURI().length() > 0))
{
String substituteUri = (String) _substituteNamespaces.get( name.getNamespaceURI() );
if (substituteUri != null)
name = _locale.makeQName( substituteUri, name.getLocalPart(), name.getPrefix());
}
return name;
}
//
//
//
protected void startDTD (String name, String publicId, String systemId )
{
_doctypeName = name;
_doctypePublicId = publicId;
_doctypeSystemId = systemId;
}
protected void endDTD ( )
{
}
protected void startElement ( QName name )
{
start( createElementXobj( _locale, checkName( name, false ), parent()._name ) );
_stripLeft = true;
}
protected void endElement ( )
{
assert parent().isElem();
end();
_stripLeft = true;
}
protected void xmlns ( String prefix, String uri )
{
assert parent().isContainer();
// BUGBUG - should assert there that there is no text before this attr
// Namespace attrs are different than regular attrs -- I don't change their name,
// I change their value!
if (_substituteNamespaces != null)
{
String substituteUri = (String) _substituteNamespaces.get( uri );
if (substituteUri != null)
uri = substituteUri;
}
Xobj x = new Xobj.AttrXobj( _locale, _locale.createXmlns( prefix ) );
start( x );
text( uri, 0, uri.length() );
end();
_lastXobj = x;
_lastPos = 0;
}
protected void attr ( QName name, String value )
{
assert parent().isContainer();
// BUGBUG - should assert there that there is no text before this attr
QName parentName = _after?
_lastXobj._parent.getQName(): _lastXobj.getQName();
boolean isId = isAttrOfTypeId(name, parentName);
Xobj x = isId ?
new Xobj.AttrIdXobj(_locale, checkName(name, true)) :
new Xobj.AttrXobj(_locale, checkName(name, true));
start(x);
text(value, 0, value.length());
end();
if (isId)
{
Cur c1 = x.tempCur();
c1.toRoot();
Xobj doc = c1._xobj;
c1.release();
if (doc instanceof Xobj.DocumentXobj)
((Xobj.DocumentXobj) doc).addIdElement(value,
x._parent.getDom());
}
_lastXobj = x;
_lastPos = 0;
}
protected void attr ( String local, String uri, String prefix, String value )
{
attr( _locale.makeQName( uri, local, prefix ), value );
}
protected void procInst ( String target, String value )
{
if (!_stripProcinsts)
{
Xobj x = new Xobj.ProcInstXobj( _locale, target );
start( x );
text( value, 0, value.length() );
end();
_lastXobj = x;
_lastPos = 0;
}
_stripLeft = true;
}
protected void comment ( String comment )
{
if (!_stripComments)
comment( comment, 0, comment.length() );
_stripLeft = true;
}
protected void comment ( char[] chars, int off, int cch )
{
if (!_stripComments)
{
comment(
_charUtil.saveChars( chars, off, cch ),
_charUtil._offSrc, _charUtil._cchSrc );
}
_stripLeft = true;
}
private void comment ( Object src, int off, int cch )
{
Xobj x = new Xobj.CommentXobj( _locale );
start( x );
text( src, off, cch );
end();
_lastXobj = x;
_lastPos = 0;
}
private boolean _stripLeft = true;
private void stripText ( Object src, int off, int cch )
{
if (_stripWhitespace)
{
// this is to avoid bug in cases like <company>Procter &amp; Gamble</company>
if (_stripLeft)
{
src = _charUtil.stripLeft( src, off, cch );
_stripLeft = false;
off = _charUtil._offSrc;
cch = _charUtil._cchSrc;
}
}
text( src, off, cch );
}
protected void text ( String s )
{
if (s == null)
return;
stripText( s, 0, s.length() );
}
protected void text ( char[] src, int off, int cch )
{
stripText( src, off, cch );
}
protected void bookmark ( XmlBookmark bm )
{
_lastXobj.setBookmark( _lastPos, bm.getKey(), bm );
}
protected void bookmarkLastNonAttr ( XmlBookmark bm )
{
if (_lastPos > 0 || !_lastXobj.isAttr())
_lastXobj.setBookmark( _lastPos, bm.getKey(), bm );
else
{
assert _lastXobj._parent != null;
_lastXobj._parent.setBookmark( 0, bm.getKey(), bm );
}
}
protected void bookmarkLastAttr ( QName attrName, XmlBookmark bm )
{
if (_lastPos == 0 && _lastXobj.isAttr())
{
assert _lastXobj._parent != null;
Xobj a = _lastXobj._parent.getAttr( attrName );
if (a != null)
a.setBookmark( 0, bm.getKey(), bm );
}
}
protected void lineNumber ( int line, int column, int offset )
{
_lastXobj.setBookmark(
_lastPos,
XmlLineNumber.class,
new XmlLineNumber( line, column, offset ) );
}
protected void abort ( )
{
_stripLeft = true;
while ( !parent().isRoot() )
end();
finish().release();
}
protected Cur finish ( )
{
flushText();
if (_after)
_frontier = _frontier._parent;
assert _frontier != null && _frontier._parent == null && _frontier.isRoot();
Cur c = _frontier.tempCur();
if (!Locale.toFirstChildElement( c ))
return c;
// See if the document element is a fragment
boolean isFrag = Locale.isFragmentQName( c.getName() );
if (_discardDocElem || isFrag)
{
if (_replaceDocElem != null)
c.setName( _replaceDocElem );
else
{
// Remove the content around the element to remove so that that content
// does not appear to have been the contents of the removed element.
while ( c.toParent() )
;
c.next();
while ( !c.isElem() )
if (c.isText()) c.moveChars( null, -1 ); else c.moveNode( null );
assert c.isElem();
c.skip();
while ( !c.isFinish() )
if (c.isText()) c.moveChars( null, -1 ); else c.moveNode( null );
c.toParent();
c.next();
assert c.isElem();
Cur c2 = c.tempCur();
c.moveNodeContents( c, true );
c.moveToCur( c2 );
c2.release();
c.moveNode( null );
}
// Remove the fragment namespace decl
if (isFrag)
{
c.moveTo( _frontier );
if (c.toFirstAttr())
{
for ( ; ; )
{
if (c.isXmlns() && c.getXmlnsUri().equals( Locale._openFragUri ))
{
c.moveNode( null );
if (!c.isAttr())
break;
}
else if (!c.toNextAttr())
break;
}
}
c.moveTo(_frontier);
_frontier = createDomDocumentRootXobj( _locale, true );
Cur c2 = _frontier.tempCur();
c2.next();
c.moveNodeContents(c2, true);
c.moveTo(_frontier);
c2.release();
}
}
if (_additionalNamespaces != null)
{
c.moveTo( _frontier );
Locale.toFirstChildElement( c );
Locale.applyNamespaces( c, _additionalNamespaces );
}
if (_doctypeName != null && (_doctypePublicId != null || _doctypeSystemId != null))
{
XmlDocumentProperties props = Locale.getDocProps(c, true);
props.setDoctypeName(_doctypeName);
if (_doctypePublicId != null)
props.setDoctypePublicId(_doctypePublicId);
if (_doctypeSystemId != null)
props.setDoctypeSystemId(_doctypeSystemId);
}
c.moveTo( _frontier );
assert c.isRoot();
return c;
}
public void dump ( )
{
_frontier.dump();
}
private Locale _locale;
private CharUtil _charUtil;
private Xobj _frontier;
private boolean _after;
private Xobj _lastXobj;
private int _lastPos;
private boolean _discardDocElem;
private QName _replaceDocElem;
private boolean _stripWhitespace;
private boolean _stripComments;
private boolean _stripProcinsts;
private Map _substituteNamespaces;
private Map _additionalNamespaces;
private String _doctypeName;
private String _doctypePublicId;
private String _doctypeSystemId;
}
//
//
//
static String kindName ( int kind )
{
switch ( kind )
{
case ROOT : return "ROOT";
case ELEM : return "ELEM";
case ATTR : return "ATTR";
case COMMENT : return "COMMENT";
case PROCINST : return "PROCINST";
case TEXT : return "TEXT";
default : return "<< Unknown Kind (" + kind + ") >>";
}
}
static void dump ( PrintStream o, Dom d, Object ref )
{
}
static void dump ( PrintStream o, Dom d )
{
d.dump( o );
}
static void dump ( Dom d )
{
dump( System.out, d );
}
static void dump ( Node n )
{
dump( System.out, n );
}
static void dump ( PrintStream o, Node n )
{
dump( o, (Dom) n );
}
void dump ( )
{
dump( System.out, _xobj, this );
}
void dump ( PrintStream o )
{
if (_xobj == null)
{
o.println( "Unpositioned xptr" );
return;
}
dump( o, _xobj, this );
}
public static void dump ( PrintStream o, Xobj xo, Object ref )
{
if (ref == null)
ref = xo;
while ( xo._parent != null )
xo = xo._parent;
dumpXobj( o, xo, 0, ref );
o.println();
}
private static void dumpCur ( PrintStream o, String prefix, Cur c, Object ref )
{
o.print( " " );
if (ref == c)
o.print( "*:" );
o.print( prefix + (c._id == null ? "<cur>" : c._id) + "[" + c._pos + "]" );
}
private static void dumpCurs ( PrintStream o, Xobj xo, Object ref )
{
for ( Cur c = xo._embedded ; c != null ; c = c._next )
dumpCur( o, "E:", c, ref );
for ( Cur c = xo._locale._registered ; c != null ; c = c._next )
{
if (c._xobj == xo)
dumpCur( o, "R:", c, ref );
}
}
private static void dumpBookmarks ( PrintStream o, Xobj xo, Object ref )
{
for ( Bookmark b = xo._bookmarks ; b != null ; b = b._next )
{
o.print( " " );
if (ref == b)
o.print( "*:" );
if (b._value instanceof XmlLineNumber)
{
XmlLineNumber l = (XmlLineNumber) b._value;
o.print( "<line:" + l.getLine() + ">" + "[" + b._pos + "]" );
}
else
o.print( "<mark>" + "[" + b._pos + "]" );
}
}
private static void dumpCharNodes ( PrintStream o, CharNode nodes, Object ref )
{
for ( CharNode n = nodes ; n != null ; n = n._next )
{
o.print( " " );
if (n == ref)
o.print( "*" );
o.print( (n instanceof TextNode ? "TEXT" : "CDATA") + "[" + n._cch + "]" );
}
}
private static void dumpChars ( PrintStream o, Object src, int off, int cch )
{
// CharUtil.dumpChars( o, src, off, cch );
o.print( "\"" );
String s = CharUtil.getString( src, off, cch );
for ( int i = 0 ; i < s.length(); )
{
if (i == 36)
{
o.print( "..." );
break;
}
int codePoint = s.codePointAt( i );
char[] chars = Character.toChars(codePoint);
if ( chars.length == 1 ) {
char ch = chars[0];
if (ch >= 32 && ch < 127)
o.print( ch );
else if (ch == '\n')
o.print( "\\n" );
else if (ch == '\r')
o.print( "\\r" );
else if (ch == '\t')
o.print( "\\t" );
else if (ch == '\"')
o.print( "\\\"" );
else
o.print( "<#" + ((int) ch) + ">" );
} else {
o.print( "<#" + codePoint + ">" );
}
i += Character.charCount(codePoint);
}
o.print( "\"" );
}
private static void dumpXobj ( PrintStream o, Xobj xo, int level, Object ref )
{
if (xo == null)
return;
if (xo == ref)
o.print( "* " );
else
o.print( " " );
for ( int i = 0 ; i < level ; i++ )
o.print( " " );
o.print( kindName( xo.kind() ) );
if (xo._name != null)
{
o.print( " " );
if (xo._name.getPrefix().length() > 0)
o.print( xo._name.getPrefix() + ":" );
o.print( xo._name.getLocalPart() );
if (xo._name.getNamespaceURI().length() > 0)
o.print( "@" + xo._name.getNamespaceURI() );
}
if (xo._srcValue != null || xo._charNodesValue != null)
{
o.print( " Value( " );
dumpChars( o, xo._srcValue, xo._offValue, xo._cchValue );
dumpCharNodes( o, xo._charNodesValue, ref );
o.print( " )" );
}
if (xo._user != null)
o.print( " (USER)" );
if (xo.isVacant())
o.print( " (VACANT)" );
if (xo._srcAfter != null || xo._charNodesAfter != null)
{
o.print( " After( " );
dumpChars( o, xo._srcAfter, xo._offAfter, xo._cchAfter );
dumpCharNodes( o, xo._charNodesAfter, ref );
o.print( " )" );
}
dumpCurs( o, xo, ref );
dumpBookmarks( o, xo, ref );
String className = xo.getClass().getName();
int i = className.lastIndexOf( '.' );
if (i > 0)
{
className = className.substring( i + 1 );
i = className.lastIndexOf( '$' );
if (i > 0)
className = className.substring( i + 1 );
}
o.print( " (" );
o.print( className );
o.print( ")" );
o.println();
for ( xo = xo._firstChild ; xo != null ; xo = xo._nextSibling )
dumpXobj( o, xo, level + 1, ref );
}
void setId ( String id )
{
_id = id;
}
//
//
//
Locale _locale;
Xobj _xobj;
int _pos;
int _state;
String _id;
Cur _nextTemp;
Cur _prevTemp;
int _tempFrame;
Cur _next;
Cur _prev;
Locale.Ref _ref;
int _stackTop;
int _selectionFirst;
int _selectionN;
int _selectionLoc;
int _selectionCount;
private int _posTemp;
int _offSrc;
int _cchSrc;
}