blob: c6aa9845f2558da9902bac32d6a4ae04076a0411 [file] [log] [blame]
/* 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.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;
import java.util.Objects;
import static org.apache.xmlbeans.impl.values.TypeStore.*;
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 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.isUnsynchronized();
_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.isValidateOnSet();
//
// Check for Saaj implementation request
//
_saaj = options.getSaaj();
if (_saaj != null) {
_saaj.setCallback(this);
}
}
//
//
//
public static Locale getLocale(SchemaTypeLoader stl, XmlOptions options) {
if (stl == null) {
stl = XmlBeans.getContextTypeLoader();
}
options = XmlOptions.maskNull(options);
if (options.getUseSameLocale() == null) {
return new Locale(stl, options);
}
Object source = options.getUseSameLocale();
Locale l;
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.getSaaj()) {
throw new IllegalArgumentException(
"Source locale does not support same saaj");
}
if (l._validateOnSet && !options.isValidateOnSet()) {
throw new IllegalArgumentException(
"Source locale does not support same validate on set");
}
// TODO - other things to check?
return l;
}
//
//
//
public static void associateSourceName(Cur c, XmlOptions options) {
String sourceName = options == null ? null : options.getDocumentSourceName();
if (sourceName != null) {
getDocProps(c, true).setSourceName(sourceName);
}
}
//
//
//
public 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 = options.getDocumentType();
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;
}
return Objects.equals(n1.getNamespaceURI(), 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 expected ")
.append(docElemName.getLocalPart())
.append(" got ")
.append(name.getLocalPart());
} else {
sb.append(": document element mismatch 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
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 = options.getDocumentType();
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 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 {
try (Reader r = new StringReader(s)) {
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);
}
}
//
//
//
//
//
//
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(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));
}
}
}
private Cur loadXMLStreamReader(XMLStreamReader xsr, XmlOptions options)
throws XMLStreamException {
options = XmlOptions.maskNull(options);
boolean lineNums = options.isLoadLineNumbers();
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);
}
}
public 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 static 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.setLoadUseLocaleCharUtil(true);
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 final SchemaType _type;
private final 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 final HashMap<Object, Object> _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 == 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 == 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 == 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 final StringBuffer _sb;
}
private static final ThreadLocal<SoftReference<ScrubBuffer>> tl_scrubBuffer =
ThreadLocal.withInitial(() -> new SoftReference<>(new ScrubBuffer()));
public static void clearThreadLocals() {
tl_scrubBuffer.remove();
}
static ScrubBuffer getScrubBuffer(int wsr) {
SoftReference<ScrubBuffer> softRef = tl_scrubBuffer.get();
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;
}
public 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<String, String> namespaces) {
assert c.isContainer();
for (String prefix : namespaces.keySet()) {
// 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(namespaces.get(prefix));
c.pop();
}
}
}
}
static Map<String, String> getAllNamespaces(Cur c, Map<String, String> 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 = (Dom) DomImpl.firstChild(_parent);
_len = 0;
// cache the 0th child
_child = x;
_n = 0;
}
for (; x != null; x = (Dom) 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 = (Dom) DomImpl.firstChild(_parent); x != null; x = (Dom) 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 = (Dom) DomImpl.nextSibling(_child); ; x = (Dom) DomImpl.nextSibling(x)) {
if (x == null) {
return null;
}
_child = x;
_n++;
break;
}
}
} else if (n < _n) {
while (n < _n) {
for (Dom x = (Dom) DomImpl.prevSibling(_child); ; x = (Dom) 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;
}
public 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
//
public 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();
public 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);
public 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.isLoadUseDefaultResolver()) {
er = options.getEntityResolver();
if (er == null) {
er = ResolverUtil.getGlobalEntityResolver();
}
if (er == null) {
er = new DefaultEntityResolver();
}
}
XMLReader xr = options.getLoadUseXMLReader();
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.isLoadLineNumbers();
_wantLineNumbersAtEndElt = safeOptions.isLoadLineNumbersEndElement();
_wantCdataBookmarks = safeOptions.isUseCDataBookmarks();
Integer limit = safeOptions.getLoadEntityBytesLimit();
if (limit != null) {
_entityBytesLimit = 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(),
options == null ? null : options.getDocumentSourceName(),
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);
}
public SchemaTypeLoader getSchemaTypeLoader() {
return _schemaTypeLoader;
}
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();
}