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

com.ctc.wstx.dtd.DTDElement Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/* Woodstox XML processor
 *
 * Copyright (c) 2004- Tatu Saloranta, [email protected]
 *
 * Licensed under the License specified in the file LICENSE which is
 * 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.dtd;

import java.util.*;

import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;

import org.codehaus.stax2.validation.XMLValidator;

import com.ctc.wstx.api.ReaderConfig;
import com.ctc.wstx.cfg.ErrorConsts;
import com.ctc.wstx.sr.InputProblemReporter;
import com.ctc.wstx.util.ExceptionUtil;
import com.ctc.wstx.util.PrefixedName;
import com.ctc.wstx.util.WordResolver;

/**
 * Class that contains element definitions from DTD.
 *

* Notes about thread-safety: this class is not thread-safe, since it does * not have to be, in general case. That is, the only instances that can * be shared are external subset instances, and those are used in read-only * manner (with the exception of temporary arrays constructed on-demand). */ public final class DTDElement { /* /////////////////////////////////////////////////// // Information about the element itself /////////////////////////////////////////////////// */ final PrefixedName mName; /** * Location of the (real) definition of the element; may be null for * placeholder elements created to hold ATTLIST definitions */ final Location mLocation; /** * Base validator object for validating content model of this element; * may be null for some simple content models (ANY, EMPTY). */ StructValidator mValidator; int mAllowedContent; /** * True if the DTD was parsed (and is to be used) in namespace-aware * mode. * Affects (name) validation amongst other things. */ final boolean mNsAware; /** * True if the DTD was parsed in xml1.1 compliant mode (referenced to * from an xml 1.1 document). * Affects (name) validation amongst other things. */ final boolean mXml11; /* /////////////////////////////////////////////////// // Attribute info /////////////////////////////////////////////////// */ HashMap mAttrMap = null; /** * Ordered list of attributes that have 'special' properties (attribute * is required, has a default value [regular or fixed]); these attributes * have to be specifically checked after actual values have been resolved. */ ArrayList mSpecAttrList = null; boolean mAnyFixed = false; /** * Flag set to true if there are any attributes that have either * basic default value, or #FIXED default value. */ boolean mAnyDefaults = false; /** * Flag that is set to true if there is at least one attribute that * has type that requires normalization and/or validation; that is, * is of some other type than CDATA. */ boolean mValidateAttrs = false; /** * Id attribute instance, if one already declared for this element; * can only have up to one such attribute per element. */ DTDAttribute mIdAttr; /** * Notation attribute instance, if one already declared for this element; * can only have up to one such attribute per element. */ DTDAttribute mNotationAttr; // // // !! If you add new attributes, make sure they get copied // // // in #define() method !! /* /////////////////////////////////////////////////// // Namespace declaration defaulting... /////////////////////////////////////////////////// */ /** * Set of namespace declarations with default values, if any * (regular ns pseudo-attr declarations are just ignored) */ HashMap mNsDefaults = null; // [String : DTDAttribute] /* /////////////////////////////////////////////////// // Life-cycle /////////////////////////////////////////////////// */ private DTDElement(Location loc, PrefixedName name, StructValidator val, int allowedContent, boolean nsAware, boolean xml11) { mName = name; mLocation = loc; mValidator = val; mAllowedContent = allowedContent; mNsAware = nsAware; mXml11 = xml11; } /** * Method called to create an actual element definition, matching * an ELEMENT directive in a DTD subset. */ public static DTDElement createDefined(ReaderConfig cfg, Location loc, PrefixedName name, StructValidator val, int allowedContent) { if (allowedContent == XMLValidator.CONTENT_ALLOW_UNDEFINED) { // sanity check ExceptionUtil.throwInternal("trying to use XMLValidator.CONTENT_ALLOW_UNDEFINED via createDefined()"); } return new DTDElement(loc, name, val, allowedContent, cfg.willSupportNamespaces(), cfg.isXml11()); } /** * Method called to create a "placeholder" element definition, needed to * contain attribute definitions. */ public static DTDElement createPlaceholder(ReaderConfig cfg, Location loc, PrefixedName name) { return new DTDElement(loc, name, null, XMLValidator.CONTENT_ALLOW_UNDEFINED, cfg.willSupportNamespaces(), cfg.isXml11()); } /** * Method called on placeholder element, to create a real instance that * has all attribute definitions placeholder had (it'll always have at * least one -- otherwise no placeholder was needed). */ public DTDElement define(Location loc, StructValidator val, int allowedContent) { verifyUndefined(); if (allowedContent == XMLValidator.CONTENT_ALLOW_UNDEFINED) { // sanity check ExceptionUtil.throwInternal("trying to use CONTENT_ALLOW_UNDEFINED via define()"); } DTDElement elem = new DTDElement(loc, mName, val, allowedContent, mNsAware, mXml11); // Ok, need to copy state collected so far: elem.mAttrMap = mAttrMap; elem.mSpecAttrList = mSpecAttrList; elem.mAnyFixed = mAnyFixed; elem.mValidateAttrs = mValidateAttrs; elem.mAnyDefaults = mAnyDefaults; elem.mIdAttr = mIdAttr; elem.mNotationAttr = mNotationAttr; elem.mNsDefaults = mNsDefaults; return elem; } /** * Method called to "upgrade" a placeholder using a defined element, * including adding attributes. */ public void defineFrom(InputProblemReporter rep, DTDElement definedElem, boolean fullyValidate) throws XMLStreamException { if (fullyValidate) { verifyUndefined(); } mValidator = definedElem.mValidator; mAllowedContent = definedElem.mAllowedContent; mergeMissingAttributesFrom(rep, definedElem, fullyValidate); } private void verifyUndefined() { if (mAllowedContent != XMLValidator.CONTENT_ALLOW_UNDEFINED) { // sanity check ExceptionUtil.throwInternal("redefining defined element spec"); } } /** * Method called by DTD parser when it has read information about * an attribute that belong to this element * * @return Newly created attribute Object if the attribute definition was * added (hadn't been declared yet); null if it's a duplicate, in which * case original definition sticks. */ public DTDAttribute addAttribute(InputProblemReporter rep, PrefixedName attrName, int valueType, DefaultAttrValue defValue, WordResolver enumValues, boolean fullyValidate) throws XMLStreamException { HashMap m = mAttrMap; if (m == null) { mAttrMap = m = new HashMap(); } List specList = defValue.isSpecial() ? getSpecialList() : null; DTDAttribute attr; int specIndex = (specList == null) ? -1 : specList.size(); switch (valueType) { case DTDAttribute.TYPE_CDATA: attr = new DTDCdataAttr(attrName, defValue, specIndex, mNsAware, mXml11); break; case DTDAttribute.TYPE_ENUMERATED: attr = new DTDEnumAttr(attrName, defValue, specIndex, mNsAware, mXml11, enumValues); break; case DTDAttribute.TYPE_ID: /* note: although ID attributes are not to have default value, * this is 'only' a validity constraint, and in dtd-aware-but- * not-validating mode it is apparently 'legal' to add default * values. Bleech. */ attr = new DTDIdAttr(attrName, defValue, specIndex, mNsAware, mXml11); break; case DTDAttribute.TYPE_IDREF: attr = new DTDIdRefAttr(attrName, defValue, specIndex, mNsAware, mXml11); break; case DTDAttribute.TYPE_IDREFS: attr = new DTDIdRefsAttr(attrName, defValue, specIndex, mNsAware, mXml11); break; case DTDAttribute.TYPE_ENTITY: attr = new DTDEntityAttr(attrName, defValue, specIndex, mNsAware, mXml11); break; case DTDAttribute.TYPE_ENTITIES: attr = new DTDEntitiesAttr(attrName, defValue, specIndex, mNsAware, mXml11); break; case DTDAttribute.TYPE_NOTATION: attr = new DTDNotationAttr(attrName, defValue, specIndex, mNsAware, mXml11, enumValues); break; case DTDAttribute.TYPE_NMTOKEN: attr = new DTDNmTokenAttr(attrName, defValue, specIndex, mNsAware, mXml11); break; case DTDAttribute.TYPE_NMTOKENS: attr = new DTDNmTokensAttr(attrName, defValue, specIndex, mNsAware, mXml11); break; default: // 18-Jan-2006, TSa: should never get here... ExceptionUtil.throwGenericInternal(); attr = null; // unreachable, but compiler wants it } DTDAttribute old = doAddAttribute(m, rep, attr, specList, fullyValidate); return (old == null) ? attr : null; } /** * Method called to add a definition of a namespace-declaration * pseudo-attribute with a default value. * * @return Attribute that acts as the placeholder, if the declaration * was added; null to indicate it * was a dup (there was an earlier declaration) */ public DTDAttribute addNsDefault (InputProblemReporter rep, PrefixedName attrName, int valueType, DefaultAttrValue defValue, boolean fullyValidate) throws XMLStreamException { /* Let's simplify handling a bit: although theoretically all * combinations of value can be used, let's really only differentiate * between CDATA and 'other' (for which let's use NMTOKEN) */ DTDAttribute nsAttr; switch (valueType) { case DTDAttribute.TYPE_CDATA: nsAttr = new DTDCdataAttr(attrName, defValue, -1, mNsAware, mXml11); break; default: // something else, default to NMTOKEN then nsAttr = new DTDNmTokenAttr(attrName, defValue, -1, mNsAware, mXml11); break; } // Ok. So which prefix are we to bind? Need to access by prefix... String prefix = attrName.getPrefix(); if (prefix == null || prefix.length() == 0) { // defult NS -> "" prefix = ""; } else { // non-default, use the local name prefix = attrName.getLocalName(); } if (mNsDefaults == null) { mNsDefaults = new HashMap(); } else { if (mNsDefaults.containsKey(prefix)) { return null; } } mNsDefaults.put(prefix, nsAttr); return nsAttr; } public void mergeMissingAttributesFrom(InputProblemReporter rep, DTDElement other, boolean fullyValidate) throws XMLStreamException { Map otherMap = other.getAttributes(); HashMap m = mAttrMap; if (m == null) { mAttrMap = m = new HashMap(); } //boolean anyAdded = false; if (otherMap != null && otherMap.size() > 0) { Iterator it = otherMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry me = (Map.Entry) it.next(); Object key = me.getKey(); // Should only add if no such attribute exists... if (!m.containsKey(key)) { // can only use as is, if it's not a special attr DTDAttribute newAttr = (DTDAttribute) me.getValue(); List specList; // otherwise need to clone if (newAttr.isSpecial()) { specList = getSpecialList(); newAttr = newAttr.cloneWith(specList.size()); } else { specList = null; } doAddAttribute(m, rep, newAttr, specList, fullyValidate); } } } HashMap otherNs = other.mNsDefaults; if (otherNs != null) { if (mNsDefaults == null) { mNsDefaults = new HashMap(); } Iterator it = otherNs.entrySet().iterator(); while (it.hasNext()) { Map.Entry me = (Map.Entry) it.next(); Object key = me.getKey(); // Should only add if no such attribute exists... if (!mNsDefaults.containsKey(key)) { mNsDefaults.put(key, me.getValue()); } } } } /** * @return Earlier declaration of the attribute, if any; null if * this was a new attribute */ private DTDAttribute doAddAttribute(Map attrMap, InputProblemReporter rep, DTDAttribute attr, List specList, boolean fullyValidate) throws XMLStreamException { PrefixedName attrName = attr.getName(); // Maybe we already have it? If so, need to ignore DTDAttribute old = (DTDAttribute) attrMap.get(attrName); if (old != null) { rep.reportProblem(null, ErrorConsts.WT_ATTR_DECL, ErrorConsts.W_DTD_DUP_ATTR, attrName, mName); return old; } switch (attr.getValueType()) { case DTDAttribute.TYPE_ID: // Only one such attribute per element (Specs, 1.0#3.3.1) if (fullyValidate && mIdAttr != null) { rep.throwParseError("Invalid id attribute \"{0}\" for element <{1}>: already had id attribute \""+mIdAttr.getName()+"\"", attrName, mName); } mIdAttr = attr; break; case DTDAttribute.TYPE_NOTATION: // Only one such attribute per element (Specs, 1.0#3.3.1) if (fullyValidate && mNotationAttr != null) { rep.throwParseError("Invalid notation attribute '"+attrName+"' for element <"+mName+">: already had notation attribute '"+mNotationAttr.getName()+"'"); } mNotationAttr = attr; break; } attrMap.put(attrName, attr); if (specList != null) { specList.add(attr); } if (!mAnyFixed) { mAnyFixed = attr.isFixed(); } if (!mValidateAttrs) { mValidateAttrs = attr.needsValidation(); } if (!mAnyDefaults) { mAnyDefaults = attr.hasDefaultValue(); } return null; } /* /////////////////////////////////////////////////// // Public API, accessors: /////////////////////////////////////////////////// */ public PrefixedName getName() { return mName; } public String toString() { return mName.toString(); } public String getDisplayName() { return mName.toString(); } public Location getLocation() { return mLocation; } public boolean isDefined() { return (mAllowedContent != XMLValidator.CONTENT_ALLOW_UNDEFINED); } /** * @return Constant that identifies what kind of nodes are in general * allowed inside this element. */ public int getAllowedContent() { return mAllowedContent; } /** * Specialized accessor used by non-validating but typing 'validator': * essentially, used to figure out whether #PCDATA is allowed or not; * and based on that, return one of 2 allowable text values (only * space, or anything). This is the relevant subset in non-validating * modes, needed to properly type resulting character events. */ public int getAllowedContentIfSpace() { int vld = mAllowedContent; return (vld <= XMLValidator.CONTENT_ALLOW_WS) ? XMLValidator.CONTENT_ALLOW_WS_NONSTRICT : XMLValidator.CONTENT_ALLOW_ANY_TEXT; } public HashMap getAttributes() { return mAttrMap; } public int getSpecialCount() { return (mSpecAttrList == null) ? 0 : mSpecAttrList.size(); } public List getSpecialAttrs() { return mSpecAttrList; } /** * @return True if at least one of the attributes has type other than * CDATA; false if not */ public boolean attrsNeedValidation() { return mValidateAttrs; } public boolean hasFixedAttrs() { return mAnyFixed; } public boolean hasAttrDefaultValues() { return mAnyDefaults; } public DTDAttribute getIdAttribute() { return mIdAttr; } public DTDAttribute getNotationAttribute() { return mNotationAttr; } public boolean hasNsDefaults() { return (mNsDefaults != null); } /* /////////////////////////////////////////////////// // Public API, factory methods: /////////////////////////////////////////////////// */ public StructValidator getValidator() { return (mValidator == null) ? null : mValidator.newInstance(); } protected HashMap getNsDefaults() { return mNsDefaults; } /* /////////////////////////////////////////////////// // Internal methods /////////////////////////////////////////////////// */ private List getSpecialList() { ArrayList l = mSpecAttrList; if (l == null) { mSpecAttrList = l = new ArrayList(); } return l; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy