package org.exist.fluent;
import javax.xml.XMLConstants;
import org.w3c.dom.*;
import org.w3c.dom.Document;
/**
* A qualified name, consisting of a namespace and a local name.
*
* @author <a href="mailto:piotr@ideanest.com">Piotr Kaminski</a>
*/
public class QName extends javax.xml.namespace.QName implements Comparable<QName> {
private final String tag;
/**
* Create a qualified name.
*
* @param namespace the namespace of the qualified name, <code>null</code> if none
* @param localName the local part of the qualified name, must not be <code>null</code> or empty
* @param prefix the prefix to use for the qualified name, <code>null</code> if default (empty) prefix
*/
public QName(String namespace, String localName, String prefix) {
super(namespace == null ? XMLConstants.NULL_NS_URI : namespace, localName, prefix == null ? XMLConstants.DEFAULT_NS_PREFIX : prefix);
if (localName == null || localName.length() == 0) throw new IllegalArgumentException("null or empty local name");
if (prefix == null || prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
tag = localName;
} else {
tag = prefix + ":" + localName;
}
}
public int compareTo(QName o) {
return 2 * getNamespaceURI().compareTo(o.getNamespaceURI()) + getLocalPart().compareTo(o.getLocalPart());
}
/**
* Return whether this qualified name is actually qualified by a namespace or not.
*
* @return <code>true</code> if the qualified name has a namespace set, <code>false</code> if it's just a local name
*/
public boolean hasNamespace() {
return !getNamespaceURI().equals(XMLConstants.NULL_NS_URI);
}
/**
* Create an element in the given document whose tag is this qualified name. Correctly calls
* <code>createElement</code> or <code>createElementNS</code> depending on whether
* this name is actually qualified or not.
*
* @param doc the document to use to create the element
* @return a new element whose tag is this qualified name
*/
public Element createElement(Document doc) {
if (hasNamespace()) return doc.createElementNS(getNamespaceURI(), tag);
return doc.createElement(tag);
}
/**
* Create an attribute in the given document whose name is this qualified name. Correctly calls
* <code>createAttribute</code> or <code>createAttributeNS</code> depending on whether
* this name is actually qualified or not.
*
* @param doc the document to use to create the attribute
* @return a new attribute whose name is this qualified name
*/
public Attr createAttribute(Document doc) {
if (hasNamespace()) return doc.createAttributeNS(getNamespaceURI(), tag);
return doc.createAttribute(tag);
}
/**
* Set an attribute value on the given element, where the attribute's name is this qualified name.
* Correctly calls <code>setAttribute</code> or <code>setAttributeNS</code> depending on whether
* this name is actually qualified or not.
*
* @param elem the element on which to set the attribute
* @param value the value of the attribute
*/
public void setAttribute(Element elem, String value) {
if (hasNamespace()) {
elem.setAttributeNS(getNamespaceURI(), tag, value);
} else {
elem.setAttribute(tag, value);
}
}
/**
* Get the attribute with this qualified name from the given element. Correctly calls
* <code>getAttributeNode</code> or <code>getAttributeNodeNS</code> depending on whether
* this name is actually qualified or not.
*
* @param elem the element to read the attribute from
* @return the attribute node with this qualified name
*/
public Attr getAttributeNode(Element elem) {
if (hasNamespace()) return elem.getAttributeNodeNS(getNamespaceURI(), getLocalPart());
return elem.getAttributeNode(tag);
}
/**
* Get the XML tag for this qualified name, either prefix:localName or just localName if there
* is no prefix. Be careful: this tag is dependent on namespace context for correct interpretation.
* @return the XML tag for this qname, based on the qualified name's prefix and localName
*/
public String getTag() {
return tag;
}
/**
* Return the qualified name of the given node.
*
* @param node the target node
* @return the node's qualified name
*/
public static QName of(org.w3c.dom.Node node) {
String localName = node.getLocalName();
if (localName == null) localName = node.getNodeName();
return new QName(node.getNamespaceURI(), localName, node.getPrefix());
}
/**
* Parse the given tag into a qualified name within the context of the given namespace bindings.
*
* @param tag the tag to parse, in standard XML format
* @param namespaces the namespace bindings to use
* @return the qualified name of the given tag
*/
public static QName parse(String tag, NamespaceMap namespaces) {
return parse(tag, namespaces, namespaces.get(""));
}
/**
* Parse the given tag into a qualified name within the context of the given namespace bindings,
* overriding the default namespace binding with the given one. This is useful for parsing
* attribute names, where a lack of prefix should be interpreted as no namespace rather
* than the default namespace currently in effect.
*
* @param tag the tag to parse, in standard XML format
* @param namespaces the namespace bindings to use
* @param defaultNamespace the URI to use as the default namespace, in preference to any specified in the namespace bindings
* @return the qualified name of the given tag
*/
public static QName parse(String tag, NamespaceMap namespaces, String defaultNamespace) {
int colonIndex = tag.indexOf(':');
if (colonIndex == 0 || colonIndex == tag.length()-1) throw new IllegalArgumentException("illegal tag syntax '" + tag + "'");
String prefix = colonIndex == -1 ? "" : tag.substring(0, colonIndex);
String ns = prefix.length() > 0 ? namespaces.get(prefix) : defaultNamespace;
if (ns == null && prefix.length() > 0) throw new IllegalArgumentException("no namespace registered for tag prefix '" + prefix + "'");
return new QName(ns, tag.substring(colonIndex+1), prefix);
}
}