All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ctc.wstx.sr.InputElementStack Maven / Gradle / Ivy

Go to download

Woodstox is a high-performance XML processor that implements Stax (JSR-173), SAX2 and Stax2 APIs

There is a newer version: 7.1.0
Show newest version
/* Woodstox XML processor
 *
 * Copyright (c) 2004- Tatu Saloranta, [email protected]
 *
 * 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 {@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). */ 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). */ protected final StringVector mNamespaces = new StringVector(64); /** * 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. */ 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. */ 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)). */ 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. */ 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. */ 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 {@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. */ 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 */ 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. * * @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. * * @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. * * @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. */ @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. */ @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 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 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(); } 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. */ @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). */ 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; } /** * @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: /** * @return Number of active prefix/namespace mappings for current scope, * including mappings from enclosing elements. */ public final int getTotalNsCount() { return mNamespaces.size() >> 1; } /** * @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: /** * @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); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy