package com.fasterxml.aalto.in;
import java.util.*;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import org.codehaus.stax2.ri.EmptyIterator;
import org.codehaus.stax2.ri.SingletonIterator;
Non-transient implementation of NamespaceContext
. /**
* Non-transient implementation of {@link NamespaceContext}.
*/
public final class FixedNsContext
implements NamespaceContext
{
We can share and reuse "no bindings" instance.
/**
* We can share and reuse "no bindings" instance.
*/
public final static FixedNsContext EMPTY_CONTEXT;
static {
EMPTY_CONTEXT = new FixedNsContext(null, new String[0]);
}
/*
////////////////////////////////////////////////////////
// Persisted namespace information
////////////////////////////////////////////////////////
*/
We will keep a reference to the last namespace declaration
in effect at point when this instance was created. This is used
for lazy invalidation of instances: if last declaration for
an instance differs from the last seen by the reader, a new
context must be created.
/**
* We will keep a reference to the last namespace declaration
* in effect at point when this instance was created. This is used
* for lazy invalidation of instances: if last declaration for
* an instance differs from the last seen by the reader, a new
* context must be created.
*/
protected final NsDeclaration _lastDeclaration;
Array that contains prefix/namespace-uri pairs, ordered from the
most recent declaration to older ones. Array is always exactly
sized so there are no empty entries at the end.
/**
* Array that contains prefix/namespace-uri pairs, ordered from the
* most recent declaration to older ones. Array is always exactly
* sized so there are no empty entries at the end.
*/
protected final String[] _declarationData;
Temporary List used for constructing compact namespace binding
information that we will actually use.
/**
* Temporary List used for constructing compact namespace binding
* information that we will actually use.
*/
protected ArrayList<String> _tmpDecl = null;
private FixedNsContext(NsDeclaration lastDecl, String[] declData)
{
_lastDeclaration = lastDecl;
_declarationData = declData;
}
Method called to either reuse this context or construct a new
one. Reuse is ok if the currently active last declaration has
not changed since time this instance was created.
/**
* Method called to either reuse this context or construct a new
* one. Reuse is ok if the currently active last declaration has
* not changed since time this instance was created.
*/
public FixedNsContext reuseOrCreate(final NsDeclaration currLastDecl)
{
if (currLastDecl == _lastDeclaration) {
return this;
}
// [aalto-xml#29]: Do not try reusing EMPTY_CONTEXT
if (this == EMPTY_CONTEXT) {
ArrayList<String> tmp = new ArrayList<String>();
for (NsDeclaration curr = currLastDecl; curr != null; curr = curr.getPrev()) {
tmp.add(curr.getPrefix());
tmp.add(curr.getCurrNsURI());
}
return new FixedNsContext(currLastDecl, tmp.toArray(new String[tmp.size()]));
}
if (_tmpDecl == null) {
_tmpDecl = new ArrayList<String>();
} else {
_tmpDecl.clear();
}
for (NsDeclaration curr = currLastDecl; curr != null; curr = curr.getPrev()) {
_tmpDecl.add(curr.getPrefix());
_tmpDecl.add(curr.getCurrNsURI());
}
return new FixedNsContext(currLastDecl, _tmpDecl.toArray(new String[_tmpDecl.size()]));
}
/*
/////////////////////////////////////////////
// NamespaceContext API
/////////////////////////////////////////////
*/
@Override
public final String getNamespaceURI(String prefix)
{
/* First the known offenders; invalid args, 2 predefined xml
* namespace prefixes
*/
if (prefix == null) {
throw new IllegalArgumentException("Null prefix not allowed");
}
if (prefix.length() > 0) {
if (prefix.equals(XMLConstants.XML_NS_PREFIX)) {
return XMLConstants.XML_NS_URI;
}
if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
}
}
// here we count on never having null prefixes, just ""
String[] ns = _declarationData;
for (int i = 0, len = ns.length; i < len; i += 2) {
if (prefix.equals(ns[i])) {
return ns[i+1];
}
}
return null;
}
@Override
public final String getPrefix(String nsURI)
{
/* First the known offenders; invalid args, 2 predefined xml
* namespace prefixes
*/
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;
}
String[] ns = _declarationData;
main_loop:
for (int i = 1, len = ns.length; i < len; i += 2) {
if (nsURI.equals(ns[i])) {
// may still suffer from masking, let's check
String prefix = ns[i-1];
for (int j = i+1; j < len; j += 2) {
// Prefixes are interned, can do straight equality check
if (ns[j] == prefix) {
continue main_loop; // was masked!
}
}
return ns[i-1];
}
}
return null;
}
@Override
public final Iterator<String> getPrefixes(String nsURI)
{
// First the known offenders; invalid args, 2 predefined xml
// namespace prefixes
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 SingletonIterator.create(XMLConstants.XML_NS_PREFIX);
}
if (nsURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
return SingletonIterator.create(XMLConstants.XMLNS_ATTRIBUTE);
}
String[] ns = _declarationData;
String first = null;
ArrayList<String> all = null;
main_loop:
for (int i = 1, len = ns.length; i < len; i += 2) {
String currNS = ns[i];
if (currNS == nsURI || currNS.equals(nsURI)) {
// Need to ensure no masking occurs...
String prefix = ns[i-1];
for (int j = i+1; j < len; j += 2) {
// Prefixes are interned, can do straight equality check
if (ns[j] == prefix) {
continue main_loop; // was masked, need to ignore
}
}
if (first == null) {
first = prefix;
} else {
if (all == null) {
all = new ArrayList<String>();
all.add(first);
}
all.add(prefix);
}
}
}
if (all != null) {
return all.iterator();
}
if (first != null) {
return SingletonIterator.create(first);
}
return EmptyIterator.getInstance();
}
/*
/////////////////////////////////////////////
// Other methods
/////////////////////////////////////////////
*/
@Override
public String toString()
{
if (this == EMPTY_CONTEXT) {
return "[EMPTY non-transient NsContext]";
}
StringBuilder sb = new StringBuilder();
sb.append('[');
for (int i = 0, len = _declarationData.length; i < len; i += 2) {
if (i > 0) {
sb.append(", ");
}
sb.append('"').append(_declarationData[i]).append("\"->\"");
sb.append(_declarationData[i+1]).append('"');
}
sb.append(']');
return sb.toString();
}
}