package com.openMap1.mapper.core; import java.util.Iterator; import java.util.Vector; import com.openMap1.mapper.util.GenUtil; import javax.xml.namespace.NamespaceContext; import com.openMap1.mapper.core.NamespaceException; import com.openMap1.mapper.GlobalMappingParameters; import com.openMap1.mapper.Namespace; /** * The set of namespaces used in an XML document * @author robert * */ public class NamespaceSet implements NamespaceContext { private Vector<namespace> namespaces; /** * constructor for an empty set of namespaces */ public NamespaceSet() { namespaces = new Vector<namespace>(); } /** * constructor from the namespaces of a mapping set * @param gmp */ public NamespaceSet(GlobalMappingParameters gmp)throws NamespaceException { namespaces = new Vector<namespace>(); if (gmp != null) for (Iterator<Namespace> it = gmp.getNameSpaces().iterator();it.hasNext();) {namespaces.add(new namespace(it.next()));} } /** * add a new namespace to the set of namespaces * @param ns the namespace to be added. * It is not allowed to add a namespace with the same prefix * as an existing namespace, and a different URI; * but you can have several prefixes for the same URI */ public void addNamespace(namespace ns) throws NamespaceException { if (getByPrefix(ns.prefix()) != null) { String previousURI = getByPrefix(ns.prefix()).URI(); if (!(previousURI.equals(ns.URI()))) {throw new NamespaceException("Cannot add another namespace with existing prefix '" + ns.prefix() + "' and URI '" + ns.URI() + "' not equal to '" + previousURI + "'");} } else {namespaces.addElement(ns);} } /** * remove from the set one namespace with the uri, if it exists * @param uri */ public void removeOneNamespace(String uri) { int remove = -1; for (int i = 0; i < namespaces.size();i++) { namespace ns = namespaces.get(i); if (ns.URI().equals(uri)) remove = i; } if (remove > -1) namespaces.remove(remove); } /** * @param prefix * @return a namespace set identical to this one, except that if this * set contains any default namespace with prefix "", that namespace is * altered to have the given prefix * @throws NamespaceException */ public NamespaceSet withPrefixForDefaultNamespace(String prefix) throws NamespaceException { NamespaceSet withPrefix = new NamespaceSet(); for (Iterator<namespace> it = namespaces.iterator(); it.hasNext();) { namespace next = it.next(); if (next.prefix().equals("")) withPrefix.addNamespace(new namespace(prefix,next.URI())); else withPrefix.addNamespace(next); } return withPrefix; } /** * @return a namespace prefix which does not clash with any existing prefix in the set */ public String nonClashPrefix() { int index = 0; boolean clash = true; String prefix = "p"; while (clash) { prefix = "p" + index; clash = (getByPrefix(prefix)!= null); index++; } return prefix; } //----------------------------------------------------------------------------------------------- // interface NamespaceContext //----------------------------------------------------------------------------------------------- /** * Get Namespace URI bound to a prefix */ public String getNamespaceURI(String prefix) { if (getByPrefix(prefix) != null) return getByPrefix(prefix).URI(); return null; } /** * get a prefix bound to a namespace URI. * If there is more than one, pick an arbitrary one */ public String getPrefix(String namespaceURI) { if (getByURI(namespaceURI) != null) return getByURI(namespaceURI).prefix(); return null; } /** * get all prefixes bound to a namespace URI. */ public Iterator<String> getPrefixes(String namespaceURI) { Vector<String> prefixes = new Vector<String>(); for (int i = 0; i < size(); i++) { namespace temp = namespaces.elementAt(i); if (temp.URI().equals(namespaceURI)) prefixes.add(temp.prefix()); } return prefixes.iterator(); } //----------------------------------------------------------------------------------------------- // //----------------------------------------------------------------------------------------------- /** * @return the number of namespaces in the set */ public int size() {return namespaces.size();} /** * write the namespaces to some output (generally System.out, for test purposes) */ public void write() { GenUtil.message("Writing namespaces: "); for (int i = 0; i < size(); i++) { namespace temp = namespaces.elementAt(i); GenUtil.message(temp.prefix() + ": '" + temp.URI() + "'"); } } /** * get the namespace with given prefix, or null if there is none */ public namespace getByPrefix(String prefix) { int i; namespace res, temp; res = null; for (i = 0; i < size(); i++) { temp = namespaces.elementAt(i); if (temp.prefix().equals(prefix)) res = temp; } return res; } /** * get an arbitrary one of the namespaces with given URI, or null if there is none */ public namespace getByURI(String URI) { int i; namespace res, temp; res = null; for (i = 0; i < size(); i++) { temp = namespaces.elementAt(i); if (temp.URI().equals(URI)) res = temp; } return res; } /** * get the namespace with given index */ public namespace getByIndex(int i) { namespace res = null; if ((i > -1) && (i < size())) { res = namespaces.elementAt(i); } else {GenUtil.message("Namespace index " + i + " is not between 0 and " + size() + ".");} return res; } /** * true if this namespace set has a default namespace */ public boolean hasDefault() {return (getByPrefix("") != null);} /** * convert the prefix of a tag name from the prefix used * in this namespace set, to the prefix used for the same URI * in some other namespace set 'toNS'. * * Write an error message and return null if: * (a) the prefix does not occur in this namespace set, or * (b) the URI does not occur in the target namespace set. * * If a tag name has no prefix (prefix(tagName) = "") * and this namespace set has a default namespace (prefix = ""), * the tag name is assumed to be in the default namespace of this set, * and its prefix converted appropriately. * * If a tag name has no prefix (prefix(tagName) == "") * and this namespace set has no default prefix, * the tag name is assumed to be in no namespace, and is not changed. * */ public String convertPrefix(String tagName, NamespaceSet toNSS) throws NamespaceException { String res,pref; namespace thisNS, thatNS; res = null; pref = prefix(tagName); //cannot be null thisNS = getByPrefix(pref); /* if pref = "" and this namespace set has a default namespace, thisNS will be the default namespace. */ if (thisNS != null) { thatNS = toNSS.getByURI(thisNS.URI()); if (thatNS != null) { res = newPrefix(tagName,thatNS.prefix()); } else { throw new NamespaceException("Error converting prefixes: no namespace with URI '" + thisNS.URI() + "' in target namespace set, " + "to match namespace in source namespace set with prefix '" + pref + "'"); } } else { if (pref.equals("")) // as this NS was not found, the node is in no namespace { res = tagName; } else throw new NamespaceException("Error converting prefixes: no namespace with prefix '" + pref + "' from node name '" + tagName + "'. in source namespace set."); } return res; } /** * compare a nodeName in an XPath in this XMLFile (with its namespace set) * with a nodeName in an XPath which may be in another XMLFile */ public boolean equalNodeName(String thisNodeName,NamespaceSet thatNSSet,String thatNodeName) throws NamespaceException { return (convertPrefix(thisNodeName,thatNSSet).equals(thatNodeName)); } /* * Extract a namespace prefix, or "" if there is none, * taking account of 'axes' before the prefix. * The prefix does not have to be in this namespace set. * Cannot return null. */ public static String prefix(String nodeName) { int i; String res, temp; res = ""; temp = nodeName; if (temp.length() > 2) for (i = 1; i < temp.length()-1; i++) { // pick up substrings before a single ':' if (temp.charAt(i) == ':') {res = temp.substring(0,i);} } return res; } /** * Strip off the prefix to give the local name */ public static String localName(String nodeName) { String res, temp, pref; res = null; if (nodeName != null) { temp = nodeName; pref = prefix(nodeName); // cannot be null if (pref.equals("")) // no prefix to strip off { res = temp; } else // strip off prefix and ':' { res = temp.substring(pref.length()+1); } } else GenUtil.message("Null node name"); return res; } /* convert a node name, so it has a new prefix, * taking account axes before the prefix. * newPrefix = "" if there is to be no prefix. */ public static String newPrefix(String nodeName, String newPrefix) throws NamespaceException { String res; res = null; if ((nodeName != null) && (newPrefix !=null)) { if (newPrefix.equals("")) {res = localName(nodeName);} else {res = newPrefix + ":" + localName(nodeName);} } else throw new NamespaceException("Null node or prefix name"); return res; } /** * Find the namespace with this URI in the namespaceSet; * or if you cannot find it, make up a new prefix, create the namespace, * and add it to the set. * Return the namespace. */ public namespace findOrAddNamespace(String URI) throws NamespaceException { namespace ns = getByURI(URI); if (ns == null) { int i = 0; // keep boosting i until there is no prefix name clash boolean added = false; while (!added) { String prefix = "p" + (size() + i); i++; if (getByPrefix(prefix)== null)// only add if there is no prefix clash { ns = new namespace(prefix,URI); addNamespace(ns); added = true; } } } return ns; } }