/* Woodstox XML processor
 *
 * Copyright (c) 2004- Tatu Saloranta, tatu.saloranta@iki.fi
 *
 * Licensed under the License specified in file LICENSE, included with
 * the source code.
 * You may not use this file except in compliance with the License.
 *
 * 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 com.ctc.wstx.sr;

import java.util.*;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;

import org.codehaus.stax2.AttributeInfo;
import org.codehaus.stax2.validation.ValidationContext;
import org.codehaus.stax2.validation.XMLValidator;
import org.codehaus.stax2.validation.XMLValidationProblem;
import org.codehaus.stax2.validation.XMLValidationSchema;
import org.codehaus.stax2.validation.ValidatorPair;

import com.ctc.wstx.api.ReaderConfig;
import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.cfg.ErrorConsts;
import com.ctc.wstx.cfg.XmlConsts;
import com.ctc.wstx.compat.QNameCreator;
import com.ctc.wstx.dtd.DTDValidatorBase; // unfortunate dependency
import com.ctc.wstx.util.*;

Shared base class that defines API stream reader uses to communicate with the element stack implementation, independent of whether it's operating in namespace-aware or non-namespace modes. Element stack class is used for storing nesting information about open elements, and for namespace-aware mode, also information about namespaces active (including default namespace), during parsing of XML input.

This class also implements NamespaceContext, since it has all the information necessary, so parser can just return element stack instance as necesary.

/** * Shared base class that defines API stream reader uses to communicate * with the element stack implementation, independent of whether it's * operating in namespace-aware or non-namespace modes. * Element stack class is used for storing nesting information about open * elements, and for namespace-aware mode, also information about * namespaces active (including default namespace), during parsing of * XML input. *<p> * This class also implements {@link NamespaceContext}, since it has all * the information necessary, so parser can just return element stack * instance as necesary. */
public final class InputElementStack implements AttributeInfo, NamespaceContext, ValidationContext { final static int ID_ATTR_NONE = -1; /* /////////////////////////////////////////////////////////// // Configuration /////////////////////////////////////////////////////////// */ protected final boolean mNsAware; protected final AttributeCollector mAttrCollector; protected final ReaderConfig mConfig; protected InputProblemReporter mReporter = null;
Object that will need to be consulted about namespace bindings, since it has some knowledge about default namespace declarations (has default attribute value expansion).
/** * Object that will need to be consulted about namespace bindings, * since it has some knowledge about default namespace declarations * (has default attribute value expansion). */
protected NsDefaultProvider mNsDefaultProvider; /* /////////////////////////////////////////////////////////// // Element, namespace information /////////////////////////////////////////////////////////// */ protected int mDepth = 0; protected long mTotalElements = 0;
Vector that contains all currently active namespaces; one String for prefix, another for matching URI. Does also include default name spaces (at most one per level).
/** * Vector that contains all currently active namespaces; one String for * prefix, another for matching URI. Does also include default name * spaces (at most one per level). */
protected final StringVector mNamespaces = new StringVector(64);
Currently open element, if any; null outside root element.
/** * Currently open element, if any; null outside root element. */
protected Element mCurrElement; protected boolean mMayHaveNsDefaults = false; /* /////////////////////////////////////////////////////////// // Element validation (optional), attribute typing /////////////////////////////////////////////////////////// */
Optional validator object that will get called if set, and that can validate xml content. Note that it is possible that this is set to a proxy object that calls multiple validators in sequence.
/** * Optional validator object that will get called if set, * and that can validate xml content. Note that it is possible * that this is set to a proxy object that calls multiple * validators in sequence. */
protected XMLValidator mValidator = null;
Index of the attribute with type of ID, if known (most likely due to Xml:id support); -1 if not available, or no ID attribute for current element.
/** * Index of the attribute with type of ID, if known (most likely * due to Xml:id support); -1 if not available, or no ID attribute * for current element. */
protected int mIdAttrIndex = ID_ATTR_NONE; /* /////////////////////////////////////////////////////////// // Simple 1-slot QName cache; used for improving // efficiency of code that uses QNames extensively // (like StAX Event API implementation) /////////////////////////////////////////////////////////// */ protected String mLastLocalName = null; protected String mLastPrefix = null; protected String mLastNsURI = null; protected QName mLastName = null; /* /////////////////////////////////////////////////////////// // Other simple caching /////////////////////////////////////////////////////////// */ // Non-transient NamespaceContext caching; mostly for event API
Last potentially shareable NamespaceContext created by this stack. This reference is cleared each time bindings change (either due to a start element with new bindings, or due to the matching end element that closes scope of such binding(s)).
/** * Last potentially shareable NamespaceContext created by * this stack. This reference is cleared each time bindings * change (either due to a start element with new bindings, or due * to the matching end element that closes scope of such binding(s)). */
protected BaseNsContext mLastNsContext = null; // Chain of reusable Element instances protected Element mFreeElement = null; /* /////////////////////////////////////////////////////////// // Life-cycle (create, update state) /////////////////////////////////////////////////////////// */ protected InputElementStack(ReaderConfig cfg, boolean nsAware) { mConfig = cfg; mNsAware = nsAware; mAttrCollector = new AttributeCollector(cfg, nsAware); } protected void connectReporter(InputProblemReporter rep) { mReporter = rep; } protected XMLValidator addValidator(XMLValidator vld) { if (mValidator == null) { mValidator = vld; } else { mValidator = new ValidatorPair(mValidator, vld); } return vld; }
Method called to connect the automatically handled DTD validator (one detected from DOCTYPE, loaded and completely handled by the stream reader without application calling validation methods). Handled separately, since its behaviour is potentially different from that of explicitly added validators.
/** * Method called to connect the automatically handled DTD validator * (one detected from DOCTYPE, loaded and completely handled by * the stream reader without application calling validation methods). * Handled separately, since its behaviour is potentially different * from that of explicitly added validators. */
protected void setAutomaticDTDValidator(XMLValidator validator, NsDefaultProvider nsDefs) { mNsDefaultProvider = nsDefs; addValidator(validator); } /* /////////////////////////////////////////////////////////// // Start/stop validation /////////////////////////////////////////////////////////// */ public XMLValidator validateAgainst(XMLValidationSchema schema) throws XMLStreamException { /* Should we first check if we maybe already have a validator * for the schema? */ return addValidator(schema.createValidator(this)); } public XMLValidator stopValidatingAgainst(XMLValidationSchema schema) throws XMLStreamException { XMLValidator[] results = new XMLValidator[2]; if (ValidatorPair.removeValidator(mValidator, schema, results)) { // found XMLValidator found = results[0]; mValidator = results[1]; found.validationCompleted(false); return found; } return null; } public XMLValidator stopValidatingAgainst(XMLValidator validator) throws XMLStreamException { XMLValidator[] results = new XMLValidator[2]; if (ValidatorPair.removeValidator(mValidator, validator, results)) { // found XMLValidator found = results[0]; mValidator = results[1]; found.validationCompleted(false); return found; } return null; } /* /////////////////////////////////////////////////////////// // Accessors: /////////////////////////////////////////////////////////// */
This is a method called by the reader to ensure that we have at least one 'real' validator. This is only needed to ensure that validation problems that the reader can detect (illegal textual content) can be reported as validity errors. Since the validator API does not have a good way to cleanly deal with such a possibility, the check is rather fragile, but should work for now: essentially we need at least one validator object that either is not a sub-class of DTDValidatorBase or returns true for reallyValidating.

!!! TODO: remove need for this method (and method itself) with Woodstox 4.0, by adding necessary support in Stax2 XMLValidator interface.

/** * This is a method called by the reader to ensure that we have at * least one 'real' validator. This is only needed to ensure that * validation problems that the reader can detect (illegal textual * content) can be reported as validity errors. Since the validator * API does not have a good way to cleanly deal with such a possibility, * the check is rather fragile, but should work for now: essentially * we need at least one validator object that either is not a sub-class * of <code>DTDValidatorBase</code> or returns true for * <code>reallyValidating</code>. *<p> * !!! TODO: remove need for this method (and method itself) with * Woodstox 4.0, by adding necessary support in Stax2 XMLValidator * interface. */
protected boolean reallyValidating() { if (mValidator == null) { // no validators, no validation // (although, should never get called if no validators) return false; } if (!(mValidator instanceof DTDValidatorBase)) { // note: happens for validator pair, for one return true; } return ((DTDValidatorBase) mValidator).reallyValidating(); }
Method called by BasicStreamReader, to retrieve the attribute collector it needs for some direct access.
/** * Method called by {@link BasicStreamReader}, to retrieve the * attribute collector it needs for some direct access. */
public final AttributeCollector getAttrCollector() { return mAttrCollector; }
Method called to construct a non-transient NamespaceContext instance; generally needed when creating events to return from event-based iterators.
/** * Method called to construct a non-transient NamespaceContext instance; * generally needed when creating events to return from event-based * iterators. */
public BaseNsContext createNonTransientNsContext(Location loc) { // Have an instance we can reuse? Great! if (mLastNsContext != null) { return mLastNsContext; } // No namespaces declared at this point? Easy, as well: int totalNsSize = mNamespaces.size(); if (totalNsSize < 1) { return (mLastNsContext = EmptyNamespaceContext.getInstance()); } // Otherwise, we need to create a new non-empty context: int localCount = getCurrentNsCount() << 1; BaseNsContext nsCtxt = new CompactNsContext (loc, /*getDefaultNsURI(),*/ mNamespaces.asArray(), totalNsSize, totalNsSize - localCount); /* And it can be shared if there are no new ('local', ie. included * within this start element) bindings -- if there are, underlying * array might be shareable, but offsets wouldn't be) */ if (localCount == 0) { mLastNsContext = nsCtxt; } return nsCtxt; }
Method called by the stream reader to add new (start) element into the stack in namespace-aware mode; called when a start element is encountered during parsing, but only in ns-aware mode.
Throws:
  • XMLStreamException –
/** * Method called by the stream reader to add new (start) element * into the stack in namespace-aware mode; called when a start element * is encountered during parsing, but only in ns-aware mode. * @throws XMLStreamException */
public final void push(String prefix, String localName) throws XMLStreamException { if (++mDepth > mConfig.getMaxElementDepth()) { throw new XMLStreamException("Maximum Element Depth limit ("+mConfig.getMaxElementDepth()+") Exceeded"); } if (++mTotalElements > mConfig.getMaxElementCount()) { throw new XMLStreamException("Maximum Element Count limit ("+mConfig.getMaxElementCount()+") Exceeded"); } String defaultNs = (mCurrElement == null) ? XmlConsts.DEFAULT_NAMESPACE_URI : mCurrElement.mDefaultNsURI; if (mCurrElement != null) { ++mCurrElement.mChildCount; final int max = mConfig.getMaxChildrenPerElement(); if (max > 0 && mCurrElement.mChildCount > max) { throw new XMLStreamException("Maximum Number of Child Elements limit ("+max+") Exceeded"); } } if (mFreeElement == null) { mCurrElement = new Element(mCurrElement, mNamespaces.size(), prefix, localName); } else { Element newElem = mFreeElement; mFreeElement = newElem.mParent; newElem.reset(mCurrElement, mNamespaces.size(), prefix, localName); mCurrElement = newElem; } mCurrElement.mDefaultNsURI = defaultNs; mAttrCollector.reset(); /* 20-Feb-2006, TSa: Hmmh. Namespace default provider unfortunately * needs an advance warning... */ if (mNsDefaultProvider != null) { mMayHaveNsDefaults = mNsDefaultProvider.mayHaveNsDefaults(prefix, localName); } }
Method called by the stream reader to remove the topmost (start) element from the stack; called when an end element is encountered during parsing.
Returns:True if stack has more elements; false if not (that is, root element closed)
/** * Method called by the stream reader to remove the topmost (start) * element from the stack; * called when an end element is encountered during parsing. * * @return True if stack has more elements; false if not (that is, * root element closed) */
public final boolean pop() throws XMLStreamException { if (mCurrElement == null) { throw new IllegalStateException("Popping from empty stack"); } --mDepth; Element child = mCurrElement; Element parent = child.mParent; mCurrElement = parent; // Let's do simple recycling of Element instances... child.relink(mFreeElement); mFreeElement = child; // Need to purge namespaces? int nsCount = mNamespaces.size() - child.mNsOffset; if (nsCount > 0) { // 2 entries for each NS mapping: mLastNsContext = null; // let's invalidate ns ctxt too, if we had one mNamespaces.removeLast(nsCount); } return (parent != null); }
Method called to resolve element and attribute namespaces (in namespace-aware mode), and do optional validation using pluggable validator object.
Returns:Text content validation state that should be effective for the fully resolved element context
/** * Method called to resolve element and attribute namespaces (in * namespace-aware mode), and do optional validation using pluggable * validator object. * * @return Text content validation state that should be effective * for the fully resolved element context */
public int resolveAndValidateElement() throws XMLStreamException { if (mDepth == 0) { // just a simple sanity check throw new IllegalStateException("Calling validate() on empty stack."); } AttributeCollector ac = mAttrCollector; // Any namespace declarations? { int nsCount = ac.getNsCount(); if (nsCount > 0) { /* let's first invalidate old (possibly) shared ns ctxt too, * if we had one; new one can be created at a later point */ mLastNsContext = null; boolean internNsUris = mConfig.willInternNsURIs(); for (int i = 0; i < nsCount; ++i) { Attribute ns = ac.resolveNamespaceDecl(i, internNsUris); String nsUri = ns.mNamespaceURI; // note: for namespaces, prefix is stored as local name String prefix = ns.mLocalName; /* 18-Jul-2004, TSa: Need to check that 'xml' and 'xmlns' * prefixes are not re-defined (and 'xmlns' not even * defined to its correct ns). */ if (prefix == "xmlns") { // xmlns can never be declared, even to its correct URI mReporter.throwParseError(ErrorConsts.ERR_NS_REDECL_XMLNS); } else if (prefix == "xml") { // whereas xml is ok, as long as it's same URI: if (!nsUri.equals(XMLConstants.XML_NS_URI)) { mReporter.throwParseError(ErrorConsts.ERR_NS_REDECL_XML, nsUri, null); } /* 09-Feb-2006, TSa: Hmmh. Now, should this explicit * xml declaration be visible to the app? SAX API * seem to ignore it. */ //mNamespaces.addStrings(prefix, nsUri); } else { // ok, valid prefix, so far /* 17-Mar-2006, TSa: Unbinding default NS needs to * result in null being added: */ if (nsUri == null || nsUri.length() == 0) { nsUri = XmlConsts.DEFAULT_NAMESPACE_URI; } // The default ns binding needs special handling: if (prefix == null) { mCurrElement.mDefaultNsURI = nsUri; } /* But then let's ensure that URIs matching xml * and xmlns are not being bound to anything else */ if (internNsUris) { // identity comparison is ok: if (nsUri == XMLConstants.XML_NS_URI) { mReporter.throwParseError(ErrorConsts.ERR_NS_REDECL_XML_URI, prefix, null); } else if (nsUri == XMLConstants.XMLNS_ATTRIBUTE_NS_URI) { mReporter.throwParseError(ErrorConsts.ERR_NS_REDECL_XMLNS_URI); } } else { // need to check equals() if (nsUri.equals(XMLConstants.XML_NS_URI)) { mReporter.throwParseError(ErrorConsts.ERR_NS_REDECL_XML_URI, prefix, null); } else if (nsUri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) { mReporter.throwParseError(ErrorConsts.ERR_NS_REDECL_XMLNS_URI); } } /* and at any rate, binding needs to be added, to * be visible to the app (including def ns): */ mNamespaces.addStrings(prefix, nsUri); } } } } /* 20-Feb-2006, TSa: Any attribute defaults for namespace declaration * pseudo-attributes? */ if (mMayHaveNsDefaults) { mNsDefaultProvider.checkNsDefaults(this); } // Then, let's set element's namespace, if any: String prefix = mCurrElement.mPrefix; String ns; if (prefix == null) { // use default NS, if any ns = mCurrElement.mDefaultNsURI; } else if (prefix == "xml") { ns = XMLConstants.XML_NS_URI; } else { // Need to find namespace with the prefix: ns = mNamespaces.findLastFromMap(prefix); /* 07-Sep-2007, TSa: "no namespace" should now be indicated * by an empty string, however, due to historical reasons * let's be bit defensive and allow nulls for the same too */ if (ns == null || ns.length() == 0) { mReporter.throwParseError(ErrorConsts.ERR_NS_UNDECLARED, prefix, null); } } mCurrElement.mNamespaceURI = ns; // And finally, resolve attributes' namespaces too: int xmlidIx = ac.resolveNamespaces(mReporter, mNamespaces); mIdAttrIndex = xmlidIx; XMLValidator vld = mValidator; /* If we have no validator(s), nothing more to do, * except perhaps little bit of Xml:id handling: */ if (vld == null) { // no validator in use if (xmlidIx >= 0) { // need to normalize xml:id, still? ac.normalizeSpacesInValue(xmlidIx); } return XMLValidator.CONTENT_ALLOW_ANY_TEXT; } // Otherwise need to call relevant validation methods. /* First, a call to check if the element itself may be acceptable * within structure: */ vld.validateElementStart (mCurrElement.mLocalName, mCurrElement.mNamespaceURI, mCurrElement.mPrefix); // Then attributes, if any: int attrLen = ac.getCount(); if (attrLen > 0) { for (int i = 0; i < attrLen; ++i) { ac.validateAttribute(i, mValidator); } } /* And finally let's wrap things up to see what textual content * is allowed as child content, if any: */ return mValidator.validateElementAndAttributes(); }
Method called after parsing (but before returning) end element, to allow for pluggable validators to verify correctness of the content model for the closing element.
Returns:Validation state that should be effective for the parent element state
/** * Method called after parsing (but before returning) end element, * to allow for pluggable validators to verify correctness of * the content model for the closing element. * * @return Validation state that should be effective for the parent * element state */
public int validateEndElement() throws XMLStreamException { if (mValidator == null) { // should never be null if we get here return XMLValidator.CONTENT_ALLOW_ANY_TEXT; } int result = mValidator.validateElementEnd (mCurrElement.mLocalName, mCurrElement.mNamespaceURI, mCurrElement.mPrefix); if (mDepth == 1) { // root closing mValidator.validationCompleted(true); } return result; } /* /////////////////////////////////////////////////////////// // AttributeInfo methods (StAX2) /////////////////////////////////////////////////////////// */ @Override public final int getAttributeCount() { return mAttrCollector.getCount(); } @Override public final int findAttributeIndex(String nsURI, String localName) { return mAttrCollector.findIndex(nsURI, localName); }
Default implementation just indicates it does not know of such attributes; this because that requires DTD information that only some implementations have.
/** * Default implementation just indicates it does not know of such * attributes; this because that requires DTD information that only * some implementations have. */
@Override public final int getIdAttributeIndex() { if (mIdAttrIndex >= 0) { return mIdAttrIndex; } return (mValidator == null) ? -1 : mValidator.getIdAttrIndex(); }
Default implementation just indicates it does not know of such attributes; this because that requires DTD information that only some implementations have.
/** * Default implementation just indicates it does not know of such * attributes; this because that requires DTD information that only * some implementations have. */
@Override public final int getNotationAttributeIndex() { return (mValidator == null) ? -1 : mValidator.getNotationAttrIndex(); } /* /////////////////////////////////////////////////////////// // Implementation of NamespaceContext: /////////////////////////////////////////////////////////// */ @Override public final String getNamespaceURI(String prefix) { if (prefix == null) { throw new IllegalArgumentException(ErrorConsts.ERR_NULL_ARG); } if (prefix.length() == 0) { if (mDepth == 0) { // unexpected... but let's not err at this point /* 07-Sep-2007, TSa: Default/"no namespace" does map to * "URI" of empty String. */ return XmlConsts.DEFAULT_NAMESPACE_URI; } return mCurrElement.mDefaultNsURI; } if (prefix.equals(XMLConstants.XML_NS_PREFIX)) { return XMLConstants.XML_NS_URI; } if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) { return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; } /* Ok, need to find the match, if any; starting from end of the * list of active namespaces. Note that we can not count on prefix * being interned/canonicalized. */ return mNamespaces.findLastNonInterned(prefix); } @Override public final String getPrefix(String nsURI) { if (nsURI == null || nsURI.length() == 0) { throw new IllegalArgumentException("Illegal to pass null/empty prefix as argument."); } if (nsURI.equals(XMLConstants.XML_NS_URI)) { return XMLConstants.XML_NS_PREFIX; } if (nsURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) { return XMLConstants.XMLNS_ATTRIBUTE; } /* Ok, need to find the match, if any; starting from end of the * list of active namespaces. Note that we can not count on prefix * being interned/canonicalized. */ String prefix = null; // 29-Sep-2004, TSa: Need to check for namespace masking, too... String[] strs = mNamespaces.getInternalArray(); int len = mNamespaces.size(); main_loop: for (int index = len-1; index > 0; index -= 2) { if (nsURI.equals(strs[index])) { // Ok, is prefix masked? prefix = strs[index-1]; for (int j = index+1; j < len; j += 2) { if (strs[j] == prefix) { // masked! prefix = null; continue main_loop; } } // nah, it's good // 17-Mar-2006, TSa: ... but default NS has prefix null... if (prefix == null) { prefix = ""; } break main_loop; } } return prefix; } @Override public final Iterator<String> getPrefixes(String nsURI) { if (nsURI == null || nsURI.length() == 0) { throw new IllegalArgumentException("Illegal to pass null/empty prefix as argument."); } if (nsURI.equals(XMLConstants.XML_NS_URI)) { return DataUtil.singletonIterator(XMLConstants.XML_NS_PREFIX); } if (nsURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) { return DataUtil.singletonIterator(XMLConstants.XMLNS_ATTRIBUTE); } // 29-Sep-2004, TSa: Need to check for namespace masking, too... String[] strs = mNamespaces.getInternalArray(); int len = mNamespaces.size(); ArrayList<String> l = null; main_loop: for (int index = len-1; index > 0; index -= 2) { if (nsURI.equals(strs[index])) { // Ok, is prefix masked? String prefix = strs[index-1]; for (int j = index+1; j < len; j += 2) { if (strs[j] == prefix) { // masked! continue main_loop; } } // nah, it's good! if (l == null) { l = new ArrayList<String>(); } l.add(prefix); } } if (l == null) { return DataUtil.emptyIterator(); } return l.iterator(); } /* /////////////////////////////////////////////////////////// // ValidationContext /////////////////////////////////////////////////////////// */ @Override public final String getXmlVersion() { return mConfig.isXml11() ? XmlConsts.XML_V_11_STR : XmlConsts.XML_V_10_STR; } // Part of Stax2, see above: //public int getAttributeCount(); @Override public String getAttributeLocalName(int index) { return getAttrCollector().getLocalName(index); } @Override public String getAttributeNamespace(int index) { return getAttrCollector().getURI(index); } @Override public String getAttributePrefix(int index) { return getAttrCollector().getPrefix(index); } @Override public String getAttributeValue(int index) { return getAttrCollector().getValue(index); } @Override public String getAttributeValue(String nsURI, String localName) { int ix = findAttributeIndex(nsURI, localName); return (ix < 0) ? null : getAttributeValue(ix); } // Part of Stax2, see above: //public int findAttributeIndex(String nsURI, String localName); @Override public boolean isNotationDeclared(String name) { // !!! TBI return false; } @Override public boolean isUnparsedEntityDeclared(String name) { // !!! TBI return false; } @Override public String getBaseUri() { // !!! TBI return null; } @Override public final QName getCurrentElementName() { if (mDepth == 0) { return null; } String prefix = mCurrElement.mPrefix; /* 17-Mar-2006, TSa: We only map prefix to empty String because * some QName impls barf on nulls. Otherwise we will always * use null to indicate missing prefixes. */ if (prefix == null) { prefix = ""; } /* 03-Dec-2004, TSa: Maybe we can just reuse the last QName * object created, if we have same data? (happens if * state hasn't changed, or we got end element for a leaf * element, or repeating leaf elements) */ String nsURI = mCurrElement.mNamespaceURI; String ln = mCurrElement.mLocalName; /* Since we generally intern most Strings, can do identity * comparisons here: */ if (ln != mLastLocalName) { mLastLocalName = ln; mLastPrefix = prefix; mLastNsURI = nsURI; } else if (prefix != mLastPrefix) { mLastPrefix = prefix; mLastNsURI = nsURI; } else if (nsURI != mLastNsURI) { mLastNsURI = nsURI; } else { return mLastName; } QName n = QNameCreator.create(nsURI, ln, prefix); mLastName = n; return n; } // This was defined above for NamespaceContext //public String getNamespaceURI(String prefix); @Override public Location getValidationLocation() { return mReporter.getLocation(); } @Override public void reportProblem(XMLValidationProblem problem) throws XMLStreamException { mReporter.reportValidationProblem(problem); }
Method called by actual validator instances when attributes with default values have no explicit values for the element; if so, default value needs to be added as if it was parsed from the element.
/** * Method called by actual validator instances when attributes with * default values have no explicit values for the element; if so, * default value needs to be added as if it was parsed from the * element. */
@Override public int addDefaultAttribute(String localName, String uri, String prefix, String value) throws XMLStreamException { return mAttrCollector.addDefaultAttribute(localName, uri, prefix, value); } /* /////////////////////////////////////////////////////////// // Support for NsDefaultProvider /////////////////////////////////////////////////////////// */ public boolean isPrefixLocallyDeclared(String internedPrefix) { if (internedPrefix != null && internedPrefix.length() == 0) { // default ns internedPrefix = null; } int offset = mCurrElement.mNsOffset; for (int len = mNamespaces.size(); offset < len; offset += 2) { // both interned, can use identity comparison String thisPrefix = mNamespaces.getString(offset); if (thisPrefix == internedPrefix) { return true; } } return false; }
Callback method called by the namespace default provider. At this point we can trust it to only call this method with somewhat valid arguments (no dups etc).
/** * Callback method called by the namespace default provider. At * this point we can trust it to only call this method with somewhat * valid arguments (no dups etc). */
public void addNsBinding(String prefix, String uri) { // Unbind? (xml 1.1...) if ((uri == null) || (uri.length() == 0)) { uri = null; } // Default ns declaration? if ((prefix == null) || (prefix.length() == 0)) { prefix = null; mCurrElement.mDefaultNsURI = uri; } mNamespaces.addStrings(prefix, uri); } /* /////////////////////////////////////////////////////////// // Support for validation: /////////////////////////////////////////////////////////// */ public final void validateText(TextBuffer tb, boolean lastTextSegment) throws XMLStreamException { tb.validateText(mValidator, lastTextSegment); } public final void validateText(String contents, boolean lastTextSegment) throws XMLStreamException { mValidator.validateText(contents, lastTextSegment); } /* /////////////////////////////////////////////////////////// // Accessors: /////////////////////////////////////////////////////////// */ // // // Generic stack information: public final boolean isNamespaceAware() { return mNsAware; } // // // Generic stack information: public final boolean isEmpty() { return mDepth == 0; }
Returns:Number of open elements in the stack; 0 when parser is in prolog/epilog, 1 inside root element and so on.
/** * @return Number of open elements in the stack; 0 when parser is in * prolog/epilog, 1 inside root element and so on. */
public final int getDepth() { return mDepth; } // // // Information about element at top of stack: public final String getDefaultNsURI() { if (mDepth == 0) { throw new IllegalStateException("Illegal access, empty stack."); } return mCurrElement.mDefaultNsURI; } public final String getNsURI() { if (mDepth == 0) { throw new IllegalStateException("Illegal access, empty stack."); } return mCurrElement.mNamespaceURI; } public final String getPrefix() { if (mDepth == 0) { throw new IllegalStateException("Illegal access, empty stack."); } return mCurrElement.mPrefix; } public final String getLocalName() { if (mDepth == 0) { throw new IllegalStateException("Illegal access, empty stack."); } return mCurrElement.mLocalName; } public final boolean matches(String prefix, String localName) { if (mDepth == 0) { throw new IllegalStateException("Illegal access, empty stack."); } String thisPrefix = mCurrElement.mPrefix; if (prefix == null || prefix.length() == 0) { // no name space if (thisPrefix != null && thisPrefix.length() > 0) { return false; } } else { if (thisPrefix != prefix && !thisPrefix.equals(prefix)) { return false; } } String thisName = mCurrElement.mLocalName; return (thisName == localName) || thisName.equals(localName); } public final String getTopElementDesc() { if (mDepth == 0) { throw new IllegalStateException("Illegal access, empty stack."); } String name = mCurrElement.mLocalName; String prefix = mCurrElement.mPrefix; if (prefix == null) { // no name space return name; } return prefix + ":" + name; } // // // Namespace information:
Returns:Number of active prefix/namespace mappings for current scope, including mappings from enclosing elements.
/** * @return Number of active prefix/namespace mappings for current scope, * including mappings from enclosing elements. */
public final int getTotalNsCount() { return mNamespaces.size() >> 1; }
Returns:Number of active prefix/namespace mappings for current scope, NOT including mappings from enclosing elements.
/** * @return Number of active prefix/namespace mappings for current scope, * NOT including mappings from enclosing elements. */
public final int getCurrentNsCount() { // Need not check for empty stack; should return 0 properly return (mNamespaces.size() - mCurrElement.mNsOffset) >> 1; } public final String getLocalNsPrefix(int index) { int offset = mCurrElement.mNsOffset; int localCount = (mNamespaces.size() - offset); index <<= 1; // 2 entries, prefix/URI for each NS if (index < 0 || index >= localCount) { throwIllegalIndex(index >> 1, localCount >> 1); } return mNamespaces.getString(offset + index); } public final String getLocalNsURI(int index) { int offset = mCurrElement.mNsOffset; int localCount = (mNamespaces.size() - offset); index <<= 1; // 2 entries, prefix/URI for each NS if (index < 0 || index >= localCount) { throwIllegalIndex(index >> 1, localCount >> 1); } return mNamespaces.getString(offset + index + 1); } private void throwIllegalIndex(int index, int localCount) { throw new IllegalArgumentException("Illegal namespace index " +(index >> 1)+"; current scope only has " +(localCount >> 1)+" namespace declarations."); } // // // DTD-derived attribute information:
Returns:Schema (DTD, RNG, W3C Schema) based type of the attribute in specified index
/** * @return Schema (DTD, RNG, W3C Schema) based type of the attribute * in specified index */
@Override public final String getAttributeType(int index) { if (index == mIdAttrIndex && index >= 0) { // second check to ensure -1 is not passed return "ID"; } return (mValidator == null) ? WstxInputProperties.UNKNOWN_ATTR_TYPE : mValidator.getAttributeType(index); } }