/* Aalto XML processor
 *
 * Copyright (c) 2006- Tatu Saloranta, tatu.saloranta@iki.fi
 *
 * 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.fasterxml.aalto.out;

import java.util.*;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;

import com.fasterxml.aalto.util.EmptyIterator;

Simple container for information regarding an open element within stream writer output.

Note: these elements are designed to be reused within context of a single document output, ie. they are owned by the stream writer, and can be recycled by it, as necessary.

/** * Simple container for information regarding an open element within * stream writer output. *<p> * Note: these elements are designed to be reused within context of * a single document output, ie. they are owned by the stream writer, * and can be recycled by it, as necessary. */
final class OutputElement { final static byte BYTE_LT = (byte) '<'; final static byte BYTE_GT = (byte) '>'; final static byte BYTE_SLASH = (byte) '/'; public enum PrefixState { UNBOUND, OK, MISBOUND };
Reference to either the parent (enclosing element) of this element, when part of active output context; or link to next reusable unused element after this one (if not part of active context).
/** * Reference to either the parent (enclosing element) of this * element, when part of active output context; or link to next * reusable unused element after this one (if not part of active * context). */
OutputElement _parent;
Prefixed name used for serialization.
/** * Prefixed name used for serialization. */
WName _name;
Namespace of the element, whatever prefix part of _name maps to. Non-final to allow reuse.
/** * Namespace of the element, whatever prefix part of {@link #_name} * maps to. Non-final to allow reuse. */
String _uri; /* //////////////////////////////////////////// // Namespace binding/mapping information //////////////////////////////////////////// */
Namespace context end application may have supplied, and that (if given) should be used to augment explicitly defined bindings.
/** * Namespace context end application may have supplied, and that * (if given) should be used to augment explicitly defined bindings. */
//NamespaceContext _rootNsContext; String _defaultNsURI = ""; NsBinder _nsBinder = null; /* //////////////////////////////////////////// // Life-cycle //////////////////////////////////////////// */ private OutputElement() { _parent = null; _name = null; _uri = null; _nsBinder = null; _defaultNsURI = ""; } private OutputElement(OutputElement parent, WName name, String uri, NsBinder binder) { _parent = parent; _name = name; _uri = uri; _nsBinder = binder; _defaultNsURI = parent._defaultNsURI; } protected static OutputElement createRoot() { return new OutputElement(); }
Simplest factory method, which gets called when non-namespace element output method is called. It is, then, assumed to use the default namespce.
/** * Simplest factory method, which gets called when non-namespace * element output method is called. It is, then, assumed to * use the default namespce. */
protected OutputElement createChild(WName name) { return new OutputElement(this, name, _defaultNsURI, _nsBinder); }
Full factory method, used for 'normal' namespace qualified output methods.
/** * Full factory method, used for 'normal' namespace qualified output * methods. */
protected OutputElement createChild(WName name, String uri) { return new OutputElement(this, name, uri, _nsBinder); } /* //////////////////////////////////////////// // Instance reuse support //////////////////////////////////////////// */
Returns:New head of the recycle pool
/** * @return New head of the recycle pool */
protected OutputElement reuseAsChild(OutputElement parent, WName name) { OutputElement poolHead = _parent; relink(parent, name, _defaultNsURI); return poolHead; } protected OutputElement reuseAsChild(OutputElement parent, WName name, String nsURI) { OutputElement poolHead = _parent; relink(parent, name, nsURI); return poolHead; }
Method called to reuse a recycled instance, as is, with same name.
/** * Method called to reuse a recycled instance, as is, with same * name. */
public void relink(OutputElement parent) { _parent = parent; _nsBinder = parent._nsBinder; _defaultNsURI = parent._defaultNsURI; }
Method called to reuse a pooled instance, but with different name
/** * Method called to reuse a pooled instance, but with different * name */
private void relink(OutputElement parent, WName name, String uri) { _parent = parent; _name = name; _uri = uri; _nsBinder = parent._nsBinder; _defaultNsURI = parent._defaultNsURI; }
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(OutputElement poolHead) { _parent = poolHead; } /* //////////////////////////////////////////// // Public API, accessors //////////////////////////////////////////// */ public OutputElement getParent() { return _parent; } public boolean isRoot() { // (Virtual) Root element has no parent... return (_parent == null); } public WName getName() { return _name; } public String getLocalName() { return _name.getLocalName(); } public String getNonNullPrefix() { String p = _name.getPrefix(); return (p == null) ? "" : null; } public boolean hasPrefix() { return _name.hasPrefix(); }
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. */
public String getNameDesc() { return _name.toString(); } public String getNamespaceURI() { return _uri; } public String getNonNullNamespaceURI() { return (_uri == null) ? "" : _uri; } public String getDefaultNsURI() { return _defaultNsURI; } public boolean hasEmptyDefaultNs() { return (_defaultNsURI == null) || (_defaultNsURI.length() == 0); } public QName getQName() { return new QName(_uri, _name.getLocalName(), _name.getPrefix()); } /* //////////////////////////////////////////// // Public API, mutators //////////////////////////////////////////// */ public void setDefaultNsURI(String uri) { _defaultNsURI = uri; } public String generatePrefix(NamespaceContext rootNsContext, String prefixBase, int[] seqArr) { // Didn't have a mapping yet? Need to create one... if (_nsBinder == null) { _nsBinder = NsBinder.createEmpty(); } // no need to share though, won't be adding anything quite yet return _nsBinder.generatePrefix(prefixBase, rootNsContext, seqArr); } public void addPrefix(String prefix, String uri) { if (_nsBinder == null) { // Didn't have a mapping yet? Need to create one... _nsBinder = NsBinder.createEmpty(); } else { if (_parent != null && _parent._nsBinder == _nsBinder) { // Shared with parent(s)? Need to branch off to be able to modify _nsBinder = _nsBinder.createChild(); } } _nsBinder.addMapping(prefix, uri); } /* ////////////////////////////////////////////////// // NamespaceContext implementation, other ns funcs ////////////////////////////////////////////////// */ public String getNamespaceURI(String prefix) { if (prefix.length() == 0) { return _defaultNsURI; } if (_nsBinder != null) { String uri = _nsBinder.findUriByPrefix(prefix); if (uri != null) { return uri; } } return null; } public String getPrefix(String uri) { if (_defaultNsURI.equals(uri)) { return ""; } if (_nsBinder != null) { String prefix = _nsBinder.findPrefixByUri(uri); if (prefix != null) { return prefix; } } return null; } @SuppressWarnings("unchecked") public Iterator<String> getPrefixes(String uri, NamespaceContext rootNsContext) { List<String> l = null; if (_defaultNsURI.equals(uri)) { l = new ArrayList<String>(); l.add(""); } if (_nsBinder != null) { l = _nsBinder.getPrefixesBoundToUri(uri, l); } // How about the root namespace context? (if any) // Note: it's quite difficult to properly resolve masking if (rootNsContext != null) { Iterator<String> it = (Iterator<String>)rootNsContext.getPrefixes(uri); while (it.hasNext()) { String prefix = it.next(); if (prefix.length() == 0) { // default NS already checked continue; } // slow check... but what the heck if (l == null) { l = new ArrayList<String>(); } else if (l.contains(prefix)) { // double-defined... continue; } l.add(prefix); } } if (l == null) { return EmptyIterator.getInstance(); } return l.iterator(); }
Method similar to getPrefix, but one that will not accept the default namespace, only an explicit one. Usually used when trying to find a prefix for attributes.
/** * Method similar to {@link #getPrefix}, but one that will not accept * the default namespace, only an explicit one. Usually used when * trying to find a prefix for attributes. */
public String getExplicitPrefix(String uri, NamespaceContext rootNsContext) { if (_nsBinder != null) { String prefix = _nsBinder.findPrefixByUri(uri); if (prefix != null) { return prefix; } } if (rootNsContext != null) { String prefix = rootNsContext.getPrefix(uri); if (prefix != null) { // Hmmh... still can't use the default NS: if (prefix.length() > 0) { return prefix; } // ... should we try to find an explicit one? } } return null; }
Method that verifies that passed-in non-empty prefix indeed maps to specified non-empty namespace URI; and depending on how it goes returns a status for caller.
Returns:OK, if passed-in prefix matches matched-in namespace URI in current scope; UNBOUND if it's not bound to anything, and MISBOUND if it's bound to another URI.
/** * Method that verifies that passed-in non-empty prefix indeed maps * to specified non-empty namespace URI; and depending on how it goes * returns a status for caller. * * @return OK, if passed-in prefix matches matched-in namespace URI * in current scope; UNBOUND if it's not bound to anything, * and MISBOUND if it's bound to another URI. */
public PrefixState checkPrefixValidity(String prefix, String nsURI, NamespaceContext rootNsContext) { /* First thing is to see if specified prefix is bound to a namespace; * and if so, verify it matches with data passed in: */ /* Need to handle 'xml' prefix and its associated * URI; they are always declared by default */ if (prefix.equals("xml")) { return nsURI.equals(XMLConstants.XML_NS_URI) ? PrefixState.OK : PrefixState.MISBOUND; } // Nope checking some other namespace String act = (_nsBinder == null) ? null : _nsBinder.findUriByPrefix(prefix); if (act == null && rootNsContext != null) { act = rootNsContext.getNamespaceURI(prefix); } // Not (yet) bound... if (act == null) { return PrefixState.UNBOUND; } return (act == nsURI || act.equals(nsURI)) ? PrefixState.OK : PrefixState.MISBOUND; } public boolean isPrefixBoundTo(String prefix, String nsURI, NamespaceContext rootNsContext) { // First: test default namespace if (prefix == null || prefix.length() == 0) { return _defaultNsURI.equals(nsURI); } /* Need to handle 'xml' prefix and its associated * URI; they are always declared by default */ if ("xml".equals(prefix)) { return nsURI.equals(XMLConstants.XML_NS_URI); } // Nope checking some other namespace String act = (_nsBinder == null) ? null : _nsBinder.findUriByPrefix(prefix); if (act == null && rootNsContext != null) { act = rootNsContext.getNamespaceURI(prefix); } // Not (yet) bound... return (act != null) && (act == nsURI || act.equals(nsURI)); } public boolean isPrefixUnbound(String prefix, NamespaceContext rootNsContext) { // First: if an explict binding is found, can't be unbound String act = (_nsBinder == null) ? null : _nsBinder.findUriByPrefix(prefix); if (act != null && act.length() != 0) { return false; } if (prefix.equals("xml")) { // "xml" is always bound as well return false; } if (rootNsContext != null) { // or maybe root context has a binding? act = rootNsContext.getNamespaceURI(prefix); if (act != null && act.length() != 0) { return false; } } // Must be unbound return true; } /* //////////////////////////////////////////// // Comparison (etc) methods //////////////////////////////////////////// */ @Override public int hashCode() { return _name.hashCode(); } }