blob: 3bac3484828984805c71bf77e860d27ad03734aa [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.newstore2;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.SAXParseException;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import java.util.HashMap;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.Reference;
import java.lang.ref.PhantomReference;
import java.lang.reflect.Method;
import javax.xml.parsers.SAXParserFactory;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.IOException;
import javax.xml.stream.XMLStreamReader;
import org.apache.xmlbeans.xml.stream.XMLInputStream;
import org.apache.xmlbeans.xml.stream.XMLStreamException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import org.apache.xmlbeans.impl.common.QNameHelper;
import org.apache.xmlbeans.impl.common.XmlLocale;
import org.apache.xmlbeans.impl.common.ResolverUtil;
import org.apache.xmlbeans.impl.newstore2.Saaj.SaajCallback;
import org.apache.xmlbeans.impl.newstore2.DomImpl.Dom;
import org.apache.xmlbeans.impl.newstore2.DomImpl.TextNode;
import org.apache.xmlbeans.impl.newstore2.DomImpl.CdataNode;
import org.apache.xmlbeans.impl.newstore2.DomImpl.SaajTextNode;
import org.apache.xmlbeans.impl.newstore2.DomImpl.SaajCdataNode;
import org.apache.xmlbeans.impl.newstore2.Cur.Locations;
import org.apache.xmlbeans.XmlBeans;
import org.apache.xmlbeans.XmlSaxHandler;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.SchemaTypeLoader;
import org.apache.xmlbeans.XmlTokenSource;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.QNameSet;
import org.apache.xmlbeans.QNameCache;
import org.apache.xmlbeans.XmlBeans;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlRuntimeException;
import org.apache.xmlbeans.XmlDocumentProperties;
import javax.xml.namespace.QName;
import org.apache.xmlbeans.impl.values.TypeStore;
import org.apache.xmlbeans.impl.values.TypeStoreUser;
import org.apache.xmlbeans.impl.values.TypeStoreUserFactory;
public final class Locale implements DOMImplementation, SaajCallback, XmlLocale
{
static final int ROOT = Cur.ROOT;
static final int ELEM = Cur.ELEM;
static final int ATTR = Cur.ATTR;
static final int COMMENT = Cur.COMMENT;
static final int PROCINST = Cur.PROCINST;
static final int TEXT = Cur.TEXT;
static final int WS_UNSPECIFIED = TypeStore.WS_UNSPECIFIED;
static final int WS_PRESERVE = TypeStore.WS_PRESERVE;
static final int WS_REPLACE = TypeStore.WS_REPLACE;
static final int WS_COLLAPSE = TypeStore.WS_COLLAPSE;
static final String _xsi = "http://www.w3.org/2001/XMLSchema-instance";
static final String _schema = "http://www.w3.org/2001/XMLSchema";
static final String _openFragUri = "http://www.openuri.org/fragment";
static final String _xml1998Uri = "http://www.w3.org/XML/1998/namespace";
static final String _xmlnsUri = "http://www.w3.org/2000/xmlns/";
static final QName _xsiNil = new QName( _xsi, "nil", "xsi" );
static final QName _xsiType = new QName( _xsi, "type", "xsi" );
static final QName _xsiLoc = new QName( _xsi, "schemaLocation", "xsi" );
static final QName _xsiNoLoc = new QName( _xsi, "noNamespaceSchemaLocation", "xsi" );
static final QName _openuriFragment = new QName( _openFragUri, "fragment", "frag" );
static final QName _xmlFragment = new QName( "xml-fragment" );
private Locale ( SchemaTypeLoader stl, XmlOptions options )
{
options = XmlOptions.maskNull( options );
//
//
//
// TODO - add option for no=sync, or make it all thread safe
//
// Also - have a thread local setting for thread safety? .. Perhaps something
// in the type loader which defines whether ot not sync is on????
_noSync = true;
_tempFrames = new Cur [ _numTempFramesLeft = 8 ];
// BUGBUG - this cannot be thread locale ....
// BUGBUG - this cannot be thread locale ....
// BUGBUG - this cannot be thread locale ....
//
// Lazy create this (loading up a locale should use the thread locale one)
// same goes for the qname factory .. use thread local for hte most part when loading
_charUtil = CharUtil.getThreadLocalCharUtil();
_qnameFactory = new DefaultQNameFactory();
_locations = new Locations();
_schemaTypeLoader = stl;
_validateOnSet = options.hasOption( XmlOptions.VALIDATE_ON_SET );
//
// Check for Saaj implementation request
//
Object saajObj = options.get( Public2.SAAJ_IMPL );
if (saajObj != null)
{
if (!(saajObj instanceof Saaj))
throw new IllegalStateException( "Saaj impl not correct type: " + saajObj );
_saaj = (Saaj) saajObj;
_saaj.setCallback( this );
}
}
//
//
//
public static final String USE_SAME_LOCALE = "USE_SAME_LOCALE";
static Locale getLocale ( SchemaTypeLoader stl, XmlOptions options )
{
if (stl == null)
stl = XmlBeans.getContextTypeLoader();
options = XmlOptions.maskNull( options );
Locale l = null;
if (options.hasOption( USE_SAME_LOCALE ))
{
Object source = options.get( USE_SAME_LOCALE );
if (source instanceof Locale)
l = (Locale) source;
else if (source instanceof XmlTokenSource)
l = (Locale) ((XmlTokenSource) source).monitor();
else
throw new IllegalArgumentException( "Source locale not understood: " + source );
if (l._schemaTypeLoader != stl)
throw new IllegalArgumentException( "Source locale does not support same schema type loader" );
if (l._saaj != null && l._saaj != options.get( Public2.SAAJ_IMPL ))
throw new IllegalArgumentException( "Source locale does not support same saaj" );
if (l._validateOnSet && !options.hasOption( XmlOptions.VALIDATE_ON_SET ))
throw new IllegalArgumentException( "Source locale does not support same validate on set" );
// TODO - other things to check?
}
else
l = new Locale( stl, options );
return l;
}
//
//
//
private void autoTypeDocument ( Cur c, SchemaType requestedType, XmlOptions options )
throws XmlException
{
assert c.isRoot();
// The type in the options overrides all sniffing
options = XmlOptions.maskNull( options );
SchemaType optionType = (SchemaType) options.get( XmlOptions.DOCUMENT_TYPE );
if (optionType != null)
{
c.setType( optionType );
return;
}
SchemaType type = null;
// An xsi:type can be used to pick a type out of the loader, or used to refine
// a type with a name.
if (requestedType == null || requestedType.getName() != null)
{
QName xsiTypeName = c.getXsiTypeName();
SchemaType xsiSchemaType =
xsiTypeName == null ? null : _schemaTypeLoader.findType( xsiTypeName );
if (requestedType == null || requestedType.isAssignableFrom( xsiSchemaType ))
type = xsiSchemaType;
}
// Look for a document element to establish type
if (type == null && (requestedType == null || requestedType.isDocumentType()))
{
assert c.isRoot();
c.push();
QName docElemName =
!c.hasAttrs() && Locale.toFirstChildElement( c ) &&
!Locale.toNextSiblingElement( c )
? c.getName() : null;
c.pop();
if (docElemName != null)
{
type = _schemaTypeLoader.findDocumentType( docElemName );
if (type != null && requestedType != null)
{
QName requesteddocElemNameName = requestedType.getDocumentElementName();
if (!requesteddocElemNameName.equals( docElemName ) &&
!requestedType.isValidSubstitution( docElemName ))
{
throw
new XmlException(
"Element " + QNameHelper.pretty( docElemName ) +
" is not a valid " + QNameHelper.pretty( requesteddocElemNameName ) +
" document or a valid substitution.");
}
}
}
}
if (type == null && requestedType == null)
{
c.push();
type =
Locale.toFirstNormalAttr( c ) && !Locale.toNextNormalAttr( c )
? _schemaTypeLoader.findAttributeType( c.getName() ) : null;
c.pop();
}
if (type == null)
type = requestedType;
if (type == null)
type = XmlBeans.NO_TYPE;
c.setType( type );
if (requestedType != null)
{
if (type.isDocumentType())
verifyDocumentType( c, type.getDocumentElementName() );
else if (type.isAttributeType())
verifyAttributeType( c, type.getAttributeTypeAttributeName() );
}
}
private boolean namespacesSame ( QName n1, QName n2 )
{
if (n1 == n2)
return true;
if (n1 == null || n2 == null)
return false;
if (n1.getNamespaceURI() == n2.getNamespaceURI())
return true;
if (n1.getNamespaceURI() == null || n2.getNamespaceURI() == null)
return false;
return n1.getNamespaceURI().equals( n2.getNamespaceURI() );
}
private void addNamespace ( StringBuffer sb, QName name )
{
if (name.getNamespaceURI() == null)
sb.append( "<no namespace>" );
else
{
sb.append( "\"" );
sb.append( name.getNamespaceURI() );
sb.append( "\"" );
}
}
private void verifyDocumentType ( Cur c, QName docElemName ) throws XmlException
{
assert c.isRoot();
c.push();
try
{
StringBuffer sb = null;
if (!Locale.toFirstChildElement( c ) || Locale.toNextSiblingElement( c ))
{
sb = new StringBuffer();
sb.append( "The document is not a " );
sb.append( QNameHelper.pretty( docElemName ) );
sb.append( c.isRoot() ? ": no document element" : ": multiple document elements" );
}
else
{
QName name = c.getName();
if (!name.equals( docElemName ))
{
sb = new StringBuffer();
sb.append( "The document is not a " );
sb.append( QNameHelper.pretty( docElemName ) );
if (docElemName.getLocalPart().equals( name.getLocalPart() ))
{
sb.append( ": document element namespace mismatch " );
sb.append( "expected " );
addNamespace( sb, docElemName );
sb.append( " got " );
addNamespace(sb, name );
}
else if (namespacesSame( docElemName, name ))
{
sb.append( ": document element local name mismatch " );
sb.append( "expected " + docElemName.getLocalPart() );
sb.append( " got " + name.getLocalPart() );
}
else
{
sb.append( ": document element mismatch " );
sb.append( "got " );
sb.append( QNameHelper.pretty( name ) );
}
}
}
if (sb != null)
{
XmlError err = XmlError.forCursor( sb.toString(), new Cursor( c ) );
throw new XmlException( err.toString(), null, err );
}
}
finally
{
c.pop();
}
}
private void verifyAttributeType ( Cur c, QName attrName ) throws XmlException
{
assert c.isRoot();
c.push();
try
{
StringBuffer sb = null;
if (!Locale.toFirstNormalAttr( c ) || Locale.toNextNormalAttr( c ))
{
sb = new StringBuffer();
sb.append( "The document is not a " );
sb.append( QNameHelper.pretty( attrName ) );
sb.append( c.isRoot() ? ": no attributes" : ": multiple attributes" );
}
else
{
QName name = c.getName();
if (!name.equals( attrName ))
{
sb = new StringBuffer();
sb.append( "The document is not a " );
sb.append( QNameHelper.pretty( attrName ) );
if (attrName.getLocalPart().equals( name.getLocalPart() ))
{
sb.append( ": attribute namespace mismatch " );
sb.append( "expected " );
addNamespace( sb, attrName );
sb.append( " got " );
addNamespace(sb, name );
}
else if (namespacesSame( attrName, name ))
{
sb.append( ": attribute local name mismatch " );
sb.append( "expected " + attrName.getLocalPart() );
sb.append( " got " + name.getLocalPart() );
}
else
{
sb.append( ": attribute element mismatch " );
sb.append( "got " );
sb.append( QNameHelper.pretty( name ) );
}
}
}
if (sb != null)
{
XmlError err = XmlError.forCursor( sb.toString(), new Cursor( c ) );
throw new XmlException( err.toString(), null, err );
}
}
finally
{
c.pop();
}
}
//
//
//
public static XmlObject newInstance (
SchemaTypeLoader stl, SchemaType type, XmlOptions options )
{
Locale l = getLocale( stl, options );
if (l.noSync()) { l.enter(); try { return l.newInstance( type, options ); } finally { l.exit(); } }
else synchronized ( l ) { l.enter(); try { return l.newInstance( type, options ); } finally { l.exit(); } }
}
private XmlObject newInstance ( SchemaType type, XmlOptions options )
{
options = XmlOptions.maskNull( options );
Cur c = tempCur();
c.createRoot();
SchemaType sType = (SchemaType) options.get( XmlOptions.DOCUMENT_TYPE );
if (sType == null)
sType = type == null ? XmlObject.type : type;
c.setType( sType );
XmlObject x = (XmlObject) c.getUser();
c.release();
return x;
}
//
//
//
public static XmlObject parseToXmlObject (
SchemaTypeLoader stl, String xmlText, SchemaType type, XmlOptions options )
throws XmlException
{
Locale l = getLocale( stl, options );
if (l.noSync()) { l.enter(); try { return l.parseToXmlObject( xmlText, type, options ); } finally { l.exit(); } }
else synchronized ( l ) { l.enter(); try { return l.parseToXmlObject( xmlText, type, options ); } finally { l.exit(); } }
}
private XmlObject parseToXmlObject ( String xmlText, SchemaType type, XmlOptions options )
throws XmlException
{
Cur c = parse( xmlText, type, options );
XmlObject x = (XmlObject) c.getUser();
c.release();
return x;
}
private Cur parse ( String xmlText, SchemaType type, XmlOptions options )
throws XmlException
{
Cur c =
getSaxLoader( options ).
load( this, new InputSource( new StringReader( xmlText ) ), options );
autoTypeDocument( c, type, options );
return c;
}
//
//
//
public static XmlObject parseToXmlObject (
SchemaTypeLoader stl, XMLInputStream xis, SchemaType type, XmlOptions options )
throws XmlException, XMLStreamException
{
throw new RuntimeException( "Not impl" );
// Root r = new Root( stl, null, options );
// r.loadXml( xis, type, options );
// return r.getObject();
}
public static XmlObject parseToXmlObject (
SchemaTypeLoader stl, XMLStreamReader xsr, SchemaType type, XmlOptions options )
throws XmlException
{
throw new RuntimeException( "Not impl" );
// Root r = new Root( stl, null, options );
// r.loadXml( xsr, type, options );
// return r.getObject();
}
public static XmlObject parseToXmlObject (
SchemaTypeLoader stl, InputStream is, SchemaType type, XmlOptions options )
throws XmlException, IOException
{
throw new RuntimeException( "Not impl" );
// Root r = new Root( stl, null, options );
// r.loadXml( is, type, options );
// return r.getObject();
}
public static XmlObject parseToXmlObject (
SchemaTypeLoader stl, Reader reader, SchemaType type, XmlOptions options )
throws XmlException, IOException
{
throw new RuntimeException( "Not impl" );
// Root r = new Root( stl, null, options );
// r.loadXml( reader, type, options );
// return r.getObject();
}
public static XmlObject parseToXmlObject (
SchemaTypeLoader stl, Node node, SchemaType type, XmlOptions options )
throws XmlException
{
throw new RuntimeException( "Not impl" );
// Root r = new Root( stl, null, options );
// r.loadXml( node, type, options );
// return r.getObject();
}
public static XmlSaxHandler newSaxHandler (
SchemaTypeLoader stl, SchemaType type, XmlOptions options )
{
throw new RuntimeException( "Not impl" );
// return new Root( stl, null, options ).newSaxHandler( type, options );
}
// TODO (ericvas ) - have a qname factory here so that the same factory may be
// used by the parser. This factory would probably come from my
// high speed parser. Otherwise, use a thread local on
QName makeQName ( String uri, String localPart )
{
assert localPart != null && localPart.length() > 0;
// TODO - make sure name is a well formed name?
return _qnameFactory.getQName( uri, localPart );
}
QName makeQName ( String uri, String local, String prefix )
{
return _qnameFactory.getQName( uri, local, prefix == null ? "" : prefix );
}
QName makeQualifiedQName ( String uri, String qname )
{
if (qname == null)
qname = "";
int i = qname.indexOf( ':' );
return i < 0
? _qnameFactory.getQName( uri, qname )
: _qnameFactory.getQName( uri, qname.substring( i + 1 ), qname.substring( 0, i ) );
}
static private class DocProps extends XmlDocumentProperties
{
private HashMap _map = new HashMap();
public Object put ( Object key, Object value ) { return _map.put( key, value ); }
public Object get ( Object key ) { return _map.get( key ); }
public Object remove ( Object key ) { return _map.remove( key ); }
}
static XmlDocumentProperties getDocProps ( Cur c, boolean ensure )
{
c.push();
while ( c.toParent() )
;
DocProps props = (DocProps) c.getBookmark( DocProps.class );
if (props == null)
c.setBookmark( DocProps.class, props = new DocProps() );
c.pop();
return props;
}
void registerForTextChange ( Cur c )
{
// The end of this list points to itself so that I can know if
// any cur is on the list by seeing if netx != null.
assert c._nextTextChangeListener == null;
if (_textChangeListeners == null)
_textChangeListeners = c._nextTextChangeListener = c;
else
{
c._nextTextChangeListener = _textChangeListeners;
_textChangeListeners = c;
}
}
interface GeneralChangeListener
{
void notifyGeneralChange ( );
void setNextGeneralChangeListener ( GeneralChangeListener listener );
GeneralChangeListener getNextGeneralChangeListener ( );
}
void registerForGeneralChange ( GeneralChangeListener listener )
{
if (listener.getNextGeneralChangeListener() == null)
{
if (_generalChangeListeners == null)
listener.setNextGeneralChangeListener( listener );
else
listener.setNextGeneralChangeListener( _generalChangeListeners );
_generalChangeListeners = listener;
}
}
void notifyGeneralChangeListeners ( )
{
while ( _generalChangeListeners != null )
{
_generalChangeListeners.notifyGeneralChange();
if (_generalChangeListeners.getNextGeneralChangeListener() == _generalChangeListeners)
_generalChangeListeners.setNextGeneralChangeListener( null );
GeneralChangeListener next = _generalChangeListeners.getNextGeneralChangeListener();
_generalChangeListeners.setNextGeneralChangeListener( null );
_generalChangeListeners = next;
}
}
void notifyTextChangeListeners ( )
{
while ( _textChangeListeners != null )
{
_textChangeListeners.textChangeNotification();
if (_textChangeListeners._nextTextChangeListener == _textChangeListeners)
_textChangeListeners._nextTextChangeListener = null;
_textChangeListeners = _textChangeListeners._nextTextChangeListener;
}
}
//
// Cursor helpers
//
static String getTextValue ( Cur c, int wsr )
{
assert c.isNode();
if (!c.hasChildren())
return c.getValueAsString( wsr );
ScrubBuffer sb = getScrubBuffer( wsr );
c.push();
for ( c.next() ; !c.isAtEndOfLastPush() ; c.next() )
if (c.isText())
sb.scrub( c.getChars( -1 ), c._offSrc, c._cchSrc );
c.pop();
return sb.getResultAsString();
}
static String applyWhiteSpaceRule ( String s, int wsr )
{
int l = s.length();
if (l == 0)
return s;
char ch;
if (wsr == Locale.WS_REPLACE)
{
for ( int i = 0 ; i < l ; i++ )
if ((ch = s.charAt( i )) == '\n' || ch == '\r' || ch == '\t')
return processWhiteSpaceRule( s, wsr );
}
else if (wsr == Locale.WS_COLLAPSE)
{
if (CharUtil.isWhiteSpace( s.charAt( 0 ) ) || CharUtil.isWhiteSpace( s.charAt( l - 1 )))
return processWhiteSpaceRule( s, wsr );
boolean lastWasWhite = false;
for ( int i = 1 ; i < l ; i++ )
{
boolean isWhite = CharUtil.isWhiteSpace( s.charAt( i ) );
if (isWhite && lastWasWhite)
return processWhiteSpaceRule( s, wsr );
lastWasWhite = isWhite;
}
}
return s;
}
static String processWhiteSpaceRule ( String s, int wsr )
{
ScrubBuffer sb = getScrubBuffer( wsr );
sb.scrub( s, 0, s.length() );
return sb.getResultAsString();
}
private static final class ScrubBuffer
{
void init ( int wsr )
{
assert wsr == Locale.WS_REPLACE || wsr == Locale.WS_COLLAPSE;
_dstOff = 0;
_replace = wsr == Locale.WS_REPLACE;
}
void scrub ( Object src, int off, int cch )
{
char[] chars;
if (cch == 0)
return;
if (src instanceof char[])
chars = (char[]) src;
else if (cch < _srcBuf.length)
chars = _srcBuf;
else if (cch < 16384)
chars = _srcBuf = new char [ 16384 ];
else
chars = new char [ cch ];
if (src != chars)
{
CharUtil.getChars( chars, 0, src, off, cch );
off = 0;
}
char ch;
while ( cch > 0 && CharUtil.isWhiteSpace( chars[ off ] ) )
{ off++; cch--; }
while ( cch > 0 && CharUtil.isWhiteSpace( chars[ off + cch - 1 ] ) )
cch--;
boolean lastWasWhite = _dstOff > 0 && CharUtil.isWhiteSpace( _dstBuf[ _dstOff ] );
while ( cch-- > 0 )
{
ch = chars[ off++ ];
if (CharUtil.isWhiteSpace( ch ))
{
if (_replace || !lastWasWhite)
_dstBuf[ _dstOff++ ] = ' ';
lastWasWhite = true;
}
else
{
_dstBuf[ _dstOff++ ] = ch;
lastWasWhite = false;
}
}
}
String getResultAsString ( )
{
return new String( _dstBuf, 0, _dstOff );
}
private static final int START_STATE = 0;
private static final int SPACE_SEEN_STATE = 1;
private static final int NOSPACE_STATE = 2;
private boolean _replace;
private char[] _srcBuf = new char [ 1024 ];
private char[] _dstBuf = new char [ 1024 ];
private int _dstOff;
}
private static ThreadLocal tl_scrubBuffer =
new ThreadLocal ( ) { protected Object initialValue ( ) { return new ScrubBuffer(); } };
static ScrubBuffer getScrubBuffer ( int wsr )
{
ScrubBuffer sb = (ScrubBuffer) tl_scrubBuffer.get();
sb.init( wsr );
return sb;
}
static final int scrubText(
Object src, int off, int cch, int wsRule, StringBuffer sb, int state )
{
char[] chars = cch < 1024 ? (char[]) tl_scrubBuffer.get() : new char [ cch ];
if (chars.length > 1024 && cch < 16384)
tl_scrubBuffer.set( chars );
CharUtil.getChars( chars, 0, src, off, cch );
throw new RuntimeException( "Not impl" );
// assert text != null;
//
// if (text._buf == null)
// {
// assert cch == 0;
// assert cp == 0;
// return state;
// }
//
// if (cch == 0)
// return state;
//
// boolean replace = false;
// boolean collapse = false;
//
// switch ( ws )
// {
// case TypeStore.WS_UNSPECIFIED : break;
// case TypeStore.WS_PRESERVE : break;
// case TypeStore.WS_REPLACE : replace = true; break;
// case TypeStore.WS_COLLAPSE : collapse = replace = true; break;
//
// default : assert false: "Unknown white space rule " +ws;
// }
//
// if (!replace && !collapse)
// {
// text.fetch(sb, cp, cch);
// return state;
// }
//
// int off = text.unObscure( cp, cch );
// int startpt = 0;
//
// for ( int i = 0 ; i < cch ; i++ )
// {
// char ch = text._buf[ off + i ];
//
// if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t')
// {
// sb.append(text._buf, off + startpt, i - startpt);
// startpt = i + 1;
//
// if (collapse)
// {
// if (state == NOSPACE_STATE)
// state = SPACE_SEEN_STATE;
// }
// else
// sb.append(' ');
// }
// else
// {
// if (state == SPACE_SEEN_STATE)
// sb.append( ' ' );
//
// state = NOSPACE_STATE;
// }
// }
//
// sb.append( text._buf, off + startpt, cch - startpt );
//
// return state;
}
static boolean pushToContainer ( Cur c )
{
c.push();
for ( ; ; )
{
switch ( c.kind() )
{
case ROOT : case ELEM : return true;
case - ROOT : case - ELEM : c.pop(); return false;
case COMMENT : case PROCINST : c.skip(); break;
default : c.nextWithAttrs(); break;
}
}
}
static boolean toFirstNormalAttr ( Cur c )
{
c.push();
if (c.toFirstAttr())
{
do
{
if (!c.isXmlns())
{
c.popButStay();
return true;
}
}
while ( c.toNextAttr() );
}
c.pop();
return false;
}
static boolean toNextNormalAttr ( Cur c )
{
c.push();
while ( c.toNextAttr() )
{
if (!c.isXmlns())
{
c.popButStay();
return true;
}
}
c.pop();
return false;
}
static boolean toFirstChildElement ( Cur c )
{
if (!pushToContainer( c ))
return false;
if (!c.toFirstChild() || (!c.isElem() && !toNextSiblingElement( c )))
{
c.pop();
return false;
}
c.popButStay();
return true;
}
static boolean toNextSiblingElement ( Cur c )
{
if (!c.hasParent())
return false;
c.push();
int k = c.kind();
if (k == ATTR)
c.toParent();
else if (k == ELEM)
c.skip();
while ( (k = c.kind()) > 0 )
{
if (k == ELEM)
{
c.popButStay();
return true;
}
if (k > 0)
c.toEnd();
c.next();
}
c.pop();
return false;
}
static boolean toChild ( Cur c, String uri, String local, int i )
{
return toChild( c, c._locale.makeQName( uri, local ), i );
}
static boolean toChild ( Cur c, QName name, int i )
{
// if (!c.pushToContainer())
// return false;
//
throw new RuntimeException( "Not implemented" );
// c.pop....
}
// private final class NthChildCache
// {
// private boolean namesSame ( QName pattern, QName name )
// {
// return pattern == null || pattern.equals( name );
// }
//
// private boolean setsSame ( QNameSet patternSet, QNameSet set)
// {
// // value equality is probably too expensive. Since the use case
// // involves QNameSets that are generated by the compiler, we
// // can use identity comparison.
// return patternSet != null && patternSet == set;
// }
//
// private boolean nameHit(QName namePattern, QNameSet setPattern, QName name)
// {
// if (setPattern == null)
// return namesSame(namePattern, name);
// else
// return setPattern.contains(name);
// }
//
// private boolean cacheSame (QName namePattern, QNameSet setPattern)
// {
// return setPattern == null ? namesSame(namePattern, _name) :
// setsSame(setPattern, _set);
// }
//
// int distance ( Splay parent, QName name, QNameSet set, int n )
// {
// assert n >= 0;
//
// if (_version != Root.this.getVersion())
// return Integer.MAX_VALUE - 1;
//
// if (parent != _parent || !cacheSame(name, set))
// return Integer.MAX_VALUE;
//
// return n > _n ? n - _n : _n - n;
// }
//
// Begin fetch ( Splay parent, QName name, QNameSet set, int n )
// {
// assert n >= 0;
//
// if (_version != Root.this.getVersion() || _parent != parent ||
// ! cacheSame(name, set) || n == 0)
// {
// _version = Root.this.getVersion();
// _parent = parent;
// _name = name;
// _child = null;
// _n = -1;
//
// if (!parent.isLeaf())
// {
// loop:
// for ( Splay s = parent.nextSplay() ; ; s = s.nextSplay() )
// {
// switch ( s.getKind() )
// {
// case END :
// case ROOT : break loop;
//
// case BEGIN :
// if (nameHit( name, set, s.getName() ))
// {
// _child = s;
// _n = 0;
// break loop;
// }
//
// s = s.getFinishSplay();
// break;
// }
// }
// }
// }
//
// if (_n < 0)
// return null;
//
// if (n > _n)
// {
// while ( n > _n )
// {
// for ( Splay s = _child.getFinishSplay().nextSplay() ; ;
// s = s.nextSplay() )
// {
// if (s.isFinish())
// return null;
//
// if (s.isBegin())
// {
// if (nameHit( name, set, s.getName() ))
// {
// _child = s;
// _n++;
// break;
// }
//
// s = s.getFinishSplay();
// }
// }
// }
// }
// else if (n < _n)
// {
// while ( n < _n )
// {
// Splay s = _child;
//
// for ( ; ; )
// {
// s = s.prevSplay();
//
// if (s.isLeaf() || s.isEnd())
// {
// if (s.isEnd())
// s = s.getContainer();
//
// if (nameHit( name, set, s.getName() ))
// {
// _child = s;
// _n--;
// break;
// }
// }
// else if (s.isContainer())
// return null;
// }
// }
// }
//
// return (Begin) _child;
// }
//
// private long _version;
// private Splay _parent;
// private QName _name;
// private QNameSet _set;
//
// private Splay _child;
// private int _n;
// }
//
//
//
long version ( )
{
return _versionAll;
}
Cur permCur ( )
{
return getCur( Cur.PERM );
}
Cur weakCur ( Object o )
{
assert o != null && !(o instanceof Ref);
Cur c = getCur( Cur.WEAK );
assert c._value == null;
c._value = new Ref( c, o );
return c;
}
final ReferenceQueue refQueue ( )
{
if (_refQueue == null)
_refQueue = new ReferenceQueue();
return _refQueue;
}
final static class Ref extends PhantomReference
{
Ref ( Cur c, Object obj )
{
super( obj, c._locale.refQueue() );
_cur = c;
}
final Cur _cur;
}
Cur tempCur ( )
{
Cur c = getCur( Cur.TEMP );
if (c._tempFrame < 0)
{
assert _numTempFramesLeft < _tempFrames.length;
int frame = _tempFrames.length - _numTempFramesLeft - 1;
assert frame >= 0 && frame < _tempFrames.length;
c._nextTemp = _tempFrames[ frame ];
_tempFrames[ frame ] = c;
c._tempFrame = frame;
}
return c;
}
void unregisterCurs ( )
{
while ( _registered != null )
_registered.embed();
assert _registered == null;
}
private Cur getCur ( int curKind )
{
assert curKind == Cur.TEMP || curKind == Cur.PERM || curKind == Cur.WEAK;
assert _curPool == null || _curPoolCount > 0;
Cur c;
if (_curPool == null)
{
c = new Cur( this );
c._tempFrame = -1;
}
else
{
c = _curPool;
_curPool = c.listRemove( _curPool );
c._state = Cur.UNREGISTERED;
_curPoolCount--;
}
assert c._state == Cur.UNREGISTERED;
assert c._prev == null && c._next == null;
assert !c.isPositioned();
assert c._value == null;
c._curKind = curKind;
return c;
}
TextNode createTextNode ( )
{
return _saaj == null ? new TextNode( this ) : new SaajTextNode( this );
}
CdataNode createCdataNode ( )
{
return _saaj == null ? new CdataNode( this ) : new SaajCdataNode( this );
}
boolean entered ( )
{
return _tempFrames.length - _numTempFramesLeft > 0;
}
public void enter ( Locale otherLocale )
{
enter();
if (otherLocale != this)
otherLocale.enter();
}
public void enter ( )
{
assert _numTempFramesLeft >= 0;
if (--_numTempFramesLeft <= 0)
{
Cur[] newTempFrames = new Cur [ (_numTempFramesLeft = _tempFrames.length) * 2 ];
System.arraycopy( _tempFrames, 0, newTempFrames, 0, _tempFrames.length );
_tempFrames = newTempFrames;
}
if (++_entryCount > 1000)
{
_entryCount = 0;
if (_refQueue != null)
{
for ( ; ; )
{
Ref ref = (Ref) _refQueue.poll();
if (ref == null)
break;
ref._cur.release();
}
}
}
}
public void exit ( Locale otherLocale )
{
exit();
if (otherLocale != this)
otherLocale.exit();
}
public void exit ( )
{
assert _numTempFramesLeft >= 0;
int frame = _tempFrames.length - ++_numTempFramesLeft;
Cur c = _tempFrames [ frame ];
_tempFrames [ frame ] = null;
while ( c != null )
{
assert c._tempFrame == frame;
Cur next = c._nextTemp;
c._nextTemp = null;
c._tempFrame = -1;
c.release();
c = next;
}
}
//
//
//
public boolean noSync ( )
{
return _noSync;
}
public boolean sync ( )
{
return !_noSync;
}
static final boolean isWhiteSpace ( String s )
{
int l = s.length();
while ( l-- > 0)
if (!CharUtil.isWhiteSpace( s.charAt( l )))
return false;
return true;
}
static final boolean isWhiteSpace ( StringBuffer sb )
{
int l = sb.length();
while ( l-- > 0)
if (!CharUtil.isWhiteSpace( sb.charAt( l )))
return false;
return true;
}
static boolean beginsWithXml ( String name )
{
if (name.length() < 3)
return false;
char ch;
if (((ch = name.charAt( 0 )) == 'x' || ch == 'X') &&
((ch = name.charAt( 1 )) == 'm' || ch == 'M') &&
((ch = name.charAt( 2 )) == 'l' || ch == 'L'))
{
return true;
}
return false;
}
static boolean isXmlns ( QName name )
{
String prefix = name.getPrefix();
if (prefix.equals( "xmlns" ))
return true;
return prefix.length() == 0 && name.getLocalPart().equals( "xmlns" );
}
QName createXmlns ( String prefix )
{
if (prefix == null)
prefix = "";
return
prefix.length() == 0
? makeQName( _xmlnsUri, "xmlns", "" )
: makeQName( _xmlnsUri, prefix, "xmlns" );
}
static String xmlnsPrefix ( QName name )
{
return name.getPrefix().equals( "xmlns" ) ? name.getLocalPart() : "";
}
//
// Loading/parsing
//
static abstract class LoadContext
{
protected abstract void startElement ( QName name );
protected abstract void endElement ( );
protected abstract void attr ( String local, String uri, String prefix,
String value );
protected abstract void comment ( char[] buff, int off, int cch );
protected abstract void procInst ( String target, String value );
protected abstract void text ( char[] buff, int off, int cch );
protected abstract Cur finish ( );
}
private static ThreadLocal tl_saxLoadersDefaultResolver =
new ThreadLocal ( ) { protected Object initialValue ( ) { return newSaxLoader(); } };
private static ThreadLocal tl_saxLoaders =
new ThreadLocal ( ) { protected Object initialValue ( ) { return newSaxLoader(); } };
private static SaxLoader getSaxLoader ( XmlOptions options )
{
// XMLReader.setEntityResolver() cannot be passed null.
// Because of this, I cannot reset the entity resolver to be
// that which was default. Thus, I need to cache two
// SaxLoaders. One which uses the default entity resolver and
// one which I can change the resolve to whatever I want for a
// given parse.
options = XmlOptions.maskNull( options );
EntityResolver er = (EntityResolver) options.get( XmlOptions.ENTITY_RESOLVER );
if (er == null)
er = ResolverUtil.getGlobalEntityResolver();
if (er == null && options.hasOption( XmlOptions.LOAD_USE_DEFAULT_RESOLVER ))
return (SaxLoader) tl_saxLoadersDefaultResolver.get();
SaxLoader sl = (SaxLoader) tl_saxLoaders.get();
if (er == null)
er = sl;
sl.setEntityResolver( er );
return sl;
}
private static SaxLoader newSaxLoader ( )
{
SaxLoader sl = null;
try
{
sl = PiccoloSaxLoader.newInstance();
if (sl == null)
sl = DefaultSaxLoader.newInstance();
}
catch ( Exception e )
{
throw new RuntimeException( "Can't find an XML parser", e );
}
if (sl == null)
throw new RuntimeException( "Can't find an XML parser" );
return sl;
}
private static class DefaultSaxLoader extends SaxLoader
{
private DefaultSaxLoader ( XMLReader xr )
{
super( xr, null );
}
static SaxLoader newInstance ( ) throws Exception
{
return
new DefaultSaxLoader(
SAXParserFactory.newInstance().newSAXParser().getXMLReader() );
}
}
private static class PiccoloSaxLoader extends SaxLoader
{
// TODO - Need to look at root.java to bring this loader up to
// date with all needed features
private PiccoloSaxLoader (
XMLReader xr, Locator startLocator, Method m_getEncoding, Method m_getVersion )
{
super( xr, startLocator );
_m_getEncoding = m_getEncoding;
_m_getVersion = m_getVersion;
}
static SaxLoader newInstance ( ) throws Exception
{
Class pc = null;
try
{
pc = Class.forName( "com.bluecast.xml.Piccolo" );
}
catch ( ClassNotFoundException e )
{
return null;
}
XMLReader xr = (XMLReader) pc.newInstance();
Method m_getEncoding = pc.getMethod( "getEncoding", null );
Method m_getVersion = pc.getMethod( "getVersion", null );
Method m_getStartLocator = pc.getMethod( "getStartLocator", null );
Locator startLocator =
(Locator) m_getStartLocator.invoke( xr, null );
return new PiccoloSaxLoader( xr, startLocator, m_getEncoding, m_getVersion );
}
private Method _m_getEncoding;
private Method _m_getVersion;
}
private static abstract class SaxLoader
implements ContentHandler, LexicalHandler, ErrorHandler, EntityResolver
{
SaxLoader ( XMLReader xr, Locator startLocator )
{
_xr = xr;
_startLocator = startLocator;
try
{
_xr.setFeature( "http://xml.org/sax/features/namespace-prefixes", true );
_xr.setFeature( "http://xml.org/sax/features/namespaces", true );
_xr.setFeature( "http://xml.org/sax/features/validation", false );
_xr.setProperty( "http://xml.org/sax/properties/lexical-handler", this );
_xr.setContentHandler( this );
_xr.setErrorHandler( this );
}
catch ( Throwable e )
{
throw new RuntimeException( e.getMessage(), e );
}
}
void setEntityResolver ( EntityResolver er )
{
_xr.setEntityResolver( er );
}
public Cur load ( Locale l, InputSource is, XmlOptions options )
{
_locale = l;
_context = new Cur.CurLoadContext( _locale, options );
try
{
_xr.parse( is );
}
catch ( Exception e )
{
throw new RuntimeException( e.getMessage(), e );
}
return _context.finish();
}
public void setDocumentLocator ( Locator locator )
{
// TODO - hook up locator ...
}
public void startDocument ( ) throws SAXException
{
// Do nothing ... start of document is implicit
}
public void endDocument ( ) throws SAXException
{
// Do nothing ... end of document is implicit
}
public void startElement ( String uri, String local, String qName, Attributes atts )
throws SAXException
{
if (local.length() == 0)
local = qName;
// Out current parser (Piccolo) does not error when a
// namespace is used and not defined. Check for these here
if (qName.indexOf( ':' ) >= 0 && uri.length() == 0)
{
XmlError err =
XmlError.forMessage(
"Use of undefined namespace prefix: " +
qName.substring( 0, qName.indexOf( ':' ) ));
throw new XmlRuntimeException( err.toString(), null, err );
}
_context.startElement( _locale.makeQualifiedQName( uri, qName ) );
for ( int i = 0, len = atts.getLength() ; i < len ; i++ )
{
String aqn = atts.getQName( i );
if (aqn.equals( "xmlns" ))
{
_context.attr( "xmlns", _xmlnsUri, null, atts.getValue( i ) );
}
else if (aqn.startsWith( "xmlns:" ))
{
String prefix = aqn.substring( 6 );
if (prefix.length() == 0)
{
XmlError err =
XmlError.forMessage( "Prefix not specified", XmlError.SEVERITY_ERROR );
throw new XmlRuntimeException( err.toString(), null, err );
}
String attrUri = atts.getValue( i );
if (attrUri.length() == 0)
{
XmlError err =
XmlError.forMessage(
"Prefix can't be mapped to no namespace: " + prefix,
XmlError.SEVERITY_ERROR );
throw new XmlRuntimeException( err.toString(), null, err );
}
_context.attr( prefix, _xmlnsUri, "xmlns", attrUri );
}
else
{
int colon = aqn.indexOf( ':' );
if (colon < 0)
_context.attr( aqn, atts.getURI( i ), null, atts.getValue( i ) );
else
{
_context.attr(
aqn.substring( colon + 1 ), atts.getURI( i ), aqn.substring( 0, colon ),
atts.getValue( i ) );
}
}
}
}
public void endElement ( String namespaceURI, String localName, String qName )
throws SAXException
{
_context.endElement();
}
public void characters ( char ch[], int start, int length ) throws SAXException
{
_context.text( ch, start, length );
}
public void ignorableWhitespace ( char ch[], int start, int length ) throws SAXException
{
}
public void comment ( char ch[], int start, int length ) throws SAXException
{
_context.comment( ch, start, length );
}
public void processingInstruction ( String target, String data ) throws SAXException
{
_context.procInst( target, data );
}
public void startDTD ( String name, String publicId, String systemId ) throws SAXException
{
}
public void endDTD ( ) throws SAXException
{
}
public void startPrefixMapping ( String prefix, String uri ) throws SAXException
{
if (beginsWithXml( prefix ) && ! ( "xml".equals( prefix ) && _xml1998Uri.equals( uri )))
{
XmlError err =
XmlError.forMessage(
"Prefix can't begin with XML: " + prefix, XmlError.SEVERITY_ERROR );
throw new XmlRuntimeException( err.toString(), null, err );
}
}
public void endPrefixMapping ( String prefix ) throws SAXException
{
}
public void skippedEntity ( String name ) throws SAXException
{
// throw new RuntimeException( "Not impl: skippedEntity" );
}
public void startCDATA ( ) throws SAXException
{
}
public void endCDATA ( ) throws SAXException
{
}
public void startEntity ( String name ) throws SAXException
{
// throw new RuntimeException( "Not impl: startEntity" );
}
public void endEntity ( String name ) throws SAXException
{
// throw new RuntimeException( "Not impl: endEntity" );
}
public void fatalError ( SAXParseException e ) throws SAXException
{
throw e;
}
public void error ( SAXParseException e ) throws SAXException
{
throw e;
}
public void warning ( SAXParseException e ) throws SAXException
{
throw e;
}
public InputSource resolveEntity ( String publicId, String systemId )
{
return new InputSource( new StringReader( "" ) );
}
private Locale _locale;
private XMLReader _xr;
private LoadContext _context;
private Locator _startLocator;
}
private Dom load ( InputSource is, XmlOptions options )
{
return getSaxLoader( options ).load( this, is, options ).getDom();
}
public Dom load ( Reader r )
{
return load( new InputSource( r ), null );
}
public Dom load ( String s )
{
return load( new InputSource( new StringReader( s ) ), null );
}
public Dom load ( InputStream in )
{
return load( in, null );
}
public Dom load ( InputStream in, XmlOptions options )
{
return load( new InputSource( in ), options );
}
public Dom load ( String s, XmlOptions options )
{
return load( new InputSource( new StringReader( s ) ), options );
}
//
// DOMImplementation methods
//
public Document createDocument ( String uri, String qname, DocumentType doctype )
{
return DomImpl._domImplementation_createDocument( this, uri, qname, doctype );
}
public DocumentType createDocumentType ( String qname, String publicId, String systemId )
{
throw new RuntimeException( "Not implemented" );
// return DomImpl._domImplementation_createDocumentType( this, qname, publicId, systemId );
}
public boolean hasFeature ( String feature, String version )
{
return DomImpl._domImplementation_hasFeature( this, feature, version );
}
public Object getFeature ( String feature, String version )
{
throw new RuntimeException( "DOM Level 3 Not implemented" );
}
//
// SaajCallback methods
//
public void setSaajData ( Node n, Object o )
{
assert n instanceof Dom;
DomImpl.saajCallback_setSaajData( (Dom) n, o );
}
public Object getSaajData ( Node n )
{
assert n instanceof Dom;
return DomImpl.saajCallback_getSaajData( (Dom) n );
}
public Element createSoapElement ( QName name, QName parentName )
{
assert _ownerDoc != null;
return DomImpl.saajCallback_createSoapElement( _ownerDoc, name, parentName );
}
public Element importSoapElement ( Document doc, Element elem, boolean deep, QName parentName )
{
assert doc instanceof Dom;
return DomImpl.saajCallback_importSoapElement( (Dom) doc, elem, deep, parentName );
}
private static final class DefaultQNameFactory implements QNameFactory
{
private QNameCache _cache = XmlBeans.getQNameCache();
public QName getQName ( String uri, String local )
{
return _cache.getName( uri, local, "" );
}
public QName getQName ( String uri, String local, String prefix )
{
return _cache.getName( uri, local, prefix );
}
public QName getQName (
char[] uriSrc, int uriPos, int uriCch,
char[] localSrc, int localPos, int localCch )
{
return
_cache.getName(
new String( uriSrc, uriPos, uriCch ),
new String( localSrc, localPos, localCch ),
"" );
}
public QName getQName (
char[] uriSrc, int uriPos, int uriCch,
char[] localSrc, int localPos, int localCch,
char[] prefixSrc, int prefixPos, int prefixCch )
{
return
_cache.getName(
new String( uriSrc, uriPos, uriCch ),
new String( localSrc, localPos, localCch ),
new String( prefixSrc, prefixPos, prefixCch ) );
}
}
//
//
//
boolean _noSync;
SchemaTypeLoader _schemaTypeLoader;
private ReferenceQueue _refQueue;
private int _entryCount;
private int _numTempFramesLeft;
private Cur[] _tempFrames;
Cur _curPool;
int _curPoolCount;
Cur _registered;
GeneralChangeListener _generalChangeListeners;
Cur _textChangeListeners;
long _versionAll;
long _versionSansText;
Locations _locations;
CharUtil _charUtil;
Saaj _saaj;
Dom _ownerDoc;
QNameFactory _qnameFactory;
boolean _validateOnSet;
}