| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2003 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Apache" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache |
| * XMLBeans", nor may "Apache" appear in their name, without prior |
| * written permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 2000-2003 BEA Systems |
| * Inc., <http://www.bea.com/>. For more information on the Apache Software |
| * Foundation, please see <http://www.apache.org/>. |
| */ |
| |
| package org.apache.xmlbeans.impl.store; |
| |
| import org.apache.xmlbeans.impl.common.XMLChar; |
| import org.apache.xmlbeans.impl.common.GlobalLock; |
| import org.apache.xmlbeans.impl.store.Root.ChangeListener; |
| import org.apache.xmlbeans.impl.store.Saver.XmlInputStreamImpl; |
| import org.apache.xmlbeans.impl.store.Splay.Annotation; |
| import org.apache.xmlbeans.impl.store.Splay.Attr; |
| import org.apache.xmlbeans.impl.store.Splay.Begin; |
| import org.apache.xmlbeans.impl.store.Splay.Comment; |
| import org.apache.xmlbeans.impl.store.Splay.CursorGoober; |
| import org.apache.xmlbeans.impl.store.Splay.Goober; |
| import org.apache.xmlbeans.impl.store.Splay.Procinst; |
| import org.apache.xmlbeans.impl.store.Splay.Xmlns; |
| import org.apache.xmlbeans.XmlCursor.ChangeStamp; |
| import org.apache.xmlbeans.XmlCursor; |
| import org.apache.xmlbeans.XmlObject; |
| import org.apache.xmlbeans.XmlOptions; |
| import org.apache.xmlbeans.XmlDocumentProperties; |
| import org.apache.xmlbeans.XmlRuntimeException; |
| |
| import java.io.FileOutputStream; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| import java.io.File; |
| import java.io.Writer; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Map; |
| import javax.xml.namespace.QName; |
| import org.w3c.dom.Node; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.ext.LexicalHandler; |
| import org.xml.sax.SAXException; |
| import weblogic.xml.stream.XMLInputStream; |
| |
| public final class Cursor implements XmlCursor, ChangeListener |
| { |
| Cursor ( Root r, Splay s ) { assert s != null; _data = CursorData.getOne( r ); set( s ); } |
| Cursor ( Root r, Splay s, int p ) { assert s != null; _data = CursorData.getOne( r ); set( s, p ); } |
| |
| // |
| // |
| // |
| |
| public Object monitor() |
| { |
| return getRoot(); |
| } |
| |
| Root getRoot ( ) { return _data._goober.getRoot(); } |
| |
| Splay getSplay ( ) { return _data._goober.getSplay(); } |
| int getPos ( ) { return _data._goober.getPos(); } |
| |
| void set ( Splay s, int p ) { _data._goober.set( s, p ); } |
| void set ( Splay s ) { _data._goober.set( s, 0 ); } |
| void set ( int p ) { _data._goober.set( p ); } |
| void set ( Goober g ) { _data._goober.set( g ); } |
| |
| int getPostCch ( ) |
| { |
| int p = getPos(); |
| |
| if (p == 0) |
| return 0; |
| |
| Splay s = getSplay(); |
| |
| int pa = s.getPosAfter(); |
| |
| assert p >= pa || s.isLeaf(); |
| |
| return p >= pa ? s.getCchAfter() - p + pa : s.getPosLeafEnd() - p; |
| } |
| |
| int getPreCch ( ) |
| { |
| // TODO - quick and dirty impl, improve |
| |
| Splay sOrig = getSplay(); |
| int pOrig = getPos(); |
| |
| int n = toPrevChar( -1 ); |
| |
| set( sOrig, pOrig ); |
| |
| return n; |
| } |
| |
| private void checkDisposed ( ) |
| { |
| checkDisposed( this ); |
| } |
| |
| private static void checkDisposed ( Cursor c ) |
| { |
| if (c.isDisposed()) |
| throw new IllegalStateException( "Cursor has been disposed" ); |
| } |
| |
| boolean isDisposed ( ) |
| { |
| return _data == null; |
| } |
| |
| // |
| // XmlCursor Methods |
| // |
| |
| public void dispose ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| if (!isDisposed()) |
| { |
| _data.release(); |
| _data = null; |
| } |
| } |
| } |
| |
| public XmlObject getObject ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Root r = getRoot(); |
| |
| if (getPos() > 0) |
| return null; |
| |
| Splay s = getSplay(); |
| |
| if (!s.isTypeable()) |
| return null; |
| |
| Type t = s.getType( r ); |
| |
| assert t != null; |
| |
| XmlObject result = t.getXmlObject(); |
| assert result != null; |
| return result; |
| } |
| } |
| |
| public boolean toCursor ( XmlCursor moveTo ) |
| { |
| if (moveTo == null) |
| throw new IllegalArgumentException( "Invalid destination cursor" ); |
| |
| if (monitor() == moveTo.monitor()) |
| { |
| synchronized ( monitor() ) |
| { |
| return toCursorImpl( moveTo ); |
| } |
| } |
| else |
| { |
| boolean acquired = false; |
| try |
| { |
| GlobalLock.acquire(); |
| acquired = true; |
| synchronized ( monitor() ) |
| { |
| synchronized (moveTo.monitor()) |
| { |
| GlobalLock.release(); |
| acquired = false; |
| |
| return toCursorImpl( moveTo ); |
| } |
| } |
| } |
| catch (InterruptedException e) |
| { |
| throw new XmlRuntimeException(e.getMessage(), e); |
| } |
| finally |
| { |
| if (acquired) |
| GlobalLock.release(); |
| } |
| } |
| } |
| |
| private boolean toCursorImpl ( XmlCursor moveTo ) |
| { |
| checkDisposed(); |
| |
| Cursor c = null; |
| |
| if (moveTo instanceof Cursor) |
| { |
| c = (Cursor) moveTo; |
| |
| checkDisposed( c ); |
| |
| if (c.getRoot() != getRoot()) |
| c = null; |
| } |
| |
| if (c == null) |
| return false; |
| |
| set( c._data._goober ); |
| |
| return true; |
| } |
| |
| public XmlDocumentProperties documentProperties ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| return getRoot().documentProperties(); |
| } |
| } |
| |
| public XmlCursor newCursor ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| return new Cursor( getRoot(), getSplay(), getPos() ); |
| } |
| } |
| |
| public boolean toBookmark ( XmlBookmark bm ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (bm == null) |
| return false; |
| |
| if (!(bm._currentMark instanceof Annotation)) |
| return false; |
| |
| Annotation a = (Annotation) bm._currentMark; |
| |
| if (a.getRoot() != getRoot()) |
| return false; |
| |
| assert a.getSplay() != null; |
| |
| set( a ); |
| |
| return true; |
| } |
| } |
| |
| public XmlBookmark toNextBookmark ( Object key ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (key == null) |
| return null; |
| |
| Splay sOrig = getSplay(); |
| int pOrig = getPos(); |
| |
| TokenType tt = currentTokenType(); |
| |
| // Advance the cursor past the current spot by the minimun amount |
| |
| if (tt.isText()) |
| { |
| toNextChar( 1 ); |
| tt = currentTokenType(); |
| } |
| else if ((tt = toNextToken()).isNone()) |
| { |
| set( sOrig, pOrig ); |
| return null; |
| } |
| |
| for ( ; ; ) |
| { |
| XmlBookmark bm = getBookmark( key ); |
| |
| if (bm != null) |
| return bm; |
| |
| int postCch; |
| |
| if (tt.isText() && (postCch = getPostCch()) > 1) |
| { |
| Splay s = getSplay(); |
| int p = getPos(); |
| int d = postCch; |
| |
| for ( Goober g = s.firstGoober() ; g != null ; |
| g = s.nextGoober( g ) ) |
| { |
| int dist; |
| XmlBookmark mark; |
| |
| if (g.isAnnotation() && (dist = g.getPos() - p) > 1 && |
| dist < d && (mark = g.getBookmark()) != null && |
| mark.getKey().equals( key )) |
| { |
| bm = mark; |
| d = dist; |
| } |
| } |
| |
| if (bm != null) |
| { |
| set( s, p + d ); |
| return bm; |
| } |
| } |
| |
| if ((tt = toNextToken()).isNone()) |
| { |
| set( sOrig, pOrig ); |
| return null; |
| } |
| } |
| } |
| } |
| |
| public XmlBookmark toPrevBookmark ( Object key ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (key == null) |
| return null; |
| |
| Splay sOrig = getSplay(); |
| int pOrig = getPos(); |
| |
| TokenType tt = prevTokenType(); |
| |
| // Retreat the cursor past the current spot by the minimun amount |
| |
| if (tt.isText()) |
| { |
| toPrevChar( 1 ); |
| tt = prevTokenType(); |
| } |
| else if (toPrevToken().isNone()) |
| { |
| set( sOrig, pOrig ); |
| return null; |
| } |
| else |
| tt = prevTokenType(); |
| |
| for ( ; ; ) |
| { |
| XmlBookmark bm = getBookmark( key ); |
| |
| if (bm != null) |
| return bm; |
| |
| int preCch; |
| |
| if (tt.isText() && (preCch = getPreCch()) > 1) |
| { |
| Splay s; |
| int p; |
| |
| if (getPos() == 0) |
| { |
| s = getSplay().prevNonAttrSplay(); |
| p = s.getEndPos(); |
| } |
| else |
| { |
| s = getSplay(); |
| p = getPos(); |
| } |
| |
| int d = preCch; |
| |
| for ( Goober g = s.firstGoober() ; g != null ; |
| g = s.nextGoober( g ) ) |
| { |
| int dist; |
| XmlBookmark mark; |
| |
| if (g.isAnnotation() && (dist = p - g.getPos()) > 1 && |
| dist < d && (mark = g.getBookmark()) != null && |
| mark.getKey().equals( key )) |
| { |
| bm = mark; |
| d = dist; |
| } |
| } |
| |
| if (bm != null) |
| { |
| set( s, p - d ); |
| return bm; |
| } |
| } |
| |
| if (tt.isText()) |
| { |
| toPrevChar( -1 ); |
| tt = prevTokenType(); |
| } |
| else if (toPrevToken().isNone()) |
| { |
| set( sOrig, pOrig ); |
| return null; |
| } |
| else |
| tt = prevTokenType(); |
| } |
| } |
| } |
| |
| public TokenType currentTokenType ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| return getSplay().getTokenType( getPos() ); |
| } |
| } |
| |
| public boolean isStartdoc ( ) { return currentTokenType().isStartdoc(); } |
| public boolean isEnddoc ( ) { return currentTokenType().isEnddoc(); } |
| public boolean isStart ( ) { return currentTokenType().isStart(); } |
| public boolean isEnd ( ) { return currentTokenType().isEnd(); } |
| public boolean isText ( ) { return currentTokenType().isText(); } |
| public boolean isAttr ( ) { return currentTokenType().isAttr(); } |
| public boolean isNamespace ( ) { return currentTokenType().isNamespace(); } |
| public boolean isComment ( ) { return currentTokenType().isComment(); } |
| public boolean isProcinst ( ) { return currentTokenType().isProcinst(); } |
| public boolean isContainer ( ) { return currentTokenType().isContainer(); } |
| public boolean isFinish ( ) { return currentTokenType().isFinish(); } |
| public boolean isAnyAttr ( ) { return currentTokenType().isAnyAttr(); } |
| |
| public TokenType prevTokenType ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| // TODO - quick and dirty implementation, improve |
| |
| Splay sOrig = getSplay(); |
| int pOrig = getPos(); |
| |
| TokenType tt; |
| |
| if (toPrevChar( 1 ) == 1) |
| tt = TokenType.TEXT; |
| else if (!(tt = toPrevToken()).isNone()) |
| tt = currentTokenType(); |
| |
| set( sOrig, pOrig ); |
| |
| return tt; |
| } |
| } |
| |
| public TokenType toNextToken ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay os = getSplay(); // Orignal splay |
| Splay s = os; |
| int p = getPos(); |
| |
| if (p == 0) |
| { |
| if (s.isRoot()) |
| return TokenType.NONE; |
| |
| // |
| // Look see if there is an attr we should visit before visiting |
| // any following content in this container. |
| // |
| |
| if (s.isContainer()) |
| { |
| Splay t = s.nextSplay(); |
| |
| if (t.isAttr()) |
| { |
| set( t, 0 ); |
| return currentTokenType(); |
| } |
| |
| // |
| // Now we're going into the content of this container. Flush |
| // out any cached type value. |
| // |
| |
| s.ensureContentValid(); |
| } |
| |
| if (s.getMaxPos() > 0) |
| p = 1; |
| else |
| { |
| s = s.nextSplay(); |
| p = 0; |
| } |
| } |
| else |
| { |
| assert p > 0; |
| assert !s.isRoot(); |
| |
| if (p >= s.getPosAfter() && s.getCchAfter() > 0) |
| { |
| s = s.nextSplay(); |
| p = 0; |
| } |
| else |
| { |
| assert s.isLeaf(); |
| assert p < s.getPosAfter(); |
| |
| if (p != s.getPosLeafEnd()) |
| p = s.getPosLeafEnd(); |
| else if (s.getCchAfter() > 0) |
| p = s.getPosAfter(); |
| else |
| { |
| s = s.nextSplay(); |
| p = 0; |
| } |
| } |
| } |
| |
| // |
| // If we are transitioning from an attr to a non attr, see if there |
| // is content in a DOC or BEGIN which needs to be visited after |
| // the attributes. |
| // |
| // Also, if we are transitioning from an attr container (BEGIN or |
| // DOC) to an attr, where the attr container has interior content, |
| // we have already visited the attrs and must skip them now. |
| // |
| // Also, if we are transitioning from pos 0 to pos 1 on an attr |
| // container, we need to visit any attributes before visiting the |
| // interior content of the attr container. |
| // |
| |
| if (p == 0) |
| { |
| if (!s.isAttr() && os.isAttr()) |
| { |
| Splay t = os.prevNonAttrSplay(); |
| |
| assert t.isContainer(); |
| |
| // |
| // We're navigating to the content of a container. Flush |
| // out any cached type value. |
| // |
| |
| t.ensureContentValid(); |
| |
| if (t.getMaxPos() > 0) |
| { |
| s = t; |
| p = 1; |
| } |
| } |
| else if (s.isAttr() && !os.isAttr() && os.getMaxPos() > 0) |
| { |
| assert os.isContainer(); |
| |
| s = s.nextNonAttrSplay(); |
| } |
| } |
| |
| set( s, p ); |
| |
| return currentTokenType(); |
| } |
| } |
| |
| public TokenType toPrevToken ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| // TODO - This code is not as compact as it can be, there is some redundancy |
| // -- rethink it later ... |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| if (p == 1 && s.isInvalid()) |
| { |
| assert s.isLeaf(); |
| p += s.ensureContentValid(); |
| } |
| |
| if (p == 1 && s.isContainer()) |
| { |
| Splay t = s.nextSplay(); |
| |
| if (t.isAttr()) |
| { |
| s = t; |
| |
| for ( t = t.nextSplay() ; t.isAttr() ; t = t.nextSplay() ) |
| s = t; |
| |
| set( s, 0 ); |
| |
| return currentTokenType(); |
| } |
| } |
| |
| if (p == 0 && !s.isAttr()) |
| { |
| if (s.isDoc()) |
| return TokenType.NONE; |
| |
| Splay t = s.prevSplay(); |
| |
| if (t.isAttr()) |
| { |
| t = t.prevNonAttrSplay(); |
| |
| assert t.isContainer(); |
| |
| if (t.isDoc()) |
| t.ensureContentValid(); |
| |
| if (t.getMaxPos() > 0) |
| { |
| set( |
| t, |
| t.getCchAfter() > 0 ? t.getPosAfter() : t.getMaxPos() ); |
| |
| return currentTokenType(); |
| } |
| } |
| } |
| |
| if (s.isAttr()) |
| { |
| assert p == 0; |
| |
| Splay t = s.prevSplay(); |
| |
| if (!t.isAttr()) |
| { |
| assert t.isContainer(); |
| |
| set( t, 0 ); |
| return currentTokenType(); |
| } |
| } |
| |
| if (p == 0) |
| { |
| if (s.isDoc()) |
| return TokenType.NONE; |
| |
| s = s.prevSplay(); |
| |
| if (s.isDoc()) |
| s.ensureContentValid(); |
| |
| p = s.getCchAfter() > 0 ? s.getPosAfter() : s.getMaxPos(); |
| } |
| else |
| { |
| assert p > 0; |
| assert !s.isRoot(); |
| |
| int posAfter = s.getPosAfter(); |
| |
| if (p >= posAfter) |
| { |
| assert s.getCchAfter() > 0; |
| p = posAfter - 1; |
| } |
| else |
| { |
| assert s.isValid(); |
| assert s.isLeaf(); |
| |
| p = p > 1 && p == posAfter - 1 ? 1 : 0; |
| } |
| } |
| |
| set( s, p ); |
| |
| return currentTokenType(); |
| } |
| } |
| |
| public void insertChars ( String text ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| if (p == 0) |
| { |
| if (s.isDoc() || s.isAttr()) |
| throw new IllegalStateException( "Invalid location for text" ); |
| |
| s = s.prevNonAttrSplay(); |
| p = s.getEndPos(); |
| } |
| |
| if (text == null) |
| return; |
| |
| int cch = text.length(); |
| |
| if (cch > 0) |
| s.insertChars( p, getRoot(), text, 0, cch ); |
| } |
| } |
| |
| private static void validateLocalName ( QName name ) |
| { |
| if (name == null) |
| throw new IllegalArgumentException( "QName is null" ); |
| |
| validateLocalName( name.getLocalPart() ); |
| } |
| |
| private static void validateLocalName ( String name ) |
| { |
| if (name == null) |
| throw new IllegalArgumentException( "Name is null" ); |
| |
| if (name.length() == 0) |
| throw new IllegalArgumentException( "Name is empty" ); |
| |
| if (!XMLChar.isValidNCName( name )) |
| throw new IllegalArgumentException( "Name is not valid" ); |
| } |
| |
| private static void validatePrefix ( String name ) |
| { |
| if (name == null) |
| throw new IllegalArgumentException( "Prefix is null" ); |
| |
| if (name.length() == 0) |
| throw new IllegalArgumentException( "Prefix is empty" ); |
| |
| if (Splay.beginsWithXml( name )) |
| throw new IllegalArgumentException( "Prefix begins with 'xml'" ); |
| |
| if (!XMLChar.isValidNCName( name )) |
| throw new IllegalArgumentException( "Prefix is not valid" ); |
| } |
| |
| private void insertAttribute ( QName name, String value ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| insert( new Attr( name ), value ); |
| } |
| } |
| |
| public void insertAttribute ( String name ) |
| { |
| insertAttributeWithValue( name, null, null ); |
| } |
| |
| public void insertAttribute ( String name, String uri ) |
| { |
| insertAttributeWithValue( name, uri, null ); |
| } |
| |
| public void insertAttributeWithValue ( String name, String value ) |
| { |
| insertAttributeWithValue( name, null, value ); |
| } |
| |
| public void insertAttributeWithValue ( |
| String name, String uri, String value ) |
| { |
| validateLocalName( name ); |
| |
| insertAttribute( new QName( uri, name ), value ); |
| } |
| |
| public void insertAttribute ( QName name ) |
| { |
| validateLocalName( name ); |
| |
| insertAttribute( name, null ); |
| } |
| |
| public void insertAttributeWithValue ( QName name, String value ) |
| { |
| validateLocalName( name ); |
| |
| insertAttribute( name, value ); |
| } |
| |
| public void insertNamespace ( String prefix, String namespace ) |
| { |
| synchronized ( monitor() ) |
| { |
| if (prefix == null) |
| prefix = ""; |
| else if (prefix.length() > 0) |
| validatePrefix( prefix ); |
| |
| if (namespace == null) |
| namespace = ""; |
| |
| if (namespace.length() == 0 && prefix.length() > 0) |
| { |
| throw |
| new IllegalArgumentException( |
| "Can't map a prefix to no namespace" ); |
| } |
| |
| insert( new Xmlns( new QName( namespace, prefix ) ), null ); |
| } |
| } |
| |
| public void insertComment ( String value ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| insert( new Comment(), value ); |
| } |
| } |
| |
| public void insertProcInst ( String target, String value ) |
| { |
| validateLocalName( target ); // used becuase "<?xml...?> is disallowed |
| |
| if (Splay.beginsWithXml( target ) && target.length() == 3) |
| throw new IllegalArgumentException( "Target begins with 'xml'" ); |
| |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| insert( new Procinst( target ), value ); |
| } |
| } |
| |
| public void insertElement ( String name ) |
| { |
| insertElementWithText( name, null, null ); |
| } |
| |
| public void insertElementWithText ( String name, String text ) |
| { |
| insertElementWithText( name, null, text ); |
| } |
| |
| public void insertElement ( String name, String uri ) |
| { |
| insertElementWithText( name, uri, null ); |
| } |
| |
| public void insertElement ( QName name ) |
| { |
| insertElementWithText( name, null ); |
| } |
| |
| public void beginElement ( QName name ) |
| { |
| insertElement( name ); |
| toPrevToken(); |
| } |
| |
| public void beginElement ( String name ) |
| { |
| insertElement( name ); |
| toPrevToken(); |
| } |
| |
| public void beginElement ( String name, String uri ) |
| { |
| insertElement( name, uri ); |
| toPrevToken(); |
| } |
| |
| public void insertElementWithText ( QName name, String text ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| validateLocalName( name.getLocalPart() ); |
| |
| Begin b = new Begin( name, null ); |
| |
| b.toggleIsLeaf(); |
| |
| insert( b, text ); |
| } |
| } |
| |
| public void insertElementWithText ( String name, String uri, String text ) |
| { |
| insertElementWithText( new QName( uri, name ), text ); |
| } |
| |
| void insert ( Splay sInsert, String value ) |
| { |
| assert !isDisposed(); |
| assert Root.dv > 0 || sInsert.getRootSlow() == null; |
| assert sInsert.getCch() == 0; |
| |
| if (value != null) |
| sInsert.adjustCch( value.length() ); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| sInsert.checkInsertionValidity( 0, s, p, false ); |
| |
| if (value != null) |
| s.insert( getRoot(), p, sInsert, value, 0, value.length(), true ); |
| else |
| s.insert( getRoot(), p, sInsert, null, 0, 0, true ); |
| |
| assert validate(); |
| } |
| |
| public String getTextValue ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| |
| if (getPos() > 0 || s.isFinish() || s.isXmlns()) |
| { |
| throw new IllegalStateException( |
| "Can't get text value, current token can have no text value" ); |
| } |
| |
| return getSplay().getText( getRoot() ); |
| } |
| } |
| |
| public int getTextValue ( char[] buf, int off, int cch ) |
| { |
| // synchronized ( monitor() ) |
| // { |
| // checkDisposed(); |
| // |
| // Splay s = getSplay(); |
| // |
| // if (getPos() > 0 || s.isFinish() || s.isXmlns()) |
| // { |
| // throw new IllegalStateException( |
| // "Can't get text value, current token can have no text value" ); |
| // } |
| // |
| // return getSplay().getText( getRoot() ); |
| // } |
| |
| // Hack impl for now |
| |
| String s = getTextValue(); |
| |
| int n = s.length(); |
| |
| if (n > cch) |
| n = cch; |
| |
| if (n <= 0) |
| return 0; |
| |
| s.getChars( 0, n, buf, off ); |
| |
| return n; |
| } |
| |
| public void setTextValue ( String text ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| if (p > 0 || s.isXmlns() || s.isFinish()) |
| { |
| throw new IllegalStateException( |
| "Can't set text value, current token can have no text value" ); |
| } |
| |
| |
| s.setText( getRoot(), text, 0, text == null ? 0 : text.length() ); |
| } |
| } |
| |
| public void setTextValue ( char[] buf, int off, int len ) |
| { |
| setTextValue( String.copyValueOf( buf, off, len ) ); |
| } |
| |
| public String getChars ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| int cch = -1; |
| |
| int postCch = getPostCch(); |
| |
| if (cch < 0 || cch > postCch) |
| cch = postCch; |
| |
| return |
| getRoot()._text.fetch( |
| getSplay().getCpForPos( getRoot(), getPos() ), cch ); |
| } |
| } |
| |
| public int getChars ( char[] buf, int off, int cch ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| int postCch = getPostCch(); |
| |
| if (cch < 0 || cch > postCch) |
| cch = postCch; |
| |
| if (buf == null || off >= buf.length) |
| return 0; |
| |
| if (buf.length - off < cch) |
| cch = buf.length - off; |
| |
| getRoot()._text.fetch( |
| buf, off, getSplay().getCpForPos( getRoot(), getPos() ), cch ); |
| |
| return cch; |
| } |
| } |
| |
| public void setBookmark ( XmlBookmark annotation ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (annotation == null) |
| return; |
| |
| clearBookmark( annotation.getKey() ); |
| |
| Annotation a = new Annotation( getRoot(), annotation ); |
| |
| if (a._key == null) |
| throw new IllegalArgumentException( "Annotation key is null" ); |
| |
| a.set( _data._goober ); |
| annotation._currentMark = a; |
| } |
| } |
| |
| public XmlBookmark getBookmark ( Object key ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (key == null) |
| return null; |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| for ( Goober g = s.firstGoober() ; g != null ; g = s.nextGoober( g ) ) |
| { |
| if (g.getKind() == Splay.ANNOTATION && g.getPos() == p) |
| { |
| Annotation a = (Annotation) g; |
| |
| XmlBookmark xa = a.getXmlBookmark(); |
| |
| if (xa != null && a._key.equals( key )) |
| return xa; |
| } |
| } |
| |
| return null; |
| } |
| } |
| |
| public void clearBookmark ( Object key ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (key == null) |
| return; |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| for ( Goober g = s.firstGoober() ; g != null ; g = s.nextGoober( g ) ) |
| { |
| if (g.getKind() == Splay.ANNOTATION && g.getPos() == p) |
| { |
| Annotation a = (Annotation) g; |
| |
| XmlBookmark xa = a.getXmlBookmark(); |
| |
| if (xa != null && a._key.equals( key )) |
| { |
| g.set( null, 0 ); |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| public void getAllBookmarkRefs ( Collection listToFill ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (listToFill == null) |
| return; |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| for ( Goober g = s.firstGoober() ; g != null ; g = s.nextGoober( g ) ) |
| { |
| if (g.getKind() == Splay.ANNOTATION && g.getPos() == p) |
| listToFill.add( ((Annotation) g).getXmlBookmark() ); |
| } |
| } |
| } |
| |
| public boolean hasNextToken ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| assert !getSplay().isRoot() || getPos() == 0; |
| return !getSplay().isRoot(); |
| } |
| } |
| |
| public boolean hasPrevToken ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| return !getSplay().isDoc() || getPos() > 0; |
| } |
| } |
| |
| public QName getName ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (getPos() > 0) |
| return null; |
| |
| Splay s = getSplay(); |
| |
| switch ( s.getKind() ) |
| { |
| case Splay.BEGIN : |
| case Splay.ATTR : |
| case Splay.PROCINST : |
| return s.getName(); |
| } |
| |
| return null; |
| } |
| } |
| |
| public void setName ( QName name ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (name == null) |
| throw new IllegalArgumentException( "Name is null" ); |
| |
| Splay s = getSplay(); |
| |
| if (getPos() > 0 || !(s.isBegin() || s.isAttr() || s.isProcinst())) |
| { |
| throw |
| new IllegalStateException( |
| "Can't set name here: " + currentTokenType() ); |
| } |
| |
| if (s.isProcinst()) |
| { |
| validatePrefix( name.getLocalPart() ); |
| |
| if (name.getNamespaceURI().length() > 0) |
| { |
| throw |
| new IllegalArgumentException( |
| "Procinst name must have no URI" ); |
| } |
| } |
| else if (s.isXmlns()) |
| { |
| if (name.getLocalPart().length() > 0) |
| validatePrefix( name.getLocalPart() ); |
| } |
| else |
| validateLocalName( name.getLocalPart() ); |
| |
| s.setName( getRoot(), name ); |
| } |
| } |
| |
| public int toNextChar ( int cch ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| int maxCch = getPostCch(); |
| |
| if (maxCch == 0 || cch == 0) |
| return 0; |
| |
| if (cch < 0 || cch > maxCch) |
| cch = maxCch; |
| |
| assert p + cch <= s.getEndPos(); |
| |
| if (p + cch == s.getEndPos()) |
| toNextToken(); |
| else |
| set( p + cch ); |
| |
| return cch; |
| } |
| } |
| |
| public int toPrevChar ( int cch ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| Splay sText = s; // The splay and pos where the text exists |
| int pText = p; |
| int maxCch = 0; // Max chars to move over |
| |
| if (p == 0) |
| { |
| if (!s.isDoc() && !s.isAttr()) |
| { |
| sText = s.prevNonAttrSplay(); |
| pText = sText.getEndPos(); |
| maxCch = sText.getCchAfter(); |
| } |
| } |
| else if (s.isLeaf() && p <= s.getPosLeafEnd()) |
| { |
| int dCch = s.ensureContentValid(); |
| p += dCch; |
| pText = p; |
| maxCch = p - 1; |
| } |
| else |
| maxCch = p - s.getPosAfter(); |
| |
| assert pText <= sText.getEndPos(); |
| |
| if (maxCch == 0 || cch == 0) |
| return 0; |
| |
| if (cch < 0 || cch > maxCch) |
| cch = maxCch; |
| |
| set( sText, pText - cch ); |
| |
| return cch; |
| } |
| } |
| |
| public void toEndDoc ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| set( getRoot(), 0 ); |
| } |
| } |
| |
| public void toStartDoc ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| set( getRoot()._doc, 0 ); |
| } |
| } |
| |
| public TokenType toFirstContentToken ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| |
| if (getPos() > 0 || !s.isContainer()) |
| return TokenType.NONE; |
| |
| s.ensureContentValid(); |
| |
| if (s.getCch() > 0 || s.isLeaf()) |
| set( 1 ); |
| else |
| set( s.nextNonAttrSplay(), 0 ); |
| |
| return currentTokenType(); |
| } |
| } |
| |
| public TokenType toEndToken ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| |
| if (getPos() > 0 || !s.isContainer()) |
| return TokenType.NONE; |
| |
| if (s.isLeaf()) |
| set( s.getPosLeafEnd() ); |
| else |
| set( s.getFinishSplay() ); |
| |
| return currentTokenType(); |
| } |
| } |
| |
| public boolean toParent ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| if (p == 0 && s.isDoc()) |
| return false; |
| |
| set( s.getContainer( p ), 0 ); |
| |
| return true; |
| } |
| } |
| |
| public boolean toNextSibling ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| if (p == 0) |
| { |
| if (s.isDoc()) |
| return false; |
| |
| if (s.isBegin()) |
| s = s.getFinishSplay().nextSplay(); |
| } |
| else |
| { |
| if (s.isLeaf() && p <= s.getPosLeafEnd()) |
| return false; |
| |
| s = s.nextSplay(); |
| } |
| |
| for ( ; !s.isBegin() ; s = s.nextSplay() ) |
| { |
| if (s.isFinish()) |
| return false; |
| } |
| |
| set( s, 0 ); |
| |
| return true; |
| } |
| } |
| |
| public boolean toNextSibling ( String name ) |
| { |
| return toNextSibling( new QName( name ) ); |
| } |
| |
| public boolean toNextSibling ( String namespace, String name ) |
| { |
| return toNextSibling( new QName( namespace, name ) ); |
| } |
| |
| public boolean toNextSibling ( QName name ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay sOriginal = getSplay(); |
| int pOriginal = getPos(); |
| |
| for ( ; ; ) |
| { |
| if (!toNextSibling()) |
| break; |
| |
| if (getName().equals( name )) |
| return true; |
| } |
| |
| set( sOriginal, pOriginal ); |
| |
| return false; |
| } |
| } |
| |
| public boolean toPrevSibling ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| if (p == 0) |
| { |
| if (s.isDoc() || s.isAttr()) |
| return false; |
| |
| s = s.prevSplay(); |
| } |
| else |
| { |
| assert p > 0; |
| |
| if (s.isContainer()) |
| { |
| if (s.isLeaf()) |
| { |
| if (p <= s.getPosLeafEnd()) |
| return false; |
| |
| set( 0 ); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| } |
| |
| for ( ; ; ) |
| { |
| if (s.isEnd()) |
| { |
| s = s.getContainer(); |
| break; |
| } |
| |
| if (s.isLeaf()) |
| break; |
| |
| if (s.isContainer()) |
| return false; |
| |
| s = s.prevSplay(); |
| } |
| |
| set( s, 0 ); |
| |
| return true; |
| } |
| } |
| |
| private Splay getStart ( ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| |
| if (!s.isContainer() || getPos() != 0) |
| { |
| push(); |
| |
| if (toNextSibling()) |
| s = getSplay(); |
| else |
| s = null; |
| |
| pop(); |
| } |
| |
| return s; |
| } |
| |
| public boolean toFirstChild ( ) |
| { |
| return toChild( (QName) null ); |
| } |
| |
| public boolean toChild ( String name ) |
| { |
| return toChild( new QName( name ) ); |
| } |
| |
| public boolean toChild ( String namespace, String name ) |
| { |
| return toChild( new QName( namespace, name ) ); |
| } |
| |
| public boolean toChild ( QName name ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getRoot().findNthBegin( getStart(), name, null, 0 ); |
| |
| if (s == null) |
| return false; |
| |
| set( s, 0 ); |
| |
| return true; |
| } |
| } |
| |
| public boolean toChild ( int n ) |
| { |
| return toChild( null, n ); |
| } |
| |
| public boolean toChild ( QName name, int n ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getRoot().findNthBegin( getStart(), name, null, n ); |
| |
| if (s == null) |
| return false; |
| |
| set( s, 0 ); |
| |
| return true; |
| } |
| } |
| |
| public boolean toLastChild ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay sOriginal = getSplay(); |
| int pOriginal = getPos(); |
| |
| if (!sOriginal.isContainer() || pOriginal != 0) |
| { |
| if (!toNextSibling()) |
| return false; |
| } |
| |
| if (!toEndToken().isNone() && toPrevSibling()) |
| return true; |
| |
| set( sOriginal, pOriginal ); |
| |
| return false; |
| } |
| } |
| |
| public boolean toFirstAttribute ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay sOriginal = getSplay(); |
| int pOriginal = getPos(); |
| |
| if (!sOriginal.isContainer() || pOriginal != 0) |
| return false; |
| |
| for ( Splay s = sOriginal.nextSplay() ; s.isAttr() ; s = s.nextSplay() ) |
| { |
| if (s.isNormalAttr()) |
| { |
| set( s, 0 ); |
| return true; |
| } |
| } |
| |
| set( sOriginal, pOriginal ); |
| return false; |
| } |
| } |
| |
| public boolean toLastAttribute ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay sOriginal = getSplay(); |
| int pOriginal = getPos(); |
| |
| if (!sOriginal.isContainer() || pOriginal != 0) |
| return false; |
| |
| Splay lastNormalAttr = null; |
| |
| for ( Splay s = sOriginal.nextSplay() ; s.isAttr() ; s = s.nextSplay() ) |
| { |
| if (s.isNormalAttr()) |
| lastNormalAttr = s; |
| } |
| |
| if (lastNormalAttr != null) |
| { |
| set( lastNormalAttr, 0 ); |
| return true; |
| } |
| |
| set( sOriginal, pOriginal ); |
| return false; |
| } |
| } |
| |
| public boolean toNextAttribute ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| |
| if (!s.isAttr()) |
| return false; |
| |
| for ( s = s.nextSplay() ; s.isAttr() ; s = s.nextSplay() ) |
| { |
| if (s.isNormalAttr()) |
| { |
| set( s, 0 ); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| } |
| |
| public boolean toPrevAttribute ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| |
| if (!s.isAttr()) |
| return false; |
| |
| for ( s = s.prevSplay() ; s.isAttr() ; s = s.prevSplay() ) |
| { |
| if (s.isNormalAttr()) |
| { |
| set( s, 0 ); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| } |
| |
| public String getAttributeText ( QName attrName ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (attrName == null) |
| throw new IllegalArgumentException( "Attr name is null" ); |
| |
| if (getPos() > 0) |
| return null; |
| |
| Splay s = getSplay().getAttr( attrName ); |
| |
| return s == null ? null : s.getText( getRoot() ); |
| } |
| } |
| |
| public boolean setAttributeText ( QName attrName, String value ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (attrName == null) |
| throw new IllegalArgumentException( "Attr name is null" ); |
| |
| validateLocalName( attrName.getLocalPart() ); |
| |
| if (getPos() > 0) |
| return false; |
| |
| Splay s = getSplay(); |
| |
| if (!s.isContainer()) |
| return false; |
| |
| if (value == null) |
| value = ""; |
| |
| s = getSplay().getAttr( attrName ); |
| |
| if (s == null) |
| { |
| XmlCursor c = newCursor(); |
| |
| try |
| { |
| // Insert the new attr at the end |
| |
| do { |
| c.toNextToken(); |
| } while ( c.isAnyAttr() ); |
| |
| c.insertAttributeWithValue( attrName, value ); |
| } |
| finally |
| { |
| c.dispose(); |
| } |
| } |
| else |
| s.setText( getRoot(), value, 0, value.length() ); |
| |
| return true; |
| } |
| } |
| |
| public boolean removeAttribute ( QName attrName ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (attrName == null) |
| throw new IllegalArgumentException( "Attr name is null" ); |
| |
| if (getPos() > 0) |
| return false; |
| |
| boolean removed = false; |
| |
| for ( ; ; ) |
| { |
| Splay s = getSplay().getAttr( attrName ); |
| |
| if (s == null) |
| break; |
| |
| s.remove( getRoot(), true ); |
| |
| removed = true; |
| } |
| |
| return removed; |
| } |
| } |
| |
| public int removeChars ( int cch ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| int postCch = getPostCch(); |
| |
| if (postCch == 0 || cch == 0) |
| return 0; |
| |
| if (cch < 0 || cch > postCch) |
| cch = postCch; |
| |
| return getSplay().removeChars( getRoot(), getPos(), cch ); |
| } |
| } |
| |
| public int moveChars ( int cch, XmlCursor dst ) |
| { |
| if (dst == null) |
| throw new IllegalArgumentException( "Destination is null" ); |
| |
| if (monitor() == dst.monitor()) |
| { |
| synchronized ( monitor() ) |
| { |
| return moveCharsImpl( cch, dst ); |
| } |
| } |
| else |
| { |
| boolean acquired = false; |
| try |
| { |
| GlobalLock.acquire(); |
| acquired = true; |
| synchronized ( monitor() ) |
| { |
| synchronized (dst.monitor()) |
| { |
| GlobalLock.release(); |
| acquired = false; |
| |
| return moveCharsImpl( cch, dst ); |
| } |
| } |
| } |
| catch (InterruptedException e) |
| { |
| throw new XmlRuntimeException(e.getMessage(), e); |
| } |
| finally |
| { |
| if (acquired) |
| GlobalLock.release(); |
| } |
| } |
| } |
| |
| private int moveCharsImpl ( int cch, XmlCursor dst ) |
| { |
| checkDisposed(); |
| |
| if (dst == null || !(dst instanceof Cursor)) |
| throw new IllegalArgumentException( "Invalid destination cursor" ); |
| |
| Cursor cDst = (Cursor) dst; |
| |
| checkDisposed( cDst ); |
| |
| Root rDst = cDst.getRoot(); |
| Splay sDst = cDst.getSplay(); |
| int pDst = cDst.getPos(); |
| |
| if (pDst == 0 && (sDst.isDoc() || sDst.isAttr())) |
| throw new IllegalArgumentException( "Invalid destination" ); |
| |
| return |
| getSplay().moveChars( |
| getRoot(), getPos(), cch, rDst, sDst, pDst, false ); |
| } |
| |
| public int copyChars ( int cch, XmlCursor dst ) |
| { |
| if (dst == null) |
| throw new IllegalArgumentException( "Destination is null" ); |
| |
| if (dst.monitor() == monitor()) |
| { |
| synchronized ( monitor() ) |
| { |
| return copyCharsImpl( cch, dst ); |
| } |
| } |
| else |
| { |
| boolean acquired = false; |
| try |
| { |
| GlobalLock.acquire(); |
| acquired = true; |
| synchronized ( monitor() ) |
| { |
| synchronized (dst.monitor()) |
| { |
| GlobalLock.release(); |
| acquired = false; |
| |
| return copyCharsImpl( cch, dst ); |
| } |
| } |
| } |
| catch (InterruptedException e) |
| { |
| throw new XmlRuntimeException(e.getMessage(), e); |
| } |
| finally |
| { |
| if (acquired) |
| GlobalLock.release(); |
| } |
| } |
| } |
| |
| private int copyCharsImpl ( int cch, XmlCursor dst ) |
| { |
| checkDisposed(); |
| |
| if (dst == null || !(dst instanceof Cursor)) |
| throw new IllegalArgumentException( "Invalid destination cursor" ); |
| |
| Cursor cDst = (Cursor) dst; |
| |
| checkDisposed( cDst ); |
| |
| Root rDst = cDst.getRoot(); |
| Splay sDst = cDst.getSplay(); |
| int pDst = cDst.getPos(); |
| |
| if (pDst == 0) |
| { |
| if (sDst.isDoc() || sDst.isAttr()) |
| throw new IllegalArgumentException( "Invalid destination" ); |
| |
| sDst = sDst.prevNonAttrSplay(); |
| pDst = sDst.getEndPos(); |
| } |
| |
| return |
| getSplay().copyChars( getRoot(), getPos(), cch, rDst, sDst, pDst ); |
| } |
| |
| public String namespaceForPrefix ( String prefix ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| |
| if (getPos() > 0 || !s.isContainer()) |
| throw new IllegalStateException( "Not on a container" ); |
| |
| return s.namespaceForPrefix( prefix ); |
| } |
| } |
| |
| public String prefixForNamespace ( String ns ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (ns == null || ns.length() == 0) |
| throw new IllegalArgumentException( "Must specify a namespace" ); |
| |
| Splay s = getSplay(); |
| |
| if (getPos() > 0 || !s.isContainer()) |
| throw new IllegalStateException( "Not on a container" ); |
| |
| String result = s.prefixForNamespace( getRoot(), ns, null, true); |
| |
| assert result != null; |
| |
| return result; |
| } |
| } |
| |
| public void getAllNamespaces ( Map addToThis ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| |
| if (getPos() > 0 || !s.isContainer()) |
| throw new IllegalStateException( "Not on a container" ); |
| |
| // Do this with cursor for now... |
| |
| XmlCursor c = newCursor(); |
| |
| do |
| { |
| assert c.isContainer(); |
| |
| QName cName = c.getName(); |
| |
| while ( !c.toNextToken().isNone() && c.isAnyAttr() ) |
| { |
| if (c.isNamespace()) |
| { |
| String prefix = c.getName().getLocalPart(); |
| String uri = c.getName().getNamespaceURI(); |
| |
| // Here I check to see if there is a default namespace |
| // mapping which is not empty on a non root container which |
| // is in a namespace. This this case, I do not want to add |
| // this mapping because it could not be persisted out this |
| // way. |
| |
| if (prefix.length() == 0 && uri.length() > 0 && |
| cName != null && cName.getNamespaceURI().length()>0) |
| { |
| continue; |
| } |
| |
| if (!addToThis.containsKey( prefix )) |
| addToThis.put( prefix, uri ); |
| } |
| } |
| |
| c.toParent(); |
| } |
| while ( c.toParent() ); |
| |
| c.dispose(); |
| } |
| } |
| |
| /** |
| * Returns: |
| * |
| * -1 is this is left of that |
| * 0 is this is left is at same position as that |
| * 1 is this is right of that |
| */ |
| |
| public int comparePosition ( XmlCursor xthat ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (xthat == null || !(xthat instanceof Cursor)) |
| throw new IllegalArgumentException( "Invalid that cursor" ); |
| |
| Cursor that = (Cursor) xthat; |
| |
| Root r = getRoot(); |
| |
| if (r != that.getRoot()) |
| throw new IllegalArgumentException( "Cursors not in same document" ); |
| |
| checkDisposed( that ); |
| |
| return |
| getSplay().compare( r, getPos(), that.getSplay(), that.getPos() ); |
| } |
| } |
| |
| public boolean isLeftOf ( XmlCursor that ) |
| { |
| return comparePosition( that ) < 0; |
| } |
| |
| public boolean isAtSamePositionAs ( XmlCursor that ) |
| { |
| return comparePosition( that ) == 0; |
| } |
| |
| public boolean isRightOf ( XmlCursor that ) |
| { |
| return comparePosition( that ) > 0; |
| } |
| |
| public InputStream newInputStream ( XmlOptions options ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| return |
| new Saver.InputStreamSaver( |
| getRoot(), getSplay(), getPos(), options ); |
| } |
| } |
| |
| public InputStream newInputStream ( ) |
| { |
| return newInputStream( null ); |
| } |
| |
| public Reader newReader ( ) |
| { |
| return newReader( null ); |
| } |
| |
| public Reader newReader ( XmlOptions options ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| return new Saver.TextReader( getRoot(), getSplay(), getPos(), options ); |
| } |
| } |
| |
| public XMLInputStream newXMLInputStream ( XmlOptions options ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| return |
| new XmlInputStreamImpl( getRoot(), getSplay(), getPos(), options ); |
| } |
| } |
| |
| public XMLInputStream newXMLInputStream ( ) |
| { |
| return newXMLInputStream( null ); |
| } |
| |
| private static final XmlOptions _toStringOptions = |
| buildPrettyOptions(); |
| |
| static final XmlOptions buildPrettyOptions ( ) |
| { |
| XmlOptions options = new XmlOptions(); |
| options.put( XmlOptions.SAVE_PRETTY_PRINT ); |
| options.put( XmlOptions.SAVE_AGGRESSIVE_NAMESPACES ); |
| options.put( XmlOptions.SAVE_USE_DEFAULT_NAMESPACE ); |
| return options; |
| } |
| |
| public String toString ( ) |
| { |
| return xmlText( _toStringOptions ); |
| } |
| |
| public String xmlText ( ) |
| { |
| return xmlText( null ); |
| } |
| |
| public String xmlText ( XmlOptions options ) |
| { |
| Saver.TextSaver saver; |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| saver = new Saver.TextSaver( |
| getRoot(), getSplay(), getPos(), options, null ); |
| } |
| return saver.saveToString(); |
| } |
| |
| static class ChangeStampImpl implements ChangeStamp |
| { |
| ChangeStampImpl ( Root r ) |
| { |
| _root = r; |
| _versionStamp = _root.getVersion(); |
| } |
| |
| public boolean hasChanged ( ) |
| { |
| return _versionStamp != _root.getVersion(); |
| } |
| |
| private final Root _root; |
| private final long _versionStamp; |
| } |
| |
| public ChangeStamp getDocChangeStamp ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| return new ChangeStampImpl( getRoot() ); |
| } |
| } |
| |
| public boolean removeXml ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| assert p < s.getEndPos(); |
| |
| if (p > 0) |
| { |
| if (s.isLeaf() && p == s.getPosLeafEnd()) |
| return false; |
| |
| int cchRemove = removeChars( getPostCch() ); |
| |
| assert cchRemove > 0; |
| |
| return true; |
| } |
| else if (s.isDoc()) |
| { |
| throw |
| new IllegalStateException( |
| "Can't remove a whole document." ); |
| } |
| else if (s.isFinish()) |
| return false; |
| |
| s.remove( getRoot(), true ); |
| |
| return true; |
| } |
| } |
| |
| public boolean moveXml ( XmlCursor dst ) |
| { |
| if (dst == null) |
| throw new IllegalArgumentException( "Destination is null" ); |
| |
| if (dst.monitor() == monitor()) |
| { |
| synchronized ( monitor() ) |
| { |
| return moveXmlImpl( dst ); |
| } |
| } |
| else |
| { |
| boolean acquired = false; |
| try |
| { |
| GlobalLock.acquire(); |
| acquired = true; |
| synchronized ( monitor() ) |
| { |
| synchronized (dst.monitor()) |
| { |
| GlobalLock.release(); |
| acquired = false; |
| |
| return moveXmlImpl ( dst ); |
| } |
| } |
| } |
| catch (InterruptedException e) |
| { |
| throw new XmlRuntimeException(e.getMessage(), e); |
| } |
| finally |
| { |
| if (acquired) |
| GlobalLock.release(); |
| } |
| } |
| } |
| |
| private boolean moveXmlImpl ( XmlCursor dst ) |
| { |
| checkDisposed(); |
| |
| if (dst == null || !(dst instanceof Cursor)) |
| { |
| throw |
| new IllegalArgumentException( |
| "Can't move to a foreign document" ); |
| } |
| |
| Cursor cDst = (Cursor) dst; |
| |
| checkDisposed( cDst ); |
| |
| Root rDst = cDst.getRoot(); |
| Splay sDst = cDst.getSplay(); |
| int pDst = cDst.getPos(); |
| |
| Root rSrc = getRoot(); |
| Splay sSrc = getSplay(); |
| int pSrc = getPos(); |
| |
| if (sSrc.checkInsertionValidity( pSrc, sDst, pDst, true )) |
| { |
| return |
| sSrc.moveChars( |
| rSrc, pSrc, getPostCch(), rDst, sDst, pDst, false ) > 0; |
| } |
| |
| assert pSrc == 0; |
| |
| // Check for a movement of stuff into itself! This case is basically |
| // a no-op |
| |
| if (rSrc == rDst && sDst.between( rDst, pDst, sSrc )) |
| { |
| // TODO - I might have to orphan types in the range here .... |
| return false; |
| } |
| |
| assert pSrc == 0; |
| |
| sSrc.move( rSrc, cDst.getRoot(), cDst.getSplay(), cDst.getPos(), true ); |
| |
| return true; |
| } |
| |
| public boolean copyXml ( XmlCursor dst ) |
| { |
| if (dst == null) |
| throw new IllegalArgumentException( "Destination is null" ); |
| |
| if (dst.monitor() == monitor()) |
| { |
| synchronized ( monitor() ) |
| { |
| return copyXmlImpl( dst ); |
| } |
| } |
| else |
| { |
| boolean acquired = false; |
| try |
| { |
| GlobalLock.acquire(); |
| acquired = true; |
| synchronized ( monitor() ) |
| { |
| synchronized (dst.monitor()) |
| { |
| GlobalLock.release(); |
| acquired = false; |
| |
| return copyXmlImpl( dst ); |
| } |
| } |
| } |
| catch (InterruptedException e) |
| { |
| throw new XmlRuntimeException(e.getMessage(), e); |
| } |
| finally |
| { |
| if (acquired) |
| GlobalLock.release(); |
| } |
| } |
| } |
| |
| private boolean copyXmlImpl ( XmlCursor dst ) |
| { |
| checkDisposed(); |
| |
| if (dst == null || !(dst instanceof Cursor)) |
| { |
| throw |
| new IllegalArgumentException( "Can't copy to a foreign document" ); |
| } |
| |
| Cursor cDst = (Cursor) dst; |
| |
| checkDisposed( cDst ); |
| |
| Splay sDst = cDst.getSplay(); |
| int pDst = cDst.getPos(); |
| |
| Splay s = getSplay(); |
| int p = getPos(); |
| |
| if (s.checkInsertionValidity( p, sDst, pDst, true )) |
| return copyCharsImpl( getPostCch(), dst ) > 0; |
| |
| assert p == 0; |
| |
| // Need to make a splay copy before getting the text because the copy |
| // will validate invalid contents/values |
| |
| Root r = getRoot(); |
| Root rDst = cDst.getRoot(); |
| |
| Splay copy = s.copySplay(); |
| |
| Object txt = r._text; |
| int cp = r.getCp( s ); |
| int cch = copy.getCchLeft() + copy.getCch(); |
| |
| // |
| // Remove text after which might be between leaf value and first attr value |
| // |
| |
| if (s.isLeaf() && s.getCchAfter() > 0) |
| { |
| int cchValue = s.getCchValue(); |
| int cchAfter = s.getCchAfter(); |
| |
| if (cchValue == 0) |
| cp += cchAfter; |
| else if (s.nextSplay().isAttr()) |
| { |
| char[] buf = new char [ cch ]; |
| r._text.fetch( buf, 0, cp, cchValue ); |
| r._text.fetch( buf, cchValue, cp + cchValue + cchAfter, cch - cchValue ); |
| |
| txt = buf; |
| cp = 0; |
| } |
| } |
| |
| sDst.insert( rDst, pDst, copy, txt, cp, cch, true ); |
| |
| return true; |
| } |
| |
| public boolean removeXmlContents ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| // TODO - should implement this with internals |
| |
| if (!isContainer()) |
| return false; |
| |
| TokenType tt = toFirstContentToken(); |
| assert !tt.isNone(); |
| |
| boolean removed = !isFinish(); |
| |
| try |
| { |
| while ( !isFinish() ) |
| { |
| boolean b = removeXml(); |
| assert b; |
| } |
| } |
| finally |
| { |
| toParent(); |
| } |
| |
| return removed; |
| } |
| } |
| |
| private boolean contains ( XmlCursor dst ) |
| { |
| if (isInSameDocument( dst )) |
| { |
| dst.push(); |
| |
| for ( ; ; ) |
| { |
| if (dst.isAtSamePositionAs( this )) |
| { |
| dst.pop(); |
| return true; |
| } |
| |
| if (!dst.toParent()) |
| break; |
| } |
| |
| dst.pop(); |
| } |
| |
| return false; |
| } |
| |
| public boolean moveXmlContents ( XmlCursor dst ) |
| { |
| if (dst == null) |
| throw new IllegalArgumentException( "Destination is null" ); |
| |
| if (dst.monitor() == monitor()) |
| { |
| synchronized ( monitor() ) |
| { |
| return moveXmlContentsImpl( dst ); |
| } |
| } |
| else |
| { |
| boolean acquired = false; |
| try |
| { |
| GlobalLock.acquire(); |
| acquired = true; |
| synchronized ( monitor() ) |
| { |
| synchronized (dst.monitor()) |
| { |
| GlobalLock.release(); |
| acquired = false; |
| |
| return moveXmlContentsImpl( dst ); |
| } |
| } |
| } |
| catch (InterruptedException e) |
| { |
| throw new XmlRuntimeException(e.getMessage(), e); |
| } |
| finally |
| { |
| if (acquired) |
| GlobalLock.release(); |
| } |
| } |
| } |
| |
| private boolean moveXmlContentsImpl ( XmlCursor dst ) |
| { |
| checkDisposed(); |
| |
| if (!isContainer()) |
| return false; |
| |
| // Check to see if dst is in src! In this case, there is nothing to |
| // do. |
| |
| if (contains( dst )) |
| return false; |
| |
| TokenType tt = toFirstContentToken(); |
| assert !tt.isNone(); |
| |
| boolean moved = !isFinish(); |
| |
| try |
| { |
| if (!moveXmlImpl( dst )) |
| return false; |
| |
| while ( !isFinish() ) |
| { |
| boolean b = moveXmlImpl( dst ); |
| assert b; |
| } |
| } |
| finally |
| { |
| toParent(); |
| } |
| |
| return moved; |
| } |
| |
| public boolean copyXmlContents ( XmlCursor dst ) |
| { |
| if (dst == null) |
| throw new IllegalArgumentException( "Destination is null" ); |
| |
| if (dst.monitor() == monitor()) |
| { |
| synchronized ( monitor() ) |
| { |
| return copyXmlContentsImpl( dst ); |
| } |
| } |
| else |
| { |
| boolean acquired = false; |
| try |
| { |
| GlobalLock.acquire(); |
| acquired = true; |
| synchronized ( monitor() ) |
| { |
| synchronized (dst.monitor()) |
| { |
| GlobalLock.release(); |
| acquired = false; |
| |
| return copyXmlContentsImpl( dst ); |
| } |
| } |
| } |
| catch (InterruptedException e) |
| { |
| throw new XmlRuntimeException(e.getMessage(), e); |
| } |
| finally |
| { |
| if (acquired) |
| GlobalLock.release(); |
| } |
| } |
| } |
| |
| private boolean copyXmlContentsImpl ( XmlCursor dst ) |
| { |
| checkDisposed(); |
| |
| if (!isContainer()) |
| return false; |
| |
| // Check to see if dst is in src! In this case, copy the src to a new |
| // document and then move the copied contents to the destination. |
| |
| if (contains( dst )) |
| { |
| XmlCursor cTmp = XmlObject.Factory.newInstance().newCursor(); |
| |
| cTmp.toNextToken(); |
| |
| if (!copyXmlContentsImpl( cTmp )) |
| { |
| cTmp.dispose(); |
| return false; |
| } |
| |
| cTmp.toStartDoc(); |
| ((Cursor)cTmp).moveXmlContentsImpl( dst ); |
| cTmp.dispose(); |
| return true; |
| } |
| |
| TokenType tt = toFirstContentToken(); |
| assert !tt.isNone(); |
| |
| boolean copied = !isFinish(); |
| |
| try |
| { |
| if (!copyXmlImpl( dst )) |
| return false; |
| |
| for ( ; ; ) |
| { |
| if (isStart()) |
| toEndToken(); |
| |
| toNextToken(); |
| |
| if (isFinish()) |
| break; |
| |
| boolean b = copyXmlImpl( dst ); |
| assert b; |
| } |
| } |
| finally |
| { |
| toParent(); |
| } |
| |
| return copied; |
| } |
| |
| public boolean isInSameDocument ( XmlCursor xthat ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (xthat == null || !(xthat instanceof Cursor)) |
| return false; |
| |
| Cursor that = (Cursor) xthat; |
| |
| checkDisposed( that ); |
| |
| return getRoot() == that.getRoot(); |
| } |
| } |
| |
| public void push ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (_data._stack == null) |
| _data._stack = new Selections(); |
| |
| _data._stack.add( getRoot(), getSplay(), getPos() ); |
| |
| getRoot().registerForChange( this ); |
| } |
| } |
| |
| public boolean pop ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (_data._stack == null || _data._stack.size() == 0) |
| return false; |
| |
| _data._stack.setCursor( this, _data._stack.size() - 1 ); |
| |
| _data._stack.pop(); |
| |
| return true; |
| } |
| } |
| |
| public int getSelectionCount ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| return _data._selections == null ? 0 : _data._selections.size(); |
| } |
| } |
| |
| public boolean toSelection ( int i ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (_data._selections != null && i >= 0 && _data._selections.setCursor( this, i )) |
| { |
| _data._currentSelection = i; |
| return true; |
| } |
| |
| return false; |
| } |
| } |
| |
| public boolean hasNextSelection ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| push(); |
| int currentSelection = _data._currentSelection; |
| |
| try |
| { |
| return toNextSelection(); |
| } |
| finally |
| { |
| pop(); |
| _data._currentSelection = currentSelection; |
| } |
| } |
| } |
| |
| public boolean toNextSelection ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (_data._selections == null || _data._currentSelection < -1) |
| return false; |
| |
| int nextSelection = _data._currentSelection + 1; |
| |
| if (!_data._selections.setCursor( this, nextSelection )) |
| { |
| _data._currentSelection = -2; |
| return false; |
| } |
| |
| _data._currentSelection = nextSelection; |
| |
| return true; |
| } |
| } |
| |
| public void clearSelections ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| _data.clearSelections(); |
| } |
| } |
| |
| public void addToSelection ( ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (_data._selections == null) |
| _data._selections = Path.newSelections(); |
| |
| // Force any selection engine to search all... |
| _data._selections.size(); |
| |
| _data._selections.add( getRoot(), getSplay(), getPos() ); |
| |
| getRoot().registerForChange( this ); |
| } |
| } |
| |
| public void changeNotification ( ) |
| { |
| if (_data._selections != null) |
| { |
| _data._selections.size(); // Force a full selection |
| _data._selections.cursify( getRoot() ); |
| } |
| |
| if (_data._stack != null) |
| _data._stack.cursify( getRoot() ); |
| } |
| |
| public void selectPath ( String path, XmlOptions options ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| if (_data._selections == null) |
| _data._selections = Path.newSelections(); |
| else |
| _data._selections.dispose(); |
| |
| _data._selections.init( |
| Path.select( getRoot(), getSplay(), getPos(), path, options ) ); |
| |
| push(); |
| |
| if (_data._selections.setCursor( this, 0 )) |
| { |
| getRoot().registerForChange( this ); |
| _data._currentSelection = -1; |
| } |
| else |
| _data._currentSelection = -2; |
| |
| pop(); |
| } |
| } |
| |
| public void selectPath ( String path ) |
| { |
| selectPath( path, null ); |
| } |
| |
| public XmlCursor execQuery ( String queryExpr, XmlOptions options ) |
| { |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| return Path.query( this, queryExpr, options ); |
| } |
| } |
| |
| public XmlCursor execQuery ( String query ) |
| { |
| return execQuery( query, null ); |
| } |
| |
| public Node newDomNode ( ) |
| { |
| return newDomNode( null ); |
| } |
| |
| public Node newDomNode ( XmlOptions options ) |
| { |
| try |
| { |
| Saver.DomSaver saver; |
| |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| saver = new Saver.DomSaver( |
| getRoot(), getSplay(), getPos(), !isFragment(), options ); |
| } |
| |
| return saver.exportDom(); |
| } |
| catch ( Exception e ) |
| { |
| if (e instanceof RuntimeException) |
| throw (RuntimeException) e; |
| |
| throw new RuntimeException( e.getMessage(), e ); |
| } |
| } |
| |
| private boolean isFragment() |
| { |
| if (! isStartdoc()) |
| return true; |
| |
| boolean seenElement = false; |
| |
| XmlCursor c = newCursor(); |
| int token = c.toNextToken().intValue(); |
| |
| try { |
| |
| LOOP: |
| while (true) |
| { |
| SWITCH: |
| switch (token) |
| { |
| case TokenType.INT_START: |
| if (seenElement) return true; |
| seenElement = true; |
| token = c.toEndToken().intValue(); |
| break SWITCH; |
| |
| case TokenType.INT_TEXT: |
| if (! Splay.isWhiteSpace(c.getChars())) |
| return true; |
| token = c.toNextToken().intValue(); |
| break SWITCH; |
| |
| |
| case TokenType.INT_NONE: |
| case TokenType.INT_ENDDOC: |
| break LOOP; |
| |
| |
| case TokenType.INT_ATTR: |
| case TokenType.INT_NAMESPACE: |
| return true; |
| |
| case TokenType.INT_END: |
| case TokenType.INT_COMMENT: |
| case TokenType.INT_PROCINST: |
| token = c.toNextToken().intValue(); |
| break SWITCH; |
| |
| case TokenType.INT_STARTDOC: |
| assert false; |
| break LOOP; |
| |
| } |
| |
| } |
| } |
| |
| finally { |
| c.dispose(); |
| } |
| |
| return ! seenElement; |
| |
| } |
| |
| public void save ( ContentHandler ch, LexicalHandler lh ) |
| throws SAXException |
| { |
| save( ch, lh, null ); |
| } |
| |
| public void save ( File file ) throws IOException |
| { |
| save( file, null ); |
| } |
| |
| public void save ( OutputStream os ) throws IOException |
| { |
| save( os, null ); |
| } |
| |
| public void save ( Writer w ) throws IOException |
| { |
| save( w, null ); |
| } |
| |
| public void save ( ContentHandler ch, LexicalHandler lh, XmlOptions options) |
| throws SAXException |
| { |
| // todo: don't hold the monitor all this long. |
| // Ideally, we'd release the monitor each time we're about to call the |
| // ch or lh objects. However, the saver code isn't _quite_ structure |
| // just right to do that. So instead, the current version will hold |
| // the monitor for the duration of the entire SAX save. |
| synchronized ( monitor() ) |
| { |
| checkDisposed(); |
| |
| new Saver.SaxSaver( getRoot(), getSplay(), getPos(), options, ch, lh ); |
| } |
| } |
| |
| public void save ( File file, XmlOptions options ) throws IOException |
| { |
| OutputStream os = new FileOutputStream( file ); |
| |
| try |
| { |
| save( os, options ); |
| } |
| finally |
| { |
| os.close(); |
| } |
| } |
| |
| public void save ( OutputStream os, XmlOptions options ) throws IOException |
| { |
| // note that we do not hold the monitor for the duration of a save. |
| // Instead, a concurrent modification exception is thrown if the |
| // document is modified while the save is in progress. If the user |
| // wishes to protect against this, he can synchronize on the monitor |
| // himself. |
| InputStream is = newInputStream( options ); |
| |
| try |
| { |
| byte[] bytes = new byte[ 8192 ]; |
| |
| for ( ; ; ) |
| { |
| int n = is.read( bytes ); |
| |
| if (n < 0) |
| break; |
| |
| os.write( bytes, 0, n ); |
| } |
| } |
| finally |
| { |
| is.close(); |
| } |
| } |
| |
| public void save ( Writer w, XmlOptions options ) throws IOException |
| { |
| Reader r = newReader( options ); |
| |
| try |
| { |
| char[] chars = new char[ 8192 ]; |
| |
| for ( ; ; ) |
| { |
| int n = r.read( chars ); |
| |
| if (n < 0) |
| break; |
| |
| w.write( chars, 0, n ); |
| } |
| } |
| finally |
| { |
| r.close(); |
| } |
| } |
| |
| // |
| // |
| // |
| |
| private boolean validate ( ) |
| { |
| assert _data._goober.getRoot().validate(); |
| return true; |
| } |
| |
| public void dump ( ) { _data._goober.getRoot().dump(); } |
| public void dump ( boolean verbose ) { _data._goober.getRoot().dump( verbose ); } |
| |
| interface PathEngine |
| { |
| boolean next ( Selections selections ); |
| } |
| |
| static class Selections |
| { |
| void init ( PathEngine pathEngine ) |
| { |
| dispose(); |
| |
| _pathEngine = pathEngine; |
| } |
| |
| void add ( Root r, Splay s ) |
| { |
| add( r, s, 0 ); |
| } |
| |
| void add ( Root r, Splay s, int p ) |
| { |
| assert s.getRootSlow() == r; |
| |
| if (_cursors != null) |
| { |
| CursorGoober g = new CursorGoober( r ); |
| |
| g.set( s, p ); |
| |
| _cursors.add( g ); |
| |
| return; |
| } |
| |
| if (_splays == null) |
| { |
| assert _count == 0; |
| _splays = new Splay [ 16 ]; |
| _positions = new int [ 16 ]; |
| } |
| else if (_count == _splays.length) |
| { |
| Splay[] newSplays = new Splay [ _count * 2 ]; |
| int[] newPositions = new int [ _count * 2 ]; |
| |
| System.arraycopy( _splays, 0, newSplays, 0, _count ); |
| System.arraycopy( _positions, 0, newPositions, 0, _count ); |
| |
| _splays = newSplays; |
| _positions = newPositions; |
| } |
| |
| _splays[ _count ] = s; |
| _positions[ _count ] = p; |
| |
| _count++; |
| } |
| |
| void pop ( ) |
| { |
| assert size() > 0; |
| |
| if (_cursors != null) |
| { |
| int i = _cursors.size() - 1; |
| ((CursorGoober) _cursors.get( i )).set( null, 0 ); |
| _cursors.remove( i ); |
| } |
| else |
| _count--; |
| } |
| |
| void cursify ( Root r ) |
| { |
| if (_cursors != null || _count <= 0) |
| return; |
| |
| _cursors = new ArrayList(); |
| |
| for ( int i = 0 ; i < _count ; i++ ) |
| { |
| CursorGoober g = new CursorGoober( r ); |
| |
| g.set( _splays[ i ], _positions[ i ] ); |
| |
| _cursors.add( g ); |
| } |
| |
| _count = 0; |
| } |
| |
| int size ( ) |
| { |
| if (_pathEngine != null) |
| { |
| while ( _pathEngine.next( this ) ) |
| ; |
| |
| _pathEngine = null; |
| } |
| |
| return currentSize(); |
| } |
| |
| int currentSize ( ) |
| { |
| return _cursors != null ? _cursors.size() : _count; |
| } |
| |
| boolean setCursor ( Cursor c, int i ) |
| { |
| assert i >= 0; |
| |
| while ( _pathEngine != null && currentSize() <= i ) |
| { |
| if (!_pathEngine.next( this )) |
| _pathEngine = null; |
| } |
| |
| if (i >= currentSize()) |
| return false; |
| |
| if (_cursors != null) |
| { |
| assert i < _cursors.size(); |
| c.set( (CursorGoober) _cursors.get( i ) ); |
| } |
| else |
| { |
| assert i < _count; |
| c.set( _splays[ i ], _positions[ i ] ); |
| } |
| |
| return true; |
| } |
| |
| void dispose ( ) |
| { |
| if (_cursors != null) |
| { |
| for ( int i = 0 ; i < _cursors.size() ; i++ ) |
| ((CursorGoober) _cursors.get( i )).set( null, 0 ); |
| |
| _cursors.clear(); |
| |
| _cursors = null; |
| } |
| |
| _count = 0; |
| |
| // TODO - cache unused Seleciton objects for later reuse |
| } |
| |
| private Splay[] _splays; |
| private int[] _positions; |
| private int _count; |
| |
| private ArrayList _cursors; |
| |
| private PathEngine _pathEngine; |
| } |
| |
| // |
| // |
| // |
| |
| CursorData _data; |
| } |