package client.net.sf.saxon.ce.tree.util;
import client.net.sf.saxon.ce.event.Receiver;
import client.net.sf.saxon.ce.om.NamespaceBinding;
import client.net.sf.saxon.ce.om.NodeInfo;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.Type;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
/**
* This class provides an iterator over the namespace codes representing the in-scope namespaces
* of any node. It relies on nodes to implement the method
* {@link client.net.sf.saxon.ce.om.NodeInfo#getDeclaredNamespaces(client.net.sf.saxon.ce.om.NamespaceBinding[])}.
*
* <p>The result does not include the XML namespace.</p>
*/
public class NamespaceIterator implements Iterator<NamespaceBinding> {
private NodeInfo element;
private int index;
private NamespaceBinding next;
private NamespaceBinding[] localDeclarations;
HashSet<String> undeclaredPrefixes;
/**
* Factory method: create an iterator over the in-scope namespace bindings for an element
* @param element the element (or other node) whose in-scope namespaces are required. If this
* is not an element, the result will be an empty iterator
* @return an iterator over the namespace bindings. A namespace binding represents
* a prefix-uri binding; the prefix and URI can be obtained by reference to the name pool. This
* iterator will represent all the in-scope namespaces, without duplicates, and respecting namespace
* undeclarations. It does not include the XML namespace.
*/
public static Iterator<NamespaceBinding> iterateNamespaces(NodeInfo element) {
if (element.getNodeKind() == Type.ELEMENT) {
return new NamespaceIterator(element);
} else {
return Collections.EMPTY_LIST.iterator();
}
}
/**
* Send all the in-scope namespaces for a node (except the XML namespace) to a specified receiver
* @param element the element in question (the method does nothing if this is not an element)
* @param receiver the receiver to which the namespaces are notified
*/
public static void sendNamespaces(NodeInfo element, Receiver receiver) throws XPathException {
if (element.getNodeKind() == Type.ELEMENT) {
boolean foundDefault = false;
for (Iterator<NamespaceBinding> iter = iterateNamespaces(element); iter.hasNext();) {
NamespaceBinding nb = iter.next();
if (nb.getPrefix().isEmpty()) {
foundDefault = true;
}
receiver.namespace(nb, 0);
}
if (!foundDefault) {
// see bug 5857
receiver.namespace(NamespaceBinding.DEFAULT_UNDECLARATION, 0);
}
}
}
private NamespaceIterator(NodeInfo element) {
this.element = element;
undeclaredPrefixes = new HashSet(8);
index = 0;
localDeclarations = element.getDeclaredNamespaces(null);
}
public boolean hasNext() {
if (next == null && index != 0) {
return false;
}
advance();
return next != null;
}
public NamespaceBinding next() {
return next;
}
private void advance() {
while (true) {
boolean ascend = index >= localDeclarations.length;
NamespaceBinding nsCode = null;
if (!ascend) {
nsCode = localDeclarations[index++];
ascend = nsCode == null;
}
if (ascend) {
element = element.getParent();
if (element != null && element.getNodeKind() == Type.ELEMENT) {
localDeclarations = element.getDeclaredNamespaces(localDeclarations);
index = 0;
continue;
} else {
next = null;
return;
}
}
String uri = nsCode.getURI();
String prefix = nsCode.getPrefix();
if (uri.isEmpty()) {
// this is an undeclaration
undeclaredPrefixes.add(prefix);
} else {
if (undeclaredPrefixes.add(prefix)) {
// it was added, so it's new, so return it
next = nsCode;
return;
}
// else it wasn't added, so we've already seen it
}
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.