| /* Copyright 2004-2018 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.xmlbeans.impl.store; |
| |
| import org.apache.xmlbeans.*; |
| import org.apache.xmlbeans.XmlCursor.XmlBookmark; |
| import org.apache.xmlbeans.impl.common.*; |
| import org.apache.xmlbeans.impl.store.Cur.Locations; |
| import org.apache.xmlbeans.impl.store.DomImpl.Dom; |
| import org.apache.xmlbeans.impl.store.Saaj.SaajCallback; |
| import org.apache.xmlbeans.impl.values.TypeStore; |
| import org.apache.xmlbeans.xml.stream.CharacterData; |
| import org.apache.xmlbeans.xml.stream.ProcessingInstruction; |
| import org.apache.xmlbeans.xml.stream.*; |
| import org.w3c.dom.*; |
| import org.xml.sax.*; |
| import org.xml.sax.ext.DeclHandler; |
| import org.xml.sax.ext.LexicalHandler; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.XMLStreamReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.lang.ref.PhantomReference; |
| import java.lang.ref.ReferenceQueue; |
| import java.lang.ref.SoftReference; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| public final class Locale |
| implements DOMImplementation, SaajCallback, XmlLocale |
| { |
| private static final XBLogger logger = XBLogFactory.getLogger(Locale.class); |
| |
| 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 = options.hasOption(XmlOptions.UNSYNCHRONIZED); |
| |
| _tempFrames = new Cur[_numTempFramesLeft = 8]; |
| |
| // BUGBUG - this cannot be thread local .... |
| // BUGBUG - this cannot be thread local .... |
| // BUGBUG - this cannot be thread local .... uhh what, again? |
| // |
| // 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 |
| |
| _qnameFactory = new DefaultQNameFactory(); //new LocalDocumentQNameFactory(); |
| |
| _locations = new Locations(this); |
| |
| _schemaTypeLoader = stl; |
| |
| _validateOnSet = options.hasOption(XmlOptions.VALIDATE_ON_SET); |
| |
| // |
| // Check for Saaj implementation request |
| // |
| |
| Object saajObj = options.get(Saaj.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"; |
| /** |
| * This option is checked in XmlObjectBase._copy(XmlOptions), the locale is used as the synchronization domain. |
| * useNewLocale = true: copy will use a new locale, false: copy will use the same locale as the source |
| * @deprecated Replace usages with CopyUseNewSynchronizationDomain option |
| * @see org.apache.xmlbeans.XmlOptions#setCopyUseNewSynchronizationDomain(boolean) |
| */ |
| public static final String COPY_USE_NEW_LOCALE = "COPY_USE_NEW_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(Saaj.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; |
| } |
| |
| // |
| // |
| // |
| |
| static void associateSourceName(Cur c, XmlOptions options) |
| { |
| String sourceName = (String) XmlOptions.safeGet(options, |
| XmlOptions.DOCUMENT_SOURCE_NAME); |
| |
| if (sourceName != null) |
| getDocProps(c, true).setSourceName(sourceName); |
| } |
| |
| // |
| // |
| // |
| |
| static 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 : c._locale._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 = |
| c._locale._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) |
| ? |
| c._locale._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 static 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 static void addNamespace(StringBuffer sb, QName name) |
| { |
| if (name.getNamespaceURI() == null) |
| sb.append("<no namespace>"); |
| else |
| { |
| sb.append("\""); |
| sb.append(name.getNamespaceURI()); |
| sb.append("\""); |
| } |
| } |
| |
| private static 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 static 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(); |
| } |
| } |
| |
| static boolean isFragmentQName(QName name) |
| { |
| return name.equals(Locale._openuriFragment) || |
| name.equals(Locale._xmlFragment); |
| } |
| |
| static boolean isFragment(Cur start, Cur end) |
| { |
| assert !end.isAttr(); |
| |
| start.push(); |
| end.push(); |
| |
| int numDocElems = 0; |
| boolean isFrag = false; |
| |
| while (!start.isSamePos(end)) |
| { |
| int k = start.kind(); |
| |
| if (k == ATTR) |
| break; |
| |
| if (k == TEXT && !isWhiteSpace(start.getCharsAsString())) |
| { |
| isFrag = true; |
| break; |
| } |
| |
| if (k == ELEM && ++numDocElems > 1) |
| { |
| isFrag = true; |
| break; |
| } |
| |
| // Move to next token |
| |
| assert k != ATTR; |
| |
| if (k != TEXT) |
| start.toEnd(); |
| |
| start.next(); |
| } |
| |
| start.pop(); |
| end.pop(); |
| |
| return isFrag || numDocElems != 1; |
| } |
| |
| // |
| // |
| // |
| |
| 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(); |
| |
| |
| SchemaType sType = (SchemaType) options.get(XmlOptions.DOCUMENT_TYPE); |
| |
| if (sType == null) |
| sType = type == null ? XmlObject.type : type; |
| if (sType.isDocumentType()) |
| c.createDomDocumentRoot(); |
| else |
| c.createRoot(); |
| c.setType(sType); |
| |
| XmlObject x = (XmlObject) c.getUser(); |
| |
| c.release(); |
| |
| return x; |
| } |
| |
| // |
| // |
| // |
| |
| public static DOMImplementation newDomImplementation(SchemaTypeLoader stl, |
| XmlOptions options) |
| { |
| return (DOMImplementation) getLocale(stl, options); |
| } |
| |
| // |
| // |
| // |
| |
| 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; |
| } |
| |
| Cur parse(String s, SchemaType type, XmlOptions options) |
| throws XmlException |
| { |
| Reader r = new StringReader(s); |
| |
| try |
| { |
| Cur c = getSaxLoader(options).load(this, new InputSource(r), |
| options); |
| |
| autoTypeDocument(c, type, options); |
| |
| return c; |
| } |
| catch (IOException e) |
| { |
| assert false: "StringReader should not throw IOException"; |
| |
| throw new XmlException(e.getMessage(), e); |
| } |
| finally |
| { |
| try |
| { |
| r.close(); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| } |
| |
| // |
| // |
| // |
| |
| /** |
| * @deprecated XMLInputStream was deprecated by XMLStreamReader from STaX - jsr173 API. |
| */ |
| public static XmlObject parseToXmlObject(SchemaTypeLoader stl, |
| XMLInputStream xis, SchemaType type, XmlOptions options) |
| throws XmlException, org.apache.xmlbeans.xml.stream.XMLStreamException |
| { |
| Locale l = getLocale(stl, options); |
| |
| if (l.noSync()) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(xis, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| else |
| synchronized (l) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(xis, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| } |
| |
| /** |
| * @deprecated XMLInputStream was deprecated by XMLStreamReader from STaX - jsr173 API. |
| */ |
| public XmlObject parseToXmlObject(XMLInputStream xis, SchemaType type, |
| XmlOptions options) |
| throws XmlException, org.apache.xmlbeans.xml.stream.XMLStreamException |
| { |
| Cur c; |
| |
| try |
| { |
| c = loadXMLInputStream(xis, options); |
| } |
| catch (org.apache.xmlbeans.xml.stream.XMLStreamException e) |
| { |
| throw new XmlException(e.getMessage(), e); |
| } |
| |
| autoTypeDocument(c, type, options); |
| |
| XmlObject x = (XmlObject) c.getUser(); |
| |
| c.release(); |
| |
| return x; |
| } |
| |
| // |
| // |
| // |
| |
| public static XmlObject parseToXmlObject(SchemaTypeLoader stl, |
| XMLStreamReader xsr, SchemaType type, XmlOptions options) |
| throws XmlException |
| { |
| Locale l = getLocale(stl, options); |
| |
| if (l.noSync()) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(xsr, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| else |
| synchronized (l) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(xsr, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| } |
| |
| public XmlObject parseToXmlObject(XMLStreamReader xsr, SchemaType type, |
| XmlOptions options) |
| throws XmlException |
| { |
| Cur c; |
| |
| try |
| { |
| c = loadXMLStreamReader(xsr, options); |
| } |
| catch (XMLStreamException e) |
| { |
| throw new XmlException(e.getMessage(), e); |
| } |
| |
| autoTypeDocument(c, type, options); |
| |
| XmlObject x = (XmlObject) c.getUser(); |
| |
| c.release(); |
| |
| return x; |
| } |
| |
| private static void lineNumber(XMLEvent xe, LoadContext context) |
| { |
| org.apache.xmlbeans.xml.stream.Location loc = xe.getLocation(); |
| |
| if (loc != null) |
| context.lineNumber(loc.getLineNumber(), loc.getColumnNumber(), -1); |
| } |
| |
| private static void lineNumber(XMLStreamReader xsr, LoadContext context) |
| { |
| javax.xml.stream.Location loc = xsr.getLocation(); |
| |
| if (loc != null) |
| { |
| context.lineNumber(loc.getLineNumber(), loc.getColumnNumber(), |
| loc.getCharacterOffset()); |
| } |
| } |
| |
| private void doAttributes(XMLStreamReader xsr, LoadContext context) |
| { |
| int n = xsr.getAttributeCount(); |
| |
| for (int a = 0; a < n; a++) |
| { |
| context.attr(xsr.getAttributeLocalName(a), |
| xsr.getAttributeNamespace(a), |
| xsr.getAttributePrefix(a), |
| xsr.getAttributeValue(a)); |
| } |
| } |
| |
| private void doNamespaces(XMLStreamReader xsr, LoadContext context) |
| { |
| int n = xsr.getNamespaceCount(); |
| |
| for (int a = 0; a < n; a++) |
| { |
| String prefix = xsr.getNamespacePrefix(a); |
| |
| if (prefix == null || prefix.length() == 0) |
| context.attr("xmlns", _xmlnsUri, null, |
| xsr.getNamespaceURI(a)); |
| else |
| context.attr(prefix, _xmlnsUri, "xmlns", |
| xsr.getNamespaceURI(a)); |
| } |
| |
| } |
| |
| /** |
| * @deprecated XMLInputStream was deprecated by XMLStreamReader from STaX - jsr173 API. |
| */ |
| private Cur loadXMLInputStream(XMLInputStream xis, XmlOptions options) |
| throws org.apache.xmlbeans.xml.stream.XMLStreamException |
| { |
| options = XmlOptions.maskNull(options); |
| |
| boolean lineNums = options.hasOption(XmlOptions.LOAD_LINE_NUMBERS); |
| |
| XMLEvent x = xis.peek(); |
| |
| if (x != null && x.getType() == XMLEvent.START_ELEMENT) |
| { |
| Map nsMap = ((StartElement) x).getNamespaceMap(); |
| |
| if (nsMap != null && nsMap.size() > 0) |
| { |
| Map namespaces = new HashMap(); |
| |
| namespaces.putAll(nsMap); |
| |
| options = new XmlOptions(options); |
| |
| options.put(XmlOptions.LOAD_ADDITIONAL_NAMESPACES, namespaces); |
| } |
| } |
| |
| String systemId = null; |
| String encoding = null; |
| String version = null; |
| boolean standAlone = true; |
| |
| LoadContext context = new Cur.CurLoadContext(this, options); |
| |
| events: |
| for (XMLEvent xe = xis.next(); xe != null; xe = xis.next()) |
| { |
| switch (xe.getType()) |
| { |
| case XMLEvent.START_DOCUMENT: |
| StartDocument doc = (StartDocument) xe; |
| |
| systemId = doc.getSystemId(); |
| encoding = doc.getCharacterEncodingScheme(); |
| version = doc.getVersion(); |
| standAlone = doc.isStandalone(); |
| standAlone = doc.isStandalone(); |
| |
| if (lineNums) |
| lineNumber(xe, context); |
| |
| break; |
| |
| case XMLEvent.END_DOCUMENT: |
| if (lineNums) |
| lineNumber(xe, context); |
| |
| break events; |
| |
| case XMLEvent.NULL_ELEMENT: |
| if (!xis.hasNext()) |
| break events; |
| break; |
| |
| case XMLEvent.START_ELEMENT: |
| context.startElement(XMLNameHelper.getQName(xe.getName())); |
| |
| if (lineNums) |
| lineNumber(xe, context); |
| |
| for (AttributeIterator ai = ((StartElement) xe).getAttributes(); |
| ai.hasNext();) |
| { |
| Attribute attr = ai.next(); |
| |
| context.attr(XMLNameHelper.getQName(attr.getName()), |
| attr.getValue()); |
| } |
| |
| for (AttributeIterator ai = ((StartElement) xe).getNamespaces() |
| ; ai.hasNext();) |
| { |
| Attribute attr = ai.next(); |
| |
| XMLName name = attr.getName(); |
| String local = name.getLocalName(); |
| |
| if (name.getPrefix() == null && local.equals("xmlns")) |
| local = ""; |
| |
| context.xmlns(local, attr.getValue()); |
| } |
| |
| break; |
| |
| case XMLEvent.END_ELEMENT: |
| context.endElement(); |
| |
| if (lineNums) |
| lineNumber(xe, context); |
| |
| break; |
| |
| case XMLEvent.SPACE: |
| if (((Space) xe).ignorable()) |
| break; |
| |
| // Fall through |
| |
| case XMLEvent.CHARACTER_DATA: |
| CharacterData cd = (CharacterData) xe; |
| |
| if (cd.hasContent()) |
| { |
| context.text(cd.getContent()); |
| |
| if (lineNums) |
| lineNumber(xe, context); |
| } |
| |
| break; |
| |
| case XMLEvent.COMMENT: |
| org.apache.xmlbeans.xml.stream.Comment comment = |
| (org.apache.xmlbeans.xml.stream.Comment) xe; |
| |
| if (comment.hasContent()) |
| { |
| context.comment(comment.getContent()); |
| |
| if (lineNums) |
| lineNumber(xe, context); |
| } |
| |
| break; |
| |
| case XMLEvent.PROCESSING_INSTRUCTION: |
| ProcessingInstruction procInstr = (ProcessingInstruction) xe; |
| |
| context.procInst(procInstr.getTarget(), procInstr.getData()); |
| |
| if (lineNums) |
| lineNumber(xe, context); |
| |
| break; |
| |
| // These are ignored |
| case XMLEvent.ENTITY_REFERENCE: |
| case XMLEvent.START_PREFIX_MAPPING: |
| case XMLEvent.END_PREFIX_MAPPING: |
| case XMLEvent.CHANGE_PREFIX_MAPPING: |
| case XMLEvent.XML_EVENT: |
| break; |
| |
| default : |
| throw new RuntimeException( |
| "Unhandled xml event type: " + xe.getTypeAsString()); |
| } |
| } |
| |
| Cur c = context.finish(); |
| |
| associateSourceName(c, options); |
| |
| XmlDocumentProperties props = getDocProps(c, true); |
| |
| props.setDoctypeSystemId(systemId); |
| props.setEncoding(encoding); |
| props.setVersion(version); |
| props.setStandalone(standAlone); |
| |
| return c; |
| } |
| |
| private Cur loadXMLStreamReader(XMLStreamReader xsr, XmlOptions options) |
| throws XMLStreamException |
| { |
| options = XmlOptions.maskNull(options); |
| |
| boolean lineNums = options.hasOption(XmlOptions.LOAD_LINE_NUMBERS); |
| |
| String encoding = null, version = null; |
| boolean standAlone = false; |
| |
| LoadContext context = new Cur.CurLoadContext(this, options); |
| int depth = 0; |
| |
| events: |
| for (int eventType = xsr.getEventType(); ; eventType = xsr.next()) |
| { |
| switch (eventType) |
| { |
| case XMLStreamReader.START_DOCUMENT: |
| { |
| depth++; |
| |
| encoding = xsr.getCharacterEncodingScheme(); |
| version = xsr.getVersion(); |
| standAlone = xsr.isStandalone(); |
| |
| if (lineNums) |
| lineNumber(xsr, context); |
| |
| break; |
| } |
| |
| case XMLStreamReader.END_DOCUMENT: |
| { |
| depth--; |
| |
| if (lineNums) |
| lineNumber(xsr, context); |
| |
| break events; |
| } |
| |
| case XMLStreamReader.START_ELEMENT: |
| { |
| depth++; |
| context.startElement(xsr.getName()); |
| |
| if (lineNums) |
| lineNumber(xsr, context); |
| |
| doAttributes(xsr, context); |
| doNamespaces(xsr, context); |
| |
| break; |
| } |
| |
| case XMLStreamReader.END_ELEMENT: |
| { |
| depth--; |
| context.endElement(); |
| |
| if (lineNums) |
| lineNumber(xsr, context); |
| |
| break; |
| } |
| |
| case XMLStreamReader.CHARACTERS: |
| case XMLStreamReader.CDATA: |
| { |
| context.text(xsr.getTextCharacters(), xsr.getTextStart(), |
| xsr.getTextLength()); |
| |
| if (lineNums) |
| lineNumber(xsr, context); |
| |
| break; |
| } |
| |
| case XMLStreamReader.COMMENT: |
| { |
| String comment = xsr.getText(); |
| |
| context.comment(comment); |
| |
| if (lineNums) |
| lineNumber(xsr, context); |
| |
| break; |
| } |
| |
| case XMLStreamReader.PROCESSING_INSTRUCTION: |
| { |
| context.procInst(xsr.getPITarget(), xsr.getPIData()); |
| |
| if (lineNums) |
| lineNumber(xsr, context); |
| |
| break; |
| } |
| |
| case XMLStreamReader.ATTRIBUTE: |
| { |
| doAttributes(xsr, context); |
| break; |
| } |
| |
| case XMLStreamReader.NAMESPACE: |
| { |
| doNamespaces(xsr, context); |
| break; |
| } |
| |
| case XMLStreamReader.ENTITY_REFERENCE: |
| { |
| context.text(xsr.getText()); |
| break; |
| } |
| |
| case XMLStreamReader.SPACE: |
| case XMLStreamReader.DTD: |
| break; |
| |
| default : |
| throw new RuntimeException( |
| "Unhandled xml event type: " + eventType); |
| } |
| |
| if (!xsr.hasNext() || depth <= 0) |
| break; |
| } |
| |
| Cur c = context.finish(); |
| |
| associateSourceName(c, options); |
| |
| XmlDocumentProperties props = getDocProps(c, true); |
| |
| props.setEncoding(encoding); |
| props.setVersion(version); |
| props.setStandalone(standAlone); |
| |
| return c; |
| } |
| |
| // |
| // |
| // |
| |
| public static XmlObject parseToXmlObject(SchemaTypeLoader stl, |
| InputStream is, SchemaType type, XmlOptions options) |
| throws XmlException, IOException |
| { |
| Locale l = getLocale(stl, options); |
| |
| if (l.noSync()) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(is, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| else |
| synchronized (l) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(is, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| } |
| |
| private XmlObject parseToXmlObject(InputStream is, SchemaType type, |
| XmlOptions options) |
| throws XmlException, IOException |
| { |
| Cur c = getSaxLoader(options).load(this, new InputSource(is), |
| options); |
| |
| autoTypeDocument(c, type, options); |
| |
| XmlObject x = (XmlObject) c.getUser(); |
| |
| c.release(); |
| |
| return x; |
| } |
| |
| // |
| // |
| // |
| |
| public static XmlObject parseToXmlObject(SchemaTypeLoader stl, |
| Reader reader, SchemaType type, XmlOptions options) |
| throws XmlException, IOException |
| { |
| Locale l = getLocale(stl, options); |
| |
| if (l.noSync()) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(reader, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| else |
| synchronized (l) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(reader, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| } |
| |
| private XmlObject parseToXmlObject(Reader reader, SchemaType type, |
| XmlOptions options) |
| throws XmlException, IOException |
| { |
| Cur c = getSaxLoader(options).load(this, new InputSource(reader), |
| options); |
| |
| autoTypeDocument(c, type, options); |
| |
| XmlObject x = (XmlObject) c.getUser(); |
| |
| c.release(); |
| |
| return x; |
| } |
| |
| // |
| // |
| // |
| |
| public static XmlObject parseToXmlObject(SchemaTypeLoader stl, Node node, |
| SchemaType type, XmlOptions options) |
| throws XmlException |
| { |
| Locale l = getLocale(stl, options); |
| |
| if (l.noSync()) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(node, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| else |
| synchronized (l) |
| { |
| l.enter(); |
| try |
| { |
| return l.parseToXmlObject(node, type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| } |
| |
| public XmlObject parseToXmlObject(Node node, SchemaType type, |
| XmlOptions options) |
| throws XmlException |
| { |
| LoadContext context = new Cur.CurLoadContext(this, options); |
| |
| loadNode(node, context); |
| |
| Cur c = context.finish(); |
| |
| associateSourceName(c, options); |
| |
| autoTypeDocument(c, type, options); |
| |
| XmlObject x = (XmlObject) c.getUser(); |
| |
| c.release(); |
| |
| return x; |
| } |
| |
| private void loadNodeChildren(Node n, LoadContext context) |
| { |
| for (Node c = n.getFirstChild(); c != null; c = c.getNextSibling()) |
| loadNode(c, context); |
| } |
| |
| void loadNode(Node n, LoadContext context) |
| { |
| switch (n.getNodeType()) |
| { |
| case Node.DOCUMENT_NODE: |
| case Node.DOCUMENT_FRAGMENT_NODE: |
| case Node.ENTITY_REFERENCE_NODE: |
| { |
| loadNodeChildren(n, context); |
| |
| break; |
| } |
| case Node.ELEMENT_NODE: |
| { |
| context.startElement( |
| makeQualifiedQName(n.getNamespaceURI(), n.getNodeName())); |
| |
| NamedNodeMap attrs = n.getAttributes(); |
| |
| for (int i = 0; i < attrs.getLength(); i++) |
| { |
| Node a = attrs.item(i); |
| |
| String attrName = a.getNodeName(); |
| String attrValue = a.getNodeValue(); |
| |
| if (attrName.toLowerCase().startsWith("xmlns")) |
| { |
| if (attrName.length() == 5) |
| context.xmlns(null, attrValue); |
| else |
| context.xmlns(attrName.substring(6), attrValue); |
| } |
| else |
| context.attr( |
| makeQualifiedQName(a.getNamespaceURI(), attrName), |
| attrValue); |
| } |
| |
| loadNodeChildren(n, context); |
| |
| context.endElement(); |
| |
| break; |
| } |
| case Node.TEXT_NODE: |
| case Node.CDATA_SECTION_NODE: |
| { |
| context.text(n.getNodeValue()); |
| break; |
| } |
| case Node.COMMENT_NODE: |
| { |
| context.comment(n.getNodeValue()); |
| break; |
| } |
| case Node.PROCESSING_INSTRUCTION_NODE: |
| { |
| context.procInst(n.getNodeName(), n.getNodeValue()); |
| break; |
| } |
| case Node.DOCUMENT_TYPE_NODE: |
| case Node.ENTITY_NODE: |
| case Node.NOTATION_NODE: |
| { |
| Node next = n.getNextSibling(); |
| if (next != null) { |
| loadNode(next, context); |
| } |
| break; |
| } |
| case Node.ATTRIBUTE_NODE: |
| { |
| throw new RuntimeException("Unexpected node"); |
| } |
| } |
| } |
| |
| // |
| // |
| // |
| |
| private class XmlSaxHandlerImpl |
| extends SaxHandler |
| implements XmlSaxHandler |
| { |
| XmlSaxHandlerImpl(Locale l, SchemaType type, XmlOptions options) |
| { |
| super(null); |
| |
| _options = options; |
| _type = type; |
| |
| // Because SAX loading is not atomic with respect to XmlBeans, I can't use the default |
| // thread local CharUtil. Instruct the SaxHandler (and the LoadContext, eventually) |
| // to use the Locale specific CharUtil. |
| |
| XmlOptions saxHandlerOptions = new XmlOptions(options); |
| saxHandlerOptions.put(Cur.LOAD_USE_LOCALE_CHAR_UTIL); |
| |
| initSaxHandler(l, saxHandlerOptions); |
| } |
| |
| public ContentHandler getContentHandler() |
| { |
| return _context == null ? null : this; |
| } |
| |
| public LexicalHandler getLexicalHandler() |
| { |
| return _context == null ? null : this; |
| } |
| |
| public void bookmarkLastEvent(XmlBookmark mark) |
| { |
| _context.bookmarkLastNonAttr(mark); |
| } |
| |
| public void bookmarkLastAttr(QName attrName, XmlBookmark mark) |
| { |
| _context.bookmarkLastAttr(attrName, mark); |
| } |
| |
| public XmlObject getObject() |
| throws XmlException |
| { |
| if (_context == null) |
| return null; |
| |
| _locale.enter(); |
| |
| try |
| { |
| Cur c = _context.finish(); |
| |
| autoTypeDocument(c, _type, _options); |
| |
| XmlObject x = (XmlObject) c.getUser(); |
| |
| c.release(); |
| |
| _context = null; |
| |
| return x; |
| } |
| finally |
| { |
| _locale.exit(); |
| } |
| } |
| |
| private SchemaType _type; |
| private XmlOptions _options; |
| } |
| |
| public static XmlSaxHandler newSaxHandler(SchemaTypeLoader stl, |
| SchemaType type, XmlOptions options) |
| { |
| Locale l = getLocale(stl, options); |
| |
| if (l.noSync()) |
| { |
| l.enter(); |
| try |
| { |
| return l.newSaxHandler(type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| else |
| synchronized (l) |
| { |
| l.enter(); |
| try |
| { |
| return l.newSaxHandler(type, options); |
| } |
| finally |
| { |
| l.exit(); |
| } |
| } |
| } |
| |
| public XmlSaxHandler newSaxHandler(SchemaType type, XmlOptions options) |
| { |
| return new XmlSaxHandlerImpl(this, 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 makeQNameNoCheck(String uri, String localPart) |
| { |
| 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 && ensure) |
| c.setBookmark(DocProps.class, props = new DocProps()); |
| |
| c.pop(); |
| |
| return props; |
| } |
| |
| interface ChangeListener |
| { |
| void notifyChange(); |
| |
| void setNextChangeListener(ChangeListener listener); |
| |
| ChangeListener getNextChangeListener(); |
| } |
| |
| void registerForChange(ChangeListener listener) |
| { |
| if (listener.getNextChangeListener() == null) |
| { |
| if (_changeListeners == null) |
| listener.setNextChangeListener(listener); |
| else |
| listener.setNextChangeListener(_changeListeners); |
| |
| _changeListeners = listener; |
| } |
| } |
| |
| void notifyChange() |
| { |
| // First, notify the registered listeners ... |
| |
| while (_changeListeners != null) |
| { |
| _changeListeners.notifyChange(); |
| |
| if (_changeListeners.getNextChangeListener() == _changeListeners) |
| _changeListeners.setNextChangeListener(null); |
| |
| ChangeListener next = _changeListeners.getNextChangeListener(); |
| |
| _changeListeners.setNextChangeListener(null); |
| |
| _changeListeners = next; |
| } |
| |
| // Then, prepare for the change in a locale specific way. Need to create real Curs for |
| // 'virtual' Curs in Locations |
| |
| _locations.notifyChange(); |
| } |
| |
| // |
| // Cursor helpers |
| // |
| |
| static String getTextValue(Cur c) |
| { |
| assert c.isNode(); |
| |
| if (!c.hasChildren()) |
| return c.getValueAsString(); |
| |
| StringBuffer sb = new StringBuffer(); |
| |
| c.push(); |
| |
| for (c.next(); !c.isAtEndOfLastPush(); c.next()) |
| if (c.isText()) |
| { |
| if ( (c._xobj.isComment() || c._xobj.isProcinst() ) && c._pos<c._xobj._cchValue ) |
| continue; |
| CharUtil.getString(sb, c.getChars(-1), c._offSrc, c._cchSrc); |
| } |
| |
| c.pop(); |
| |
| return sb.toString(); |
| } |
| |
| static int getTextValue(Cur c, int wsr, char[] chars, int off, int maxCch) |
| { |
| // TODO - hack impl for now ... improve |
| |
| assert c.isNode(); |
| |
| String s = c._xobj.getValueAsString(wsr); |
| |
| int n = s.length(); |
| |
| if (n > maxCch) |
| n = maxCch; |
| |
| if (n <= 0) |
| return 0; |
| |
| s.getChars(0, n, chars, off); |
| |
| return n; |
| } |
| |
| static String applyWhiteSpaceRule(String s, int wsr) |
| { |
| int l = s == null ? 0 : s.length(); |
| |
| if (l == 0 || wsr == WS_PRESERVE) |
| return s; |
| |
| char ch; |
| |
| if (wsr == 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(); |
| } |
| |
| static final class ScrubBuffer |
| { |
| ScrubBuffer() |
| { |
| _sb = new StringBuffer(); |
| } |
| |
| void init(int wsr) |
| { |
| _sb.delete(0, _sb.length()); |
| |
| _wsr = wsr; |
| _state = START_STATE; |
| } |
| |
| void scrub(Object src, int off, int cch) |
| { |
| if (cch == 0) |
| return; |
| |
| if (_wsr == Locale.WS_PRESERVE) |
| { |
| CharUtil.getString(_sb, src, off, cch); |
| return; |
| } |
| |
| char[] chars; |
| |
| 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]; |
| |
| CharUtil.getChars(chars, 0, src, off, cch); |
| off = 0; |
| } |
| |
| int start = 0; |
| |
| for (int i = 0; i < cch; i++) |
| { |
| char ch = chars[off + i]; |
| |
| if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') |
| { |
| _sb.append(chars, off + start, i - start); |
| |
| start = i + 1; |
| |
| if (_wsr == Locale.WS_REPLACE) |
| _sb.append(' '); |
| else if (_state == NOSPACE_STATE) |
| _state = SPACE_SEEN_STATE; |
| } |
| else |
| { |
| if (_state == SPACE_SEEN_STATE) |
| _sb.append(' '); |
| |
| _state = NOSPACE_STATE; |
| } |
| } |
| |
| _sb.append(chars, off + start, cch - start); |
| } |
| |
| String getResultAsString() |
| { |
| return _sb.toString(); |
| } |
| |
| private static final int START_STATE = 0; |
| private static final int SPACE_SEEN_STATE = 1; |
| private static final int NOSPACE_STATE = 2; |
| |
| private int _state; |
| |
| private int _wsr; |
| |
| private char[] _srcBuf = new char[1024]; |
| private StringBuffer _sb; |
| } |
| |
| private static ThreadLocal tl_scrubBuffer = |
| new ThreadLocal() |
| { |
| protected Object initialValue() |
| { |
| return new SoftReference(new ScrubBuffer()); |
| } |
| }; |
| |
| public static void clearThreadLocals() { |
| tl_scrubBuffer.remove(); |
| } |
| |
| static ScrubBuffer getScrubBuffer(int wsr) |
| { |
| SoftReference softRef = (SoftReference) tl_scrubBuffer.get(); |
| ScrubBuffer scrubBuffer = (ScrubBuffer) (softRef).get(); |
| if (scrubBuffer == null) |
| { |
| scrubBuffer = new ScrubBuffer(); |
| tl_scrubBuffer.set(new SoftReference(scrubBuffer)); |
| } |
| |
| scrubBuffer.init(wsr); |
| return scrubBuffer; |
| } |
| |
| 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 toPrevNormalAttr(Cur c) |
| { |
| if (c.isAttr()) |
| { |
| c.push(); |
| |
| for (; ;) |
| { |
| assert c.isAttr(); |
| |
| // See if I can move backward. If I'm at the first attr, prev must return |
| // false and not move. |
| |
| if (!c.prev()) |
| break; |
| |
| // Skip past the text value or attr begin |
| |
| c.prev(); |
| |
| // I might have skipped over text above |
| |
| if (!c.isAttr()) |
| c.prev(); |
| |
| if (c.isNormalAttr()) |
| { |
| c.popButStay(); |
| return true; |
| } |
| } |
| |
| 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; |
| } |
| |
| Xobj findNthChildElem(Xobj parent, QName name, QNameSet set, int n) |
| { |
| // only one of (set or name) is not null |
| // or both are null for a wildcard |
| assert (name == null || set == null); |
| assert n >= 0; |
| |
| if (parent == null) |
| return null; |
| |
| int da = _nthCache_A.distance(parent, name, set, n); |
| int db = _nthCache_B.distance(parent, name, set, n); |
| |
| Xobj x = |
| da <= db |
| ? _nthCache_A.fetch(parent, name, set, n) |
| : _nthCache_B.fetch(parent, name, set, n); |
| |
| if (da == db) |
| { |
| nthCache temp = _nthCache_A; |
| _nthCache_A = _nthCache_B; |
| _nthCache_B = temp; |
| } |
| |
| return x; |
| } |
| |
| int count(Xobj parent, QName name, QNameSet set) |
| { |
| int n = 0; |
| |
| for (Xobj x = findNthChildElem(parent, name, set, 0); |
| x != null; x = x._nextSibling) |
| { |
| if (x.isElem()) |
| { |
| if (set == null) |
| { |
| if (x._name.equals(name)) |
| n++; |
| } |
| else if (set.contains(x._name)) |
| n++; |
| } |
| } |
| |
| return n; |
| } |
| |
| static boolean toChild(Cur c, QName name, int n) |
| { |
| if (n >= 0 && pushToContainer(c)) |
| { |
| Xobj x = c._locale.findNthChildElem(c._xobj, name, null, n); |
| |
| c.pop(); |
| |
| if (x != null) |
| { |
| c.moveTo(x); |
| return true; |
| } |
| } |
| |
| 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; |
| |
| Xobj originalXobj = c._xobj; |
| int originalPos = c._pos; |
| |
| loop: |
| for (; ;) |
| { |
| switch (c.kind()) |
| { |
| case ROOT: |
| case ELEM: |
| break loop; |
| case -ROOT: |
| case -ELEM: |
| c.moveTo(originalXobj, originalPos); |
| return false; |
| case COMMENT: |
| case PROCINST: |
| c.skip(); |
| break; |
| default: |
| c.nextWithAttrs(); |
| break; |
| } |
| } |
| |
| if (!c.toFirstChild() || (!c.isElem() && !toNextSiblingElement(c))) |
| { |
| c.moveTo(originalXobj, originalPos); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static boolean toLastChildElement(Cur c) |
| { |
| if (!pushToContainer(c)) |
| return false; |
| |
| if (!c.toLastChild() || (!c.isElem() && !toPrevSiblingElement(c))) |
| { |
| c.pop(); |
| return false; |
| } |
| |
| c.popButStay(); |
| |
| return true; |
| } |
| |
| static boolean toPrevSiblingElement(Cur cur) |
| { |
| if (!cur.hasParent()) |
| return false; |
| |
| Cur c = cur.tempCur(); |
| |
| boolean moved = false; |
| |
| int k = c.kind(); |
| |
| if (k != ATTR) |
| { |
| for (; ;) |
| { |
| if (!c.prev()) |
| break; |
| |
| k = c.kind(); |
| |
| if (k == ROOT || k == ELEM) |
| break; |
| |
| if (c.kind() == -ELEM) |
| { |
| c.toParent(); |
| |
| cur.moveToCur(c); |
| moved = true; |
| |
| break; |
| } |
| } |
| } |
| |
| c.release(); |
| |
| return moved; |
| } |
| |
| static boolean toNextSiblingElement(Cur c) |
| { |
| if (!c.hasParent()) |
| return false; |
| |
| c.push(); |
| |
| int k = c.kind(); |
| |
| if (k == ATTR) |
| { |
| c.toParent(); |
| c.next(); |
| } |
| 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 toNextSiblingElement(Cur c, Xobj parent) |
| { |
| Xobj originalXobj = c._xobj; |
| int originalPos = c._pos; |
| |
| int k = c.kind(); |
| |
| if (k == ATTR) |
| { |
| c.moveTo(parent); |
| c.next(); |
| } |
| else if (k == ELEM) |
| c.skip(); |
| |
| while ((k = c.kind()) >= 0) |
| { |
| if (k == ELEM) |
| { |
| return true; |
| } |
| |
| if (k > 0) |
| c.toEnd(); |
| |
| c.next(); |
| } |
| |
| c.moveTo(originalXobj, originalPos); |
| |
| return false; |
| } |
| |
| static void applyNamespaces(Cur c, Map namespaces) |
| { |
| assert c.isContainer(); |
| |
| java.util.Iterator i = namespaces.keySet().iterator(); |
| |
| while (i.hasNext()) |
| { |
| String prefix = (String) i.next(); |
| |
| // Usually, this is the predefined xml namespace |
| if (!prefix.toLowerCase().startsWith("xml")) |
| { |
| if (c.namespaceForPrefix(prefix, false) == null) |
| { |
| c.push(); |
| |
| c.next(); |
| c.createAttr(c._locale.createXmlns(prefix)); |
| c.next(); |
| |
| c.insertString((String) namespaces.get(prefix)); |
| |
| c.pop(); |
| } |
| } |
| } |
| } |
| |
| static Map getAllNamespaces(Cur c, Map filleMe) |
| { |
| assert c.isNode(); |
| |
| c.push(); |
| |
| if (!c.isContainer()) |
| c.toParent(); |
| |
| assert c.isContainer(); |
| |
| do |
| { |
| QName cName = c.getName(); |
| |
| while (c.toNextAttr()) |
| { |
| if (c.isXmlns()) |
| { |
| String prefix = c.getXmlnsPrefix(); |
| String uri = c.getXmlnsUri(); |
| |
| if (filleMe == null) |
| filleMe = new HashMap(); |
| |
| if (!filleMe.containsKey(prefix)) |
| filleMe.put(prefix, uri); |
| } |
| } |
| |
| if (!c.isContainer()) |
| c.toParentRaw(); |
| } |
| while (c.toParentRaw()); |
| |
| c.pop(); |
| |
| return filleMe; |
| } |
| |
| class nthCache |
| { |
| 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) |
| { |
| return |
| setPattern == null |
| ? namesSame(namePattern, name) |
| : setPattern.contains(name); |
| } |
| |
| private boolean cacheSame(QName namePattern, QNameSet setPattern) |
| { |
| return |
| setPattern == null |
| ? namesSame(namePattern, _name) |
| : setsSame(setPattern, _set); |
| } |
| |
| int distance(Xobj parent, QName name, QNameSet set, int n) |
| { |
| assert n >= 0; |
| |
| if (_version != Locale.this.version()) |
| return Integer.MAX_VALUE - 1; |
| |
| if (parent != _parent || !cacheSame(name, set)) |
| return Integer.MAX_VALUE; |
| |
| return n > _n ? n - _n : _n - n; |
| } |
| |
| Xobj fetch(Xobj parent, QName name, QNameSet set, int n) |
| { |
| assert n >= 0; |
| |
| if (_version != Locale.this.version() || _parent != parent || |
| !cacheSame(name, set) || n == 0) |
| { |
| _version = Locale.this.version(); |
| _parent = parent; |
| _name = name; |
| _child = null; |
| _n = -1; |
| |
| loop: |
| for (Xobj x = parent._firstChild; |
| x != null; x = x._nextSibling) |
| { |
| if (x.isElem() && nameHit(name, set, x._name)) |
| { |
| _child = x; |
| _n = 0; |
| |
| break loop; |
| } |
| } |
| } |
| |
| if (_n < 0) |
| return null; |
| |
| if (n > _n) |
| { |
| while (n > _n) |
| { |
| for (Xobj x = _child._nextSibling; ; x = x._nextSibling) |
| { |
| if (x == null) |
| return null; |
| |
| if (x.isElem() && nameHit(name, set, x._name)) |
| { |
| _child = x; |
| _n++; |
| |
| break; |
| } |
| } |
| } |
| } |
| else if (n < _n) |
| { |
| while (n < _n) |
| { |
| for (Xobj x = _child._prevSibling; ; x = x._prevSibling) |
| { |
| if (x == null) |
| return null; |
| |
| if (x.isElem() && nameHit(name, set, x._name)) |
| { |
| _child = x; |
| _n--; |
| |
| break; |
| } |
| } |
| } |
| } |
| |
| return _child; |
| } |
| |
| private long _version; |
| private Xobj _parent; |
| private QName _name; |
| private QNameSet _set; |
| private Xobj _child; |
| private int _n; |
| } |
| |
| // |
| // |
| // |
| |
| Dom findDomNthChild ( Dom parent, int n ) |
| { |
| assert n >= 0; |
| |
| if (parent == null) |
| return null; |
| |
| int da = _domNthCache_A.distance(parent, n); |
| int db = _domNthCache_B.distance(parent, n); |
| |
| |
| // the "better" cache should never walk more than 1/2 len |
| Dom x = null; |
| boolean bInvalidate = (db - _domNthCache_B._len / 2 > 0) && |
| (db - _domNthCache_B._len / 2 - domNthCache.BLITZ_BOUNDARY > 0); |
| boolean aInvalidate = (da - _domNthCache_A._len / 2 > 0) && |
| (da - _domNthCache_A._len / 2 - domNthCache.BLITZ_BOUNDARY > 0); |
| if (da <= db) |
| if (!aInvalidate) |
| x = _domNthCache_A.fetch(parent, n); |
| else |
| { |
| _domNthCache_B._version = -1;//blitz the cache |
| x = _domNthCache_B.fetch(parent, n); |
| } |
| else if (!bInvalidate) |
| x = _domNthCache_B.fetch(parent, n); |
| else |
| { |
| _domNthCache_A._version = -1;//blitz the cache |
| x = _domNthCache_A.fetch(parent, n); |
| } |
| |
| if (da == db) |
| { |
| domNthCache temp = _domNthCache_A; |
| _domNthCache_A = _domNthCache_B; |
| _domNthCache_B = temp; |
| } |
| |
| return x; |
| } |
| |
| int domLength ( Dom parent ) |
| { |
| if (parent == null) |
| return 0; |
| |
| int da = _domNthCache_A.distance( parent, 0 ); |
| int db = _domNthCache_B.distance( parent, 0 ); |
| |
| int len = |
| da <= db |
| ? _domNthCache_A.length( parent ) |
| : _domNthCache_B.length( parent ); |
| |
| if (da == db) |
| { |
| domNthCache temp = _domNthCache_A; |
| _domNthCache_A = _domNthCache_B; |
| _domNthCache_B = temp; |
| } |
| |
| return len; |
| } |
| |
| void invalidateDomCaches ( Dom d ) |
| { |
| if (_domNthCache_A._parent == d) |
| _domNthCache_A._version = -1; |
| if (_domNthCache_B._parent == d) |
| _domNthCache_B._version = -1; |
| } |
| |
| boolean isDomCached ( Dom d ) |
| { |
| return _domNthCache_A._parent == d || _domNthCache_B._parent == d; |
| } |
| |
| class domNthCache |
| { |
| |
| int distance ( Dom parent, int n ) |
| { |
| assert n >= 0; |
| |
| if (_version != Locale.this.version()) |
| return Integer.MAX_VALUE - 1; |
| |
| if (parent != _parent) |
| return Integer.MAX_VALUE; |
| |
| return n > _n ? n - _n : _n - n; |
| } |
| |
| int length ( Dom parent ) |
| { |
| if (_version != Locale.this.version() || _parent != parent) |
| { |
| _parent = parent; |
| _version = Locale.this.version(); |
| _child = null; |
| _n = -1; |
| _len = -1; |
| } |
| |
| if (_len == -1) |
| { |
| Dom x = null; |
| |
| if (_child != null && _n != -1) |
| { |
| x = _child; |
| _len = _n; |
| } |
| else |
| { |
| x = DomImpl.firstChild(_parent); |
| _len = 0; |
| |
| // cache the 0th child |
| _child = x; |
| _n = 0; |
| } |
| |
| for (; x != null; x = DomImpl.nextSibling(x) ) |
| { |
| _len++; |
| } |
| } |
| |
| |
| return _len; |
| } |
| |
| Dom fetch ( Dom parent, int n ) |
| { |
| assert n >= 0; |
| |
| if (_version != Locale.this.version() || _parent != parent) |
| { |
| _parent = parent; |
| _version = Locale.this.version(); |
| _child = null; |
| _n = -1; |
| _len = -1; |
| |
| for (Dom x = DomImpl.firstChild(_parent); x != null; x = DomImpl.nextSibling(x) ) |
| { |
| _n++; |
| if (_child == null && n == _n ) |
| { |
| _child = x; |
| break; |
| } |
| } |
| |
| return _child; |
| } |
| |
| if (_n < 0) |
| return null; |
| |
| if (n > _n) |
| { |
| while ( n > _n ) |
| { |
| for (Dom x = DomImpl.nextSibling(_child); ; x = DomImpl.nextSibling(x) ) |
| { |
| if (x == null) |
| return null; |
| |
| _child = x; |
| _n++; |
| |
| break; |
| } |
| } |
| } |
| else if (n < _n) |
| { |
| while ( n < _n ) |
| { |
| for (Dom x = DomImpl.prevSibling(_child); ; x = DomImpl.prevSibling(x) ) |
| { |
| if (x == null) |
| return null; |
| |
| _child = x; |
| _n--; |
| |
| break; |
| } |
| } |
| } |
| |
| return _child; |
| } |
| |
| public static final int BLITZ_BOUNDARY = 40; //walk small lists |
| private long _version; |
| private Dom _parent; |
| private Dom _child; |
| private int _n; |
| private int _len; |
| } |
| |
| // |
| // |
| // |
| |
| CharUtil getCharUtil() |
| { |
| if (_charUtil == null) |
| _charUtil = new CharUtil(1024); |
| |
| return _charUtil; |
| } |
| |
| long version() |
| { |
| return _versionAll; |
| } |
| |
| Cur weakCur(Object o) |
| { |
| assert o != null && !(o instanceof Ref); |
| |
| Cur c = getCur(); |
| |
| assert c._tempFrame == -1; |
| assert c._ref == null; |
| |
| c._ref = 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; |
| } |
| |
| Cur _cur; |
| } |
| |
| Cur tempCur() |
| { |
| return tempCur(null); |
| } |
| |
| Cur tempCur(String id) |
| { |
| Cur c = getCur(); |
| |
| assert c._tempFrame == -1; |
| |
| assert _numTempFramesLeft < _tempFrames.length : "Temp frame not pushed"; |
| |
| int frame = _tempFrames.length - _numTempFramesLeft - 1; |
| |
| assert frame >= 0 && frame < _tempFrames.length; |
| |
| Cur next = _tempFrames[frame]; |
| |
| c._nextTemp = next; |
| assert c._prevTemp == null; |
| |
| if (next != null) |
| { |
| assert next._prevTemp == null; |
| next._prevTemp = c; |
| } |
| |
| _tempFrames[frame] = c; |
| c._tempFrame = frame; |
| |
| c._id = id; |
| |
| return c; |
| } |
| |
| Cur getCur() |
| { |
| assert _curPool == null || _curPoolCount > 0; |
| |
| Cur c; |
| |
| if (_curPool == null) |
| c = new Cur(this); |
| else |
| { |
| _curPool = _curPool.listRemove(c = _curPool); |
| _curPoolCount--; |
| } |
| |
| assert c._state == Cur.POOLED; |
| assert c._prev == null && c._next == null; |
| assert c._xobj == null && c._pos == Cur.NO_POS; |
| assert c._ref == null; |
| |
| _registered = c.listInsert(_registered); |
| c._state = Cur.REGISTERED; |
| |
| return c; |
| } |
| |
| void embedCurs() |
| { |
| for (Cur c; (c = _registered) != null;) |
| { |
| assert c._xobj != null; |
| |
| _registered = c.listRemove(_registered); |
| c._xobj._embedded = c.listInsert(c._xobj._embedded); |
| c._state = Cur.EMBEDDED; |
| } |
| } |
| |
| 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[_tempFrames.length * 2]; |
| //move this assignment down so if array allocation fails, error is not masked |
| _numTempFramesLeft = _tempFrames.length; |
| System.arraycopy(_tempFrames, 0, newTempFrames, 0, |
| _tempFrames.length); |
| _tempFrames = newTempFrames; |
| } |
| |
| if (++_entryCount > 1000) |
| { |
| pollQueue(); |
| _entryCount = 0; |
| } |
| } |
| |
| private void pollQueue() |
| { |
| if (_refQueue != null) |
| { |
| for (; ;) |
| { |
| Ref ref = (Ref) _refQueue.poll(); |
| |
| if (ref == null) |
| break; |
| |
| if (ref._cur != null) |
| ref._cur.release(); |
| } |
| } |
| } |
| |
| public void exit(Locale otherLocale) |
| { |
| exit(); |
| |
| if (otherLocale != this) |
| otherLocale.exit(); |
| } |
| |
| public void exit() |
| { |
| // assert _numTempFramesLeft >= 0; |
| //asserts computed frame fits between 0 and _tempFrames.length |
| assert _numTempFramesLeft >= 0 && |
| (_numTempFramesLeft <= _tempFrames.length - 1): |
| " Temp frames mismanaged. Impossible stack frame. Unsynchronized: " + |
| noSync(); |
| |
| int frame = _tempFrames.length - ++_numTempFramesLeft; |
| |
| while (_tempFrames[frame] != null) |
| _tempFrames[frame].release(); |
| } |
| |
| // |
| // |
| // |
| |
| 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 startDTD(String name, String publicId, |
| String systemId); |
| |
| protected abstract void endDTD(); |
| |
| protected abstract void startElement(QName name); |
| |
| protected abstract void endElement(); |
| |
| protected abstract void attr(QName name, String value); |
| |
| protected abstract void attr(String local, String uri, String prefix, |
| String value); |
| |
| protected abstract void xmlns(String prefix, String uri); |
| |
| protected abstract void comment(char[] buff, int off, int cch); |
| |
| protected abstract void comment(String comment); |
| |
| protected abstract void procInst(String target, String value); |
| |
| protected abstract void text(char[] buff, int off, int cch); |
| |
| protected abstract void text(String s); |
| |
| protected abstract Cur finish(); |
| |
| protected abstract void abort(); |
| |
| protected abstract void bookmark(XmlBookmark bm); |
| |
| protected abstract void bookmarkLastNonAttr(XmlBookmark bm); |
| |
| protected abstract void bookmarkLastAttr(QName attrName, |
| XmlBookmark bm); |
| |
| protected abstract void lineNumber(int line, int column, int offset); |
| |
| protected void addIdAttr(String eName, String aName){ |
| if ( _idAttrs == null ) |
| _idAttrs = new java.util.Hashtable(); |
| _idAttrs.put(aName,eName); |
| } |
| |
| protected boolean isAttrOfTypeId(QName aqn, QName eqn){ |
| if (_idAttrs == null) |
| return false; |
| String pre = aqn.getPrefix(); |
| String lName = aqn.getLocalPart(); |
| String urnName = "".equals(pre)?lName:pre + ":" + lName; |
| String eName = (String) _idAttrs.get(urnName); |
| if (eName == null ) return false; |
| //get the name of the parent elt |
| pre = eqn.getPrefix(); |
| lName = eqn.getLocalPart(); |
| lName = eqn.getLocalPart(); |
| urnName = "".equals(pre)?lName:pre + ":" + lName; |
| return eName.equals(urnName); |
| } |
| private java.util.Hashtable _idAttrs; |
| } |
| |
| private static class DefaultEntityResolver |
| implements EntityResolver |
| { |
| public InputSource resolveEntity(String publicId, String systemId) |
| { |
| return new InputSource(new StringReader("")); |
| } |
| } |
| |
| private static SaxLoader getSaxLoader(XmlOptions options) throws XmlException |
| { |
| options = XmlOptions.maskNull(options); |
| |
| EntityResolver er = null; |
| |
| if (!options.hasOption(XmlOptions.LOAD_USE_DEFAULT_RESOLVER)) |
| { |
| er = (EntityResolver) options.get(XmlOptions.ENTITY_RESOLVER); |
| |
| if (er == null) |
| er = ResolverUtil.getGlobalEntityResolver(); |
| |
| if (er == null) |
| er = new DefaultEntityResolver(); |
| } |
| |
| XMLReader xr = (XMLReader) options.get( |
| XmlOptions.LOAD_USE_XMLREADER); |
| |
| if (xr == null) { |
| try { |
| xr = SAXHelper.newXMLReader(new XmlOptionsBean(options)); |
| } catch(Exception e) { |
| throw new XmlException("Problem creating XMLReader", e); |
| } |
| } |
| |
| SaxLoader sl = new XmlReaderSaxLoader(xr); |
| |
| // I've noticed that most XMLReaders don't like a null EntityResolver... |
| |
| if (er != null) |
| xr.setEntityResolver(er); |
| |
| return sl; |
| } |
| |
| private static class XmlReaderSaxLoader |
| extends SaxLoader |
| { |
| XmlReaderSaxLoader(XMLReader xr) |
| { |
| super(xr, null); |
| } |
| } |
| |
| private static abstract class SaxHandler |
| implements ContentHandler, LexicalHandler , DeclHandler, DTDHandler |
| { |
| protected Locale _locale; |
| |
| protected LoadContext _context; |
| |
| private boolean _wantLineNumbers; |
| private boolean _wantLineNumbersAtEndElt; |
| private boolean _wantCdataBookmarks; |
| private Locator _startLocator; |
| private boolean _insideCDATA = false; |
| private int _entityBytesLimit = 10240; |
| private int _entityBytes = 0; |
| private int _insideEntity = 0; |
| |
| SaxHandler(Locator startLocator) |
| { |
| _startLocator = startLocator; |
| } |
| |
| SaxHandler() |
| { |
| this(null); |
| } |
| |
| void initSaxHandler(Locale l, final XmlOptions options) |
| { |
| _locale = l; |
| |
| XmlOptions safeOptions = XmlOptions.maskNull(options); |
| |
| _context = new Cur.CurLoadContext(_locale, safeOptions); |
| |
| _wantLineNumbers = safeOptions.hasOption(XmlOptions.LOAD_LINE_NUMBERS); |
| _wantLineNumbersAtEndElt = safeOptions.hasOption(XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT); |
| _wantCdataBookmarks = safeOptions.hasOption(XmlOptions.LOAD_SAVE_CDATA_BOOKMARKS); |
| |
| if (safeOptions.hasOption(XmlOptions.LOAD_ENTITY_BYTES_LIMIT)) |
| _entityBytesLimit = (Integer) (safeOptions.get(XmlOptions.LOAD_ENTITY_BYTES_LIMIT)); |
| } |
| |
| 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 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)); |
| |
| if (_wantLineNumbers && _startLocator != null) |
| { |
| _context.bookmark( |
| new XmlLineNumber(_startLocator.getLineNumber(), |
| _startLocator.getColumnNumber() - 1, -1)); |
| } |
| |
| for (int i = 0, len = atts.getLength(); i < len; i++) |
| { |
| String aqn = atts.getQName(i); |
| |
| if (aqn.equals("xmlns")) |
| { |
| _context.xmlns("", 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.xmlns(prefix, 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(); |
| if (_wantLineNumbersAtEndElt && _startLocator != null) |
| { |
| _context.bookmark( |
| new XmlLineNumber(_startLocator.getLineNumber(), |
| _startLocator.getColumnNumber() - 1, -1)); |
| } |
| } |
| |
| public void characters(char ch[], int start, int length) |
| throws SAXException |
| { |
| _context.text(ch, start, length); |
| |
| if (_wantCdataBookmarks && _insideCDATA && _startLocator != null) |
| _context.bookmarkLastNonAttr(CDataBookmark.CDATA_BOOKMARK); |
| |
| if (_insideEntity!=0) |
| { |
| if ((_entityBytes += length) > _entityBytesLimit) |
| { |
| XmlError err = XmlError.forMessage(XmlErrorCodes.EXCEPTION_EXCEEDED_ENTITY_BYTES, |
| new Integer[]{_entityBytesLimit}); |
| |
| throw new SAXException(err.getMessage()); |
| } |
| } |
| } |
| |
| 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 |
| { |
| _context.startDTD(name, publicId, systemId); |
| } |
| |
| public void endDTD() |
| throws SAXException |
| { |
| _context.endDTD(); |
| } |
| |
| 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 |
| { |
| _insideCDATA = true; |
| } |
| |
| public void endCDATA() |
| throws SAXException |
| { |
| _insideCDATA = false; |
| } |
| |
| public void startEntity(String name) |
| throws SAXException |
| { |
| _insideEntity++; |
| } |
| |
| public void endEntity(String name) |
| throws SAXException |
| { |
| _insideEntity--; |
| assert _insideEntity>=0; |
| |
| if (_insideEntity==0) |
| { |
| _entityBytes=0; |
| } |
| } |
| |
| public void setDocumentLocator(Locator locator) { |
| if (_startLocator == null) { |
| _startLocator = locator; |
| } |
| } |
| |
| //DeclHandler |
| public void attributeDecl(String eName, String aName, String type, String valueDefault, String value){ |
| if (type.equals("ID")){ |
| _context.addIdAttr(eName,aName); |
| } |
| } |
| public void elementDecl(String name, String model){ |
| } |
| public void externalEntityDecl(String name, String publicId, String systemId){ |
| } |
| public void internalEntityDecl(String name, String value){ |
| } |
| |
| //DTDHandler |
| public void notationDecl(String name, String publicId, String systemId){ |
| } |
| public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName){ |
| } |
| } |
| |
| private static abstract class SaxLoader |
| extends SaxHandler |
| implements ErrorHandler |
| { |
| SaxLoader(XMLReader xr, Locator startLocator) |
| { |
| super(startLocator); |
| |
| _xr = xr; |
| |
| 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.setDTDHandler(this); |
| _xr.setErrorHandler(this); |
| } |
| catch (Throwable e) { |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| try |
| { |
| _xr.setProperty("http://xml.org/sax/properties/declaration-handler", this); |
| } |
| catch (Throwable e) { |
| logger.log(XBLogger.WARN, "SAX Declaration Handler is not supported", e); |
| } |
| } |
| |
| void setEntityResolver(EntityResolver er) |
| { |
| _xr.setEntityResolver(er); |
| } |
| |
| void postLoad(Cur c) |
| { |
| // fix garbage collection of Locale -> Xobj -> STL |
| _locale = null; |
| _context = null; |
| } |
| |
| public Cur load(Locale l, InputSource is, XmlOptions options) |
| throws XmlException, IOException |
| { |
| is.setSystemId("file://"); |
| |
| initSaxHandler(l, options); |
| |
| try |
| { |
| _xr.parse(is); |
| |
| Cur c = _context.finish(); |
| |
| associateSourceName(c, options); |
| |
| postLoad(c); |
| |
| return c; |
| } |
| catch (XmlRuntimeException e) |
| { |
| _context.abort(); |
| |
| throw new XmlException(e); |
| } |
| catch (SAXParseException e) |
| { |
| _context.abort(); |
| |
| XmlError err = |
| XmlError.forLocation(e.getMessage(), |
| (String) XmlOptions.safeGet(options, |
| XmlOptions.DOCUMENT_SOURCE_NAME), |
| e.getLineNumber(), e.getColumnNumber(), -1); |
| |
| throw new XmlException(err.toString(), e, err); |
| } |
| catch (SAXException e) |
| { |
| _context.abort(); |
| |
| XmlError err = XmlError.forMessage(e.getMessage()); |
| |
| throw new XmlException(err.toString(), e, err); |
| } |
| catch (RuntimeException e) |
| { |
| _context.abort(); |
| |
| throw e; |
| } |
| } |
| |
| 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; |
| } |
| |
| private XMLReader _xr; |
| } |
| |
| private Dom load(InputSource is, XmlOptions options) |
| throws XmlException, IOException |
| { |
| return getSaxLoader(options).load(this, is, options).getDom(); |
| } |
| |
| public Dom load(Reader r) |
| throws XmlException, IOException |
| { |
| return load(r, null); |
| } |
| |
| public Dom load(Reader r, XmlOptions options) |
| throws XmlException, IOException |
| { |
| return load(new InputSource(r), options); |
| } |
| |
| public Dom load(InputStream in) |
| throws XmlException, IOException |
| { |
| return load(in, null); |
| } |
| |
| public Dom load(InputStream in, XmlOptions options) |
| throws XmlException, IOException |
| { |
| return load(new InputSource(in), options); |
| } |
| |
| public Dom load(String s) |
| throws XmlException |
| { |
| return load(s, null); |
| } |
| |
| public Dom load(String s, XmlOptions options) |
| throws XmlException |
| { |
| Reader r = new StringReader(s); |
| |
| try |
| { |
| return load(r, options); |
| } |
| catch (IOException e) |
| { |
| assert false: "StringReader should not throw IOException"; |
| |
| throw new XmlException(e.getMessage(), e); |
| } |
| finally |
| { |
| try |
| { |
| r.close(); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| } |
| |
| // |
| // 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"); |
| } |
| |
| // |
| // Dom methods |
| // |
| |
| private static Dom checkNode(Node n) |
| { |
| if (n == null) |
| throw new IllegalArgumentException("Node is null"); |
| |
| if (!(n instanceof Dom)) |
| throw new IllegalArgumentException("Node is not an XmlBeans node"); |
| |
| return (Dom) n; |
| } |
| |
| public static XmlCursor nodeToCursor(Node n) |
| { |
| return DomImpl._getXmlCursor(checkNode(n)); |
| } |
| |
| public static XmlObject nodeToXmlObject(Node n) |
| { |
| return DomImpl._getXmlObject(checkNode(n)); |
| } |
| |
| public static XMLStreamReader nodeToXmlStream(Node n) |
| { |
| return DomImpl._getXmlStreamReader(checkNode(n)); |
| } |
| |
| public static Node streamToNode(XMLStreamReader xs) |
| { |
| return Jsr173.nodeFromStream(xs); |
| } |
| |
| // |
| // 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)); |
| } |
| } |
| |
| |
| private static final class LocalDocumentQNameFactory |
| implements QNameFactory |
| { |
| private QNameCache _cache = new QNameCache( 32 ); |
| |
| 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; |
| |
| int _numTempFramesLeft; |
| Cur[] _tempFrames; |
| |
| Cur _curPool; |
| int _curPoolCount; |
| |
| Cur _registered; |
| |
| ChangeListener _changeListeners; |
| |
| long _versionAll; |
| long _versionSansText; |
| |
| Locations _locations; |
| |
| private CharUtil _charUtil; |
| |
| int _offSrc; |
| int _cchSrc; |
| |
| Saaj _saaj; |
| |
| Dom _ownerDoc; |
| |
| QNameFactory _qnameFactory; |
| |
| boolean _validateOnSet; |
| |
| int _posTemp; |
| |
| nthCache _nthCache_A = new nthCache(); |
| nthCache _nthCache_B = new nthCache(); |
| |
| domNthCache _domNthCache_A = new domNthCache(); |
| domNthCache _domNthCache_B = new domNthCache(); |
| } |