/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2012 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution.xml.internal.stream;
import java.util.Iterator;
import javolution.text.CharArray;
import javolution.util.FastTable;
import javolution.xml.stream.NamespaceContext;
/**
* This class represents the namespaces stack while parsing.
*
* @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
* @version 3.2, April 2, 2005
*/
public final class NamespacesImpl implements NamespaceContext {
/**
* Holds the number of predefined namespaces.
*/
static final int NBR_PREDEFINED_NAMESPACES = 3;
/**
* Holds useful CharArray instances (non-static to avoid potential
* inter-thread corruption).
*/
final CharArray _nullNsURI = new CharArray(""); // No namespace URI.
final CharArray _defaultNsPrefix = new CharArray("");
final CharArray _xml = new CharArray("xml");
final CharArray _xmlURI = new CharArray(
"http://www.w3.org/XML/1998/namespace");
final CharArray _xmlns = new CharArray("xmlns");
final CharArray _xmlnsURI = new CharArray("http://www.w3.org/2000/xmlns/");
/**
* Holds the current nesting level.
*/
private int _nesting = 0;
/**
* Holds the currently mapped prefixes.
*/
CharArray[] _prefixes = new CharArray[16];
/**
* Holds the currently mapped namespaces.
*/
CharArray[] _namespaces = new CharArray[_prefixes.length];
/**
* Indicates if the prefix has to been written (when writing).
*/
boolean[] _prefixesWritten = new boolean[_prefixes.length];
/**
* Holds the number of prefix/namespace association per nesting level.
*/
int[] _namespacesCount = new int[16];
/**
* Holds the default namespace.
*/
CharArray _defaultNamespace = _nullNsURI;
/**
* Holds the default namespace index.
*/
int _defaultNamespaceIndex;
/**
* Default constructor.
*/
public NamespacesImpl() {
_prefixes[0] = _defaultNsPrefix;
_namespaces[0] = _nullNsURI;
_prefixes[1] = _xml;
_namespaces[1] = _xmlURI;
_prefixes[2] = _xmlns;
_namespaces[2] = _xmlnsURI;
_namespacesCount[0] = NBR_PREDEFINED_NAMESPACES;
}
// Implements NamespaceContext
public CharArray getNamespaceURI(CharSequence prefix) {
if (prefix == null)
throw new IllegalArgumentException("null prefix not allowed");
return getNamespaceURINullAllowed(prefix);
}
CharArray getNamespaceURINullAllowed(CharSequence prefix) {
if ((prefix == null) || (prefix.length() == 0))
return _defaultNamespace;
final int count = _namespacesCount[_nesting];
for (int i = count; --i >= 0;) {
if (_prefixes[i].equals(prefix))
return _namespaces[i];
}
return null; // Not bound.
}
// Implements NamespaceContext
public CharArray getPrefix(CharSequence uri) {
if (uri == null)
throw new IllegalArgumentException("null namespace URI not allowed");
return _defaultNamespace.equals(uri) ? _defaultNsPrefix : getPrefix(
uri, _namespacesCount[_nesting]);
}
CharArray getPrefix(CharSequence uri, int count) {
for (int i = count; --i >= 0;) {
CharArray prefix = _prefixes[i];
CharArray namespace = _namespaces[i];
if (namespace.equals(uri)) { // Find matching uri.
// Checks that the prefix has not been overwriten after being set.
boolean isPrefixOverwritten = false;
for (int j = i + 1; j < count; j++) {
if (prefix.equals(_prefixes[j])) {
isPrefixOverwritten = true;
break;
}
}
if (!isPrefixOverwritten)
return prefix;
}
}
return null; // Not bound.
}
// Implements NamespaceContext
public Iterator<CharArray> getPrefixes(CharSequence namespaceURI) {
FastTable<CharArray> prefixes = new FastTable<CharArray>();
for (int i = _namespacesCount[_nesting]; --i >= 0;) {
if (_namespaces[i].equals(namespaceURI)) {
prefixes.add(_prefixes[i]);
}
}
return prefixes.iterator();
}
// Null values are not allowed.
void setPrefix(CharArray prefix, CharArray uri) {
int index = _namespacesCount[_nesting];
_prefixes[index] = prefix;
_namespaces[index] = uri;
if (prefix.length() == 0) { // The default namespace is set.
_defaultNamespaceIndex = index;
_defaultNamespace = uri;
}
if (++_namespacesCount[_nesting] >= _prefixes.length)
resizePrefixStack();
}
// Used only by XMLStreamWriter (converts CharSequence to CharArray).
// Null values are not allowed.
void setPrefix(final CharSequence prefix, CharSequence uri,
boolean isWritten) {
final int index = _namespacesCount[_nesting];
_prefixesWritten[index] = isWritten;
final int prefixLength = prefix.length();
CharArray prefixTmp = _prefixesTmp[index];
if ((prefixTmp == null) || (prefixTmp.array().length < prefixLength)) {
_prefixesTmp[index] = new CharArray().setArray(
new char[prefixLength + 32], 0, 0);
prefixTmp = _prefixesTmp[index];
}
for (int i = 0; i < prefixLength; i++) {
prefixTmp.array()[i] = prefix.charAt(i);
}
prefixTmp.setArray(prefixTmp.array(), 0, prefixLength);
final int uriLength = uri.length();
CharArray namespaceTmp = _namespacesTmp[index];
if ((namespaceTmp == null) || (namespaceTmp.array().length < uriLength)) {
_namespacesTmp[index] = new CharArray().setArray(
new char[uriLength + 32], 0, 0);
namespaceTmp = _namespacesTmp[index];
}
for (int i = 0; i < uriLength; i++) {
namespaceTmp.array()[i] = uri.charAt(i);
}
namespaceTmp.setArray(namespaceTmp.array(), 0, uriLength);
// Sets the prefix using CharArray instances.
setPrefix(prefixTmp, namespaceTmp);
}
private CharArray[] _prefixesTmp = new CharArray[_prefixes.length];
private CharArray[] _namespacesTmp = new CharArray[_prefixes.length];
void pop() {
if (_namespacesCount[--_nesting] <= _defaultNamespaceIndex) {
searchDefaultNamespace();
}
}
private void searchDefaultNamespace() {
int count = _namespacesCount[_nesting];
for (int i = count; --i >= 0;) {
if (_prefixes[i].length() == 0) {
_defaultNamespaceIndex = i;
return;
}
}
throw new Error("Cannot find default namespace");
}
void push() {
_nesting++;
if (_nesting >= _namespacesCount.length) {
resizeNamespacesCount();
}
_namespacesCount[_nesting] = _namespacesCount[_nesting - 1];
}
public void reset() {
_defaultNamespace = _nullNsURI;
_defaultNamespaceIndex = 0;
_namespacesCount[0] = NBR_PREDEFINED_NAMESPACES;
_nesting = 0;
}
private void resizeNamespacesCount() {
final int oldLength = _namespacesCount.length;
final int newLength = oldLength * 2;
// Resizes namespaces counts.
int[] tmp = new int[newLength];
System.arraycopy(_namespacesCount, 0, tmp, 0, oldLength);
_namespacesCount = tmp;
}
// Resizes prefix mapping stack.
private void resizePrefixStack() {
final int oldLength = _prefixes.length;
final int newLength = oldLength * 2;
// Resizes prefixes.
CharArray[] tmp0 = new CharArray[newLength];
System.arraycopy(_prefixes, 0, tmp0, 0, oldLength);
_prefixes = tmp0;
// Resizes namespaces uri.
CharArray[] tmp1 = new CharArray[newLength];
System.arraycopy(_namespaces, 0, tmp1, 0, oldLength);
_namespaces = tmp1;
// Resizes prefix sets.
boolean[] tmp2 = new boolean[newLength];
System.arraycopy(_prefixesWritten, 0, tmp2, 0, oldLength);
_prefixesWritten = tmp2;
// Resizes temporary prefix (CharSequence to CharArray conversion).
CharArray[] tmp3 = new CharArray[newLength];
System.arraycopy(_prefixesTmp, 0, tmp3, 0, oldLength);
_prefixesTmp = tmp3;
// Resizes temporary namespaces (CharSequence to CharArray conversion).
CharArray[] tmp4 = new CharArray[newLength];
System.arraycopy(_namespacesTmp, 0, tmp4, 0, oldLength);
_namespacesTmp = tmp4;
}
}