diff --git a/src/main/java/com/ctc/wstx/sw/BaseNsStreamWriter.java b/src/main/java/com/ctc/wstx/sw/BaseNsStreamWriter.java index 403e53b7..ded11065 100644 --- a/src/main/java/com/ctc/wstx/sw/BaseNsStreamWriter.java +++ b/src/main/java/com/ctc/wstx/sw/BaseNsStreamWriter.java @@ -59,7 +59,7 @@ public abstract class BaseNsStreamWriter * (we are in repairing mode) */ final protected boolean mAutomaticNS; - + final protected EmptyElementHandler mEmptyElementHandler; /* @@ -169,7 +169,7 @@ public void setPrefix(String prefix, String uri) } /* 25-Sep-2004, TSa: Let's check that "xml" and "xmlns" are not - * (re-)defined to any other value, nor that value they + * (re-)defined to any other value, nor that value they * are bound to are bound to other prefixes. */ /* 01-Apr-2005, TSa: And let's not leave it optional: such @@ -193,7 +193,7 @@ public void setPrefix(String prefix, String uri) throwOutputError(ErrorConsts.ERR_NS_REDECL_XMLNS_URI, prefix); } } - + /* 05-Feb-2005, TSa: Also, as per namespace specs; the 'empty' * namespace URI can not be bound as a non-default namespace * (ie. for any actual prefix) @@ -334,9 +334,6 @@ protected void writeTypedAttribute(String prefix, String nsURI, String localName if (!mStartElementOpen) { throwOutputError(ErrorConsts.WERR_ATTR_NO_ELEM); } - if (mCheckAttrs) { // still need to ensure no duplicate attrs? - mCurrElem.checkAttrWrite(nsURI, localName); - } try { if (mValidator == null) { if (prefix == null || prefix.length() == 0) { @@ -346,7 +343,7 @@ protected void writeTypedAttribute(String prefix, String nsURI, String localName } } else { mWriter.writeTypedAttribute - (prefix, localName, nsURI, enc, mValidator, getCopyBuffer()); + (prefix, localName, nsURI, enc, mCurrElem.wrap(mValidator), getCopyBuffer()); } } catch (IOException ioe) { throw new WstxIOException(ioe); @@ -494,7 +491,7 @@ protected final void doWriteAttr(String localName, String nsURI, String prefix, throws XMLStreamException { if (mCheckAttrs) { // still need to ensure no duplicate attrs? - mCurrElem.checkAttrWrite(nsURI, localName); + mCurrElem.checkAttrWrite(nsURI, localName, prefix, value); } if (mValidator != null) { // No need to get it normalized... even if validator does normalize @@ -537,7 +534,7 @@ protected final void doWriteAttr(String localName, String nsURI, String prefix, throws XMLStreamException { if (mCheckAttrs) { // still need to ensure no duplicate attrs? - mCurrElem.checkAttrWrite(nsURI, localName); + mCurrElem.checkAttrWrite(nsURI, localName, prefix, new String(buf, start, len)); } if (mValidator != null) { // No need to get it normalized... even if validator does normalize @@ -763,4 +760,47 @@ protected abstract void writeStartOrEmpty(String localName, String nsURI) protected abstract void writeStartOrEmpty(String prefix, String localName, String nsURI) throws XMLStreamException; + + @Override + public int getAttributeCount() + { + return mCurrElem.getAttributeCount(); + } + + @Override + public String getAttributeLocalName(int index) + { + return mCurrElem.getAttributeLocalName(index); + } + + @Override + public String getAttributeNamespace(int index) + { + return mCurrElem.getAttributeNamespace(index); + } + + @Override + public String getAttributePrefix(int index) + { + return mCurrElem.getAttributePrefix(index); + } + + @Override + public String getAttributeValue(int index) + { + return mCurrElem.getAttributeValue(index); + } + + @Override + public String getAttributeValue(String nsURI, String localName) + { + return mCurrElem.getAttributeValue(nsURI, localName); + } + + @Override + public int findAttributeIndex(String nsURI, String localName) + { + return mCurrElem.findAttributeIndex(nsURI, localName); + } + } diff --git a/src/main/java/com/ctc/wstx/sw/SimpleOutputElement.java b/src/main/java/com/ctc/wstx/sw/SimpleOutputElement.java index bdf6c715..598494ec 100644 --- a/src/main/java/com/ctc/wstx/sw/SimpleOutputElement.java +++ b/src/main/java/com/ctc/wstx/sw/SimpleOutputElement.java @@ -21,6 +21,9 @@ import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; +import org.codehaus.stax2.validation.XMLValidationSchema; +import org.codehaus.stax2.validation.XMLValidator; + import com.ctc.wstx.compat.QNameCreator; import com.ctc.wstx.util.BijectiveNsMap; @@ -83,7 +86,7 @@ public final class SimpleOutputElement * Map used to check for duplicate attribute declarations, if * feature is enabled. */ - protected HashSet mAttrSet = null; + protected final AttrMap mAttrSet = new AttrMap(); /* /////////////////////////////////////////////////////////////////////// @@ -150,7 +153,7 @@ protected SimpleOutputElement createChild(String localName) * that when a child element has been opened, no more attributes * can be output. */ - mAttrSet = null; + mAttrSet.reset(); return new SimpleOutputElement(this, null, localName, mDefaultNsURI, mNsMapping); } @@ -161,7 +164,7 @@ protected SimpleOutputElement createChild(String localName) protected SimpleOutputElement reuseAsChild(SimpleOutputElement parent, String localName) { - mAttrSet = null; + mAttrSet.reset(); SimpleOutputElement poolHead = mParent; relink(parent, null, localName, mDefaultNsURI); return poolHead; @@ -171,7 +174,7 @@ protected SimpleOutputElement reuseAsChild(SimpleOutputElement parent, String prefix, String localName, String uri) { - mAttrSet = null; + mAttrSet.reset(); SimpleOutputElement poolHead = mParent; relink(parent, prefix, localName, uri); return poolHead; @@ -188,7 +191,7 @@ protected SimpleOutputElement createChild(String prefix, String localName, * that when a child element has been opened, no more attributes * can be output. */ - mAttrSet = null; + mAttrSet.reset(); return new SimpleOutputElement(this, prefix, localName, uri, mNsMapping); } @@ -255,17 +258,10 @@ public QName getName() { /////////////////////////////////////////////////////////////////////// */ - public void checkAttrWrite(String nsURI, String localName) + public void checkAttrWrite(String nsURI, String localName, String prefix, String value) throws XMLStreamException { - AttrName an = new AttrName(nsURI, localName); - if (mAttrSet == null) { - /* 13-Dec-2005, TSa: Should use a more efficient Set/Map value - * for this in future -- specifically one that could use - * ns/local-name pairs without intermediate objects - */ - mAttrSet = new HashSet(); - } + Attribute an = new Attribute(nsURI, localName, prefix, value); if (!mAttrSet.add(an)) { throw new XMLStreamException("Duplicate attribute write for attribute '"+an+"'"); } @@ -301,33 +297,207 @@ protected final void setRootNsContext(NamespaceContext ctxt) } } + + public int getAttributeCount() + { + return mAttrSet.size(); + } + + public String getAttributeLocalName(int index) + { + return mAttrSet.getAttributeLocalName(index); + } + + public String getAttributeNamespace(int index) + { + return mAttrSet.getAttributeNamespace(index); + } + + public String getAttributePrefix(int index) + { + return mAttrSet.getAttributePrefix(index); + } + + public String getAttributeValue(int index) + { + return mAttrSet.getAttributeValue(index); + } + + public String getAttributeValue(String nsURI, String localName) + { + return mAttrSet.getAttributeValue(nsURI, localName); + } + + public int findAttributeIndex(String nsURI, String localName) + { + return mAttrSet.findAttributeIndex(nsURI, localName); + } + /* /////////////////////////////////////////////////////////////////////// // Helper classes: /////////////////////////////////////////////////////////////////////// */ + XMLValidator wrap(XMLValidator validator) { + return new WrappedXMLValidator(validator); + } + + final class WrappedXMLValidator extends XMLValidator { + private final XMLValidator delegate; + + public WrappedXMLValidator(XMLValidator delegate) { + super(); + this.delegate = delegate; + } + + public String getSchemaType() { + return delegate.getSchemaType(); + } + + public XMLValidationSchema getSchema() { + return delegate.getSchema(); + } + + public void validateElementStart(String localName, String uri, String prefix) throws XMLStreamException { + delegate.validateElementStart(localName, uri, prefix); + } + + public String validateAttribute(String localName, String uri, String prefix, String value) throws XMLStreamException { + checkAttrWrite(uri, localName, prefix, value); + return delegate.validateAttribute(localName, uri, prefix, value); + } + + public String validateAttribute(String localName, String uri, String prefix, char[] valueChars, int valueStart, + int valueEnd) throws XMLStreamException { + checkAttrWrite(uri, localName, prefix, new String(valueChars, valueStart, valueEnd)); + return delegate.validateAttribute(localName, uri, prefix, valueChars, valueStart, valueEnd); + } + + public int validateElementAndAttributes() throws XMLStreamException { + return delegate.validateElementAndAttributes(); + } + + public int validateElementEnd(String localName, String uri, String prefix) throws XMLStreamException { + return delegate.validateElementEnd(localName, uri, prefix); + } + + public void validateText(String text, boolean lastTextSegment) throws XMLStreamException { + delegate.validateText(text, lastTextSegment); + } + + public void validateText(char[] cbuf, int textStart, int textEnd, boolean lastTextSegment) throws XMLStreamException { + delegate.validateText(cbuf, textStart, textEnd, lastTextSegment); + } + + public void validationCompleted(boolean eod) throws XMLStreamException { + delegate.validationCompleted(eod); + } + + public String getAttributeType(int index) { + return delegate.getAttributeType(index); + } + + public int getIdAttrIndex() { + return delegate.getIdAttrIndex(); + } + + public int getNotationAttrIndex() { + return delegate.getNotationAttrIndex(); + } + } + + final static class AttrMap { + /* 13-Dec-2005, TSa: Should use a more efficient Set/Map value + * for this in future -- specifically one that could use + * ns/local-name pairs without intermediate objects + */ + List mAttrList; + HashMap mAttrMap; + + public boolean add(Attribute an) { + if (mAttrList == null) { + mAttrList = new ArrayList(); + mAttrMap = new HashMap(); + } + mAttrList.add(an); + return mAttrMap.put(an, mAttrList.size() - 1) == null; + } + + public int size() { + return mAttrList == null ? 0 : mAttrList.size(); + } + + public String getAttributeLocalName(int index) { + return mAttrList == null ? null : mAttrList.get(index).mLocalName; + } + + public String getAttributeNamespace(int index) { + return mAttrList == null ? null : mAttrList.get(index).mNsURI; + } + + public String getAttributePrefix(int index) { + return mAttrList == null ? null : mAttrList.get(index).mPrefix; + } + + public String getAttributeValue(int index) { + return mAttrList == null ? null : mAttrList.get(index).mValue; + } + + public String getAttributeValue(String nsURI, String localName) { + if (mAttrMap == null) { + return null; + } + Integer index = mAttrMap.get(new Attribute(nsURI, localName, null, null)); + return index == null ? null : mAttrList.get(index.intValue()).mValue; + } + + // No idea where to get the type in a reader from + // public String getAttributeType(int index) { + // // TODO Auto-generated method stub + // return null; + // } + + public int findAttributeIndex(String nsURI, String localName) { + if (mAttrMap == null) { + return -1; + } + Integer index = mAttrMap.get(new Attribute(nsURI, localName, null, null)); + return index == null ? -1 : index.intValue(); + } + + public void reset() { + mAttrList = null; + mAttrMap = null; + } + } + /** * Simple key class used to represent two-piece (attribute) names; * first part being optional (URI), and second non-optional (local name). */ - final static class AttrName - implements Comparable + final static class Attribute + implements Comparable { final String mNsURI; final String mLocalName; + // mPrefix and mValue are intentionally not a part of {@link #equals(Object)} and {@link #hashCode()} + final String mPrefix; + final String mValue; /** * Let's cache the hash code, since although hash calculation is - * fast, hash code is needed a lot as this is always used as a + * fast, hash code is needed a lot as this is always used as a * HashSet/TreeMap key. */ final int mHashCode; - public AttrName(String nsURI, String localName) { + public Attribute(String nsURI, String localName, String prefix, String value) { mNsURI = (nsURI == null) ? "" : nsURI; mLocalName = localName; mHashCode = mNsURI.hashCode() * 31 ^ mLocalName.hashCode(); + mPrefix = prefix; + mValue = value; } @Override @@ -335,10 +505,10 @@ public boolean equals(Object o) { if (o == this) { return true; } - if (!(o instanceof AttrName)) { + if (!(o instanceof Attribute)) { return false; } - AttrName other = (AttrName) o; + Attribute other = (Attribute) o; String otherLN = other.mLocalName; // Local names are shorter, more varying: if (otherLN != mLocalName && !otherLN.equals(mLocalName)) { @@ -362,7 +532,7 @@ public int hashCode() { } @Override - public int compareTo(AttrName other) { + public int compareTo(Attribute other) { // Let's first order by namespace: int result = mNsURI.compareTo(other.mNsURI); if (result == 0) {