/* Woodstox XML processor
 *
 * Copyright (c) 2005 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.sw;

import java.util.*;

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

import com.ctc.wstx.compat.QNameCreator;
import com.ctc.wstx.util.BijectiveNsMap;

Class that encapsulates information about a specific element in virtual output stack for namespace-aware writers. It provides support for URI-to-prefix mappings as well as namespace mapping generation.

One noteworthy feature of the class is that it is designed to allow "short-term recycling", ie. instances can be reused within context of a simple document output. While reuse/recycling of such lightweight object is often useless or even counter productive, here it may be worth using, due to simplicity of the scheme (basically using a very simple free-elements linked list).

/** * Class that encapsulates information about a specific element in virtual * output stack for namespace-aware writers. * It provides support for URI-to-prefix mappings as well as namespace * mapping generation. *<p> * One noteworthy feature of the class is that it is designed to allow * "short-term recycling", ie. instances can be reused within context * of a simple document output. While reuse/recycling of such lightweight * object is often useless or even counter productive, here it may * be worth using, due to simplicity of the scheme (basically using * a very simple free-elements linked list). */
public final class SimpleOutputElement extends OutputElementBase { /* /////////////////////////////////////////////////////////////////////// // Information about element itself: /////////////////////////////////////////////////////////////////////// */
Reference to the parent element, element enclosing this element. Null for root element. Non-final only to allow temporary pooling (on per-writer basis, to keep these short-lived).
/** * Reference to the parent element, element enclosing this element. * Null for root element. * Non-final only to allow temporary pooling * (on per-writer basis, to keep these short-lived). */
protected SimpleOutputElement mParent;
Prefix that is used for the element. Can not be final, since sometimes it needs to be dynamically generated and bound after creating the element instance.
/** * Prefix that is used for the element. Can not be final, since sometimes * it needs to be dynamically generated and bound after creating the * element instance. */
protected String mPrefix;
Local name of the element. Non-final only to allow reuse.
/** * Local name of the element. * Non-final only to allow reuse. */
protected String mLocalName;
Namespace of the element, whatever mPrefix maps to. Non-final only to allow reuse.
/** * Namespace of the element, whatever {@link #mPrefix} maps to. * Non-final only to allow reuse. */
protected String mURI; /* /////////////////////////////////////////////////////////////////////// // Attribute information /////////////////////////////////////////////////////////////////////// */
Map used to check for duplicate attribute declarations, if feature is enabled.
/** * Map used to check for duplicate attribute declarations, if * feature is enabled. */
protected HashSet<AttrName> mAttrSet = null; /* /////////////////////////////////////////////////////////////////////// // Life-cycle /////////////////////////////////////////////////////////////////////// */
Constructor for the virtual root element
/** * Constructor for the virtual root element */
private SimpleOutputElement() { super(); mParent = null; mPrefix = null; mLocalName = ""; mURI = null; } private SimpleOutputElement(SimpleOutputElement parent, String prefix, String localName, String uri, BijectiveNsMap ns) { super(parent, ns); mParent = parent; mPrefix = prefix; mLocalName = localName; mURI = uri; }
Method called to reuse a pooled instance.
@returnsChained pooled instance that should now be head of the reuse chain
/** * Method called to reuse a pooled instance. * * @returns Chained pooled instance that should now be head of the * reuse chain */
private void relink(SimpleOutputElement parent, String prefix, String localName, String uri) { super.relink(parent); mParent = parent; mPrefix = prefix; mLocalName = localName; mURI = uri; mNsMapping = parent.mNsMapping; mNsMapShared = (mNsMapping != null); mDefaultNsURI = parent.mDefaultNsURI; mRootNsContext = parent.mRootNsContext; } public static SimpleOutputElement createRoot() { return new SimpleOutputElement(); }
Simplest factory method, which gets called when a 1-argument element output method is called. It is, then, assumed to use the default namespce.
/** * Simplest factory method, which gets called when a 1-argument * element output method is called. It is, then, assumed to * use the default namespce. */
protected SimpleOutputElement createChild(String localName) { /* At this point we can also discard attribute Map; it is assumed * that when a child element has been opened, no more attributes * can be output. */ mAttrSet = null; return new SimpleOutputElement(this, null, localName, mDefaultNsURI, mNsMapping); }
Returns:New head of the recycle pool
/** * @return New head of the recycle pool */
protected SimpleOutputElement reuseAsChild(SimpleOutputElement parent, String localName) { mAttrSet = null; SimpleOutputElement poolHead = mParent; relink(parent, null, localName, mDefaultNsURI); return poolHead; } protected SimpleOutputElement reuseAsChild(SimpleOutputElement parent, String prefix, String localName, String uri) { mAttrSet = null; SimpleOutputElement poolHead = mParent; relink(parent, prefix, localName, uri); return poolHead; }
Full factory method, used for 'normal' namespace qualified output methods.
/** * Full factory method, used for 'normal' namespace qualified output * methods. */
protected SimpleOutputElement createChild(String prefix, String localName, String uri) { /* At this point we can also discard attribute Map; it is assumed * that when a child element has been opened, no more attributes * can be output. */ mAttrSet = null; return new SimpleOutputElement(this, prefix, localName, uri, mNsMapping); }
Method called to temporarily link this instance to a pool, to allow reusing of instances with the same reader.
/** * Method called to temporarily link this instance to a pool, to * allow reusing of instances with the same reader. */
protected void addToPool(SimpleOutputElement poolHead) { mParent = poolHead; } /* /////////////////////////////////////////////////////////////////////// // Public API, accessors /////////////////////////////////////////////////////////////////////// */ public SimpleOutputElement getParent() { return mParent; } @Override public boolean isRoot() { // (Virtual) Root element has no parent... return (mParent == null); }
Returns:String presentation of the fully-qualified name, in "prefix:localName" format (no URI). Useful for error and debugging messages.
/** * @return String presentation of the fully-qualified name, in * "prefix:localName" format (no URI). Useful for error and * debugging messages. */
@Override public String getNameDesc() { if (mPrefix != null && mPrefix.length() > 0) { return mPrefix + ":" +mLocalName; } if (mLocalName != null && mLocalName.length() > 0) { return mLocalName; } return "#error"; // unexpected case } public String getPrefix() { return mPrefix; } public String getLocalName() { return mLocalName; } public String getNamespaceURI() { return mURI; } public QName getName() { return QNameCreator.create(mURI, mLocalName, mPrefix); } /* /////////////////////////////////////////////////////////////////////// // Public API, ns binding, checking /////////////////////////////////////////////////////////////////////// */ public void checkAttrWrite(String nsURI, String localName) 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<AttrName>(); } if (!mAttrSet.add(an)) { throw new XMLStreamException("Duplicate attribute write for attribute '"+an+"'"); } } /* /////////////////////////////////////////////////////////////////////// // Public API, mutators /////////////////////////////////////////////////////////////////////// */ public void setPrefix(String prefix) { mPrefix = prefix; } @Override public void setDefaultNsUri(String uri) { mDefaultNsURI = uri; }
Note: this method can and will only be called before outputting the root element.
/** * Note: this method can and will only be called before outputting * the root element. */
@Override protected final void setRootNsContext(NamespaceContext ctxt) { mRootNsContext = ctxt; // Let's also figure out the default ns binding, if any: String defURI = ctxt.getNamespaceURI(""); if (defURI != null && defURI.length() > 0) { mDefaultNsURI = defURI; } } /* /////////////////////////////////////////////////////////////////////// // Helper classes: /////////////////////////////////////////////////////////////////////// */
Simple key class used to represent two-piece (attribute) names; first part being optional (URI), and second non-optional (local name).
/** * 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<AttrName> { final String mNsURI; final String mLocalName;
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 HashSet/TreeMap key.
/** * 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 * HashSet/TreeMap key. */
final int mHashCode; public AttrName(String nsURI, String localName) { mNsURI = (nsURI == null) ? "" : nsURI; mLocalName = localName; mHashCode = mNsURI.hashCode() * 31 ^ mLocalName.hashCode(); } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof AttrName)) { return false; } AttrName other = (AttrName) o; String otherLN = other.mLocalName; // Local names are shorter, more varying: if (otherLN != mLocalName && !otherLN.equals(mLocalName)) { return false; } String otherURI = other.mNsURI; return (otherURI == mNsURI || otherURI.equals(mNsURI)); } @Override public String toString() { if (mNsURI.length() > 0) { return "{"+mNsURI + "} " +mLocalName; } return mLocalName; } @Override public int hashCode() { return mHashCode; } @Override public int compareTo(AttrName other) { // Let's first order by namespace: int result = mNsURI.compareTo(other.mNsURI); if (result == 0) { result = mLocalName.compareTo(other.mLocalName); } return result; } } }