package org.openprovenance.prov.model; import java.util.Hashtable; import java.util.Map; import javax.xml.XMLConstants; import javax.xml.namespace.QName; import org.openprovenance.prov.model.ProvUtilities; import org.openprovenance.prov.model.exception.QualifiedNameException; /** A class to manipulate Namespaces when creating, serializing and converting prov documents. * @author Luc Moreau * */ public class Namespace { private static ThreadLocal<Namespace> threadNamespace = new ThreadLocal<Namespace> () { protected synchronized Namespace initialValue () { return new Namespace(); } }; public static Namespace getThreadNamespace() { return threadNamespace.get(); } public static Namespace withThreadNamespace(Namespace ns) { return threadNamespace.get().set(ns); } private Namespace set(Namespace ns) { if (ns==null) return null; Hashtable<String, String> tmp1=new Hashtable<String, String>(); tmp1.putAll(ns.prefixes); Hashtable<String, String> tmp2=new Hashtable<String, String>(); tmp2.putAll(ns.namespaces); this.prefixes=tmp1; this.namespaces=tmp2; this.defaultNamespace=ns.defaultNamespace; this.parent=ns.parent; return this; } /** * Extends this Namespace with all the prefix/namespace registration of the Namespace received as argument. * @param ns the {@link Namespace} whose prefix/namespace registration are added to this {@link Namespace}. */ public void extendWith(Namespace ns) { if (ns==null) return; if (ns.getDefaultNamespace()!=null) { registerDefault(ns.getDefaultNamespace()); } for (String prefix: ns.prefixes.keySet()) { register(prefix, ns.prefixes.get(prefix)); } } protected Map<String, String> prefixes=new Hashtable<String, String>(); protected Map<String, String> namespaces=new Hashtable<String, String>(); private String defaultNamespace=null; private Namespace parent=null; public void setParent(Namespace parent) { this.parent=parent; } public Namespace getParent() { return parent; } public void addKnownNamespaces() { getPrefixes().put("prov",NamespacePrefixMapper.PROV_NS); getNamespaces().put(NamespacePrefixMapper.PROV_NS,"prov"); getPrefixes().put("xsd",NamespacePrefixMapper.XSD_NS); getNamespaces().put(NamespacePrefixMapper.XSD_NS,"xsd"); } public Namespace() {} public Namespace(Hashtable<String,String> pref) { for (java.util.Map.Entry<String,String> entry: pref.entrySet()) { prefixes.put(entry.getKey(),entry.getValue()); namespaces.put(entry.getValue(),entry.getKey()); } } public Namespace(Namespace other) { this.defaultNamespace=other.defaultNamespace; this.prefixes=new Hashtable<String, String>(); prefixes.putAll(other.prefixes); this.namespaces=new Hashtable<String,String>(); namespaces.putAll(other.namespaces); } public String getDefaultNamespace () { return defaultNamespace; } public void setDefaultNamespace(String defaultNamespace) { this.defaultNamespace=defaultNamespace; } public Map<String, String> getPrefixes() { return prefixes; } public Map<String, String> getNamespaces() { return namespaces; } public boolean check(String prefix, String namespace) { String knownAs=prefixes.get(prefix); return namespace==knownAs; } public void registerDefault(String namespace) { register(null,namespace); } public void register(String prefix, String namespace) { if ((prefix == null) || (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX))) { if (defaultNamespace == null) { defaultNamespace = namespace; } else { newPrefix(namespace); } } else { String old = prefixes.get(prefix); if (old == null) { prefixes.put(prefix, namespace); if (namespaces.get(namespace)==null) { // make sure we don't overwrite an existing namespace namespaces.put(namespace,prefix); } } else { newPrefix(namespace); } } } static final public String xmlns = "pre_"; int prefixCount = 0; public void newPrefix(String namespace) { boolean success = false; while (!success) { String old=namespaces.get(namespace); if (old!=null) return; int count = prefixCount++; String newPrefix = xmlns + count; //gensym a new prefix String oldPrefix = prefixes.get(newPrefix); // check that it hasn't been used yet if (oldPrefix == null) { prefixes.put(newPrefix, namespace); success = true; if (namespaces.get(namespace)==null) { // make sure we don't overwrite namespace namespaces.put(namespace, newPrefix); } } } } public void unregister(String prefix, String namespace) { String val=prefixes.get(prefix); if (val!=null){ if (val.equals(namespace)) { prefixes.remove(prefix); namespaces.remove(namespace); } } } public void unregisterDeafult(String namespace) { String val=getDefaultNamespace(); if (val!=null){ if (val.equals(namespace)) { setDefaultNamespace(null); } } } static ProvUtilities u=new ProvUtilities(); /* A Utility to find all namespaces (and associated prefixes). Prefixes are adopted first-come. * If a prefix is bound to another namespace, then a new prefix is generated for that namespace. */ static public Namespace gatherNamespaces(Document doc) { NamespaceGatherer gatherer=new NamespaceGatherer(); u.forAllStatementOrBundle(doc.getStatementOrBundle(), gatherer); Namespace ns=gatherer.getNamespace(); return ns; } static public Namespace gatherNamespaces(Bundle bundle) { NamespaceGatherer gatherer=new NamespaceGatherer(); u.forAllStatement(bundle.getStatement(), gatherer); gatherer.register(bundle.getId()); Namespace ns=gatherer.getNamespace(); return ns; } static public Namespace gatherNamespaces(Bundle bundle, ProvFactory pFactory) { NamespaceGatherer gatherer=new NamespaceGatherer(); u.forAllStatement(bundle.getStatement(), gatherer); gatherer.register(bundle.getId()); Namespace ns=gatherer.getNamespace(); Namespace ns2=pFactory.newNamespace(ns); return ns2; } public QualifiedName stringToQualifiedName(String id, ProvFactory pFactory) { if (id == null) return null; int index = id.indexOf(':'); if (index == -1) { String tmp = getDefaultNamespace(); if (tmp == null && parent != null) tmp = parent.getDefaultNamespace(); if (tmp==null) throw new NullPointerException("Namespace.stringToQualifiedName(: Null namespace for "+id); return pFactory.newQualifiedName(tmp, id, null); } String prefix = id.substring(0, index); String local = id.substring(index + 1, id.length()); //TODO: why have special cases here, prov and xsd are now declared prefixes in namespaces if ("prov".equals(prefix)) { return pFactory.newQualifiedName(NamespacePrefixMapper.PROV_NS, local, prefix); } else if ("xsd".equals(prefix)) { return pFactory.newQualifiedName(NamespacePrefixMapper.XSD_NS, // + "#", // RDF ns ends // in #, not // XML ns. local, prefix); } else { String tmp=prefixes.get(prefix); if (tmp==null) { if (parent!=null) { return parent.stringToQualifiedName(id, pFactory); } else { throw new QualifiedNameException("Namespace.stringToQualifiedName(): Null namespace for " + id + " namespace " + this); } } return pFactory.newQualifiedName(tmp, local, prefix); } } /** * Creates a qualified name, with given prefix and local name. The prefix needs to have been pre-registered. * Prefix is allowed to be null: in that case, the intended namespace is the default namespace * (see {@link Namespace#getDefaultNamespace()}) which must have been pre-registered. * @param prefix the prefix for the {@link QualifiedName} * @param local the local name for the {@link QualifiedName} * @param pFactory the factory method used to construct the {@link QualifiedName} * @return a {@link QualifiedName} * @throws QualifiedNameException if prefix is not pre-registered. * @throws NullPointerException if prefix is null and defaultnamespace has not been registered. */ public QualifiedName qualifiedName(String prefix, String local, ProvFactory pFactory) { if (prefix == null) { String tmp = getDefaultNamespace(); if (tmp == null && parent != null) tmp = parent.getDefaultNamespace(); if (tmp==null) throw new NullPointerException("Namespace.stringToQualifiedName(: Null namespace for "+ local); return pFactory.newQualifiedName(tmp, local, null); } //TODO: why have special cases here, prov and xsd are now declared prefixes in namespaces if ("prov".equals(prefix)) { return pFactory.newQualifiedName(NamespacePrefixMapper.PROV_NS, local, prefix); } else if ("xsd".equals(prefix)) { return pFactory.newQualifiedName(NamespacePrefixMapper.XSD_NS, // + "#", // RDF ns ends // in #, not // XML ns. local, prefix); } else { String tmp=prefixes.get(prefix); if (tmp==null) { if (parent!=null) { return parent.qualifiedName(prefix, local, pFactory); } else { throw new QualifiedNameException("Namespace.stringToQualifiedName(): Null namespace for " + prefix + ":" + local + " namespace " + this); } } return pFactory.newQualifiedName(tmp, local, prefix); } } static public String qualifiedNameToStringWithNamespace(QualifiedName name) { Namespace ns=Namespace.getThreadNamespace(); return ns.qualifiedNameToString(name); } public String qualifiedNameToString(QName name) { return qualifiedNameToString(name,null); } public String qualifiedNameToString(QualifiedName name) { return qualifiedNameToString(name,null); } /** * @param name the QualifiedName to convert to string * @param child argument used just for the purpose of debugging when throwing an exception * @return a string representation of the QualifiedName */ public String qualifiedNameToString(QualifiedName name, Namespace child) { if ((getDefaultNamespace()!=null) && (getDefaultNamespace().equals(name.getNamespaceURI()))) { return name.getLocalPart(); } else { String pref=getNamespaces().get(name.getNamespaceURI()); if (pref!=null) { return pref + ":" + name.getLocalPart(); } else { if (parent!=null) { return parent.qualifiedNameToString(name,this); } else throw new QualifiedNameException("unknown qualified name " + name + " with namespace " + toString() + ((child==null)? "" : (" and " + child))); } } } /** * @param name the QName to convert to string * @param child argument used just for the purpose of debugging when throwing an exception * @return a string representation of the QualifiedName */ public String qualifiedNameToString(QName name, Namespace child) { if ((getDefaultNamespace()!=null) && (getDefaultNamespace().equals(name.getNamespaceURI()))) { return name.getLocalPart(); } else { String pref=getNamespaces().get(name.getNamespaceURI()); if (pref!=null) { return pref + ":" + name.getLocalPart(); } else { if (parent!=null) { return parent.qualifiedNameToString(name,this); } else throw new QualifiedNameException("unknown qualified name " + name + " with namespace " + toString() + ((child==null)? "" : (" and " + child))); } } } public String toString() { return "[Namespace (" + defaultNamespace + ") " + prefixes + ", parent: " + parent + "]"; } }