Namespace (final String prefix, final String uri) { // This is a rewrite of the JDOM 1 getNamespace() to use // java.util.concurrent. The motivation is: // 1. avoid having to create a new NamespaceKey for each query. // 2. avoid a 'big' synchronisation bottleneck in the Namespace class. // 3. no-memory-lookup for pre-existing Namespaces... (avoid 'new' and // most String methods that allocte memory (like trim()) if (uri == null) { if (prefix == null || NS_PREFIX_DEFAULT.equals(prefix)) { return NO_NAMESPACE; } // we have an attempt for some prefix // (not "" or it would have found NO_NAMESPACE) on the null URI throw new IllegalNameException("", "namespace", "Namespace URIs must be non-null and non-empty Strings"); } // must have checked for 'null' uri else namespacemap throws NPE // do not 'trim' uri's any more see issue #50 ConcurrentMap<String, Namespace> urimap = namespacemap.get(uri); if (urimap == null) { // no Namespaces yet with that URI. // Ensure proper naming String reason; if ((reason = Verifier.checkNamespaceURI(uri)) != null) { throw new IllegalNameException(uri, "Namespace URI", reason); } urimap = new ConcurrentHashMap<>(); final ConcurrentMap<String, Namespace> xmap = namespacemap.putIfAbsent(uri, urimap); if (xmap != null) { // some other thread registered this URI between when we // first checked, and when we got a new map created. // we must use the already-registered value. urimap = xmap; } } // OK, we have a container for the URI, let's search on the prefix. Namespace ns = urimap.get(prefix == null ? NS_PREFIX_DEFAULT : prefix); if (ns != null) { // got one. return ns; } // OK, no namespace yet for that uri/prefix // validate the prefix (the uri is already validated). if (NS_URI_DEFAULT.equals(uri)) { // we have an attempt for some prefix // (not "" or it would have found NO_NAMESPACE) on the "" URI // note, we have already done this check for 'null' uri above. throw new IllegalNameException("", "namespace", "Namespace URIs must be non-null and non-empty Strings"); } // http://www.w3.org/TR/REC-xml-names/#xmlReserved // The erratum to Namespaces in XML 1.0 that suggests this // next check is controversial. Not everyone accepts it. if (NS_URI_XML.equals(uri)) { throw new IllegalNameException(uri, "Namespace URI", "The " + NS_URI_XML + " must be bound to " + "only the '" + NS_PREFIX_XML + "' prefix."); } // http://www.w3.org/TR/REC-xml-names/#xmlReserved if (NS_URI_XMLNS.equals(uri)) { throw new IllegalNameException(uri, "Namespace URI", "The " + NS_URI_XMLNS + " must be bound to " + "only the '" + NS_PREFIX_XMLNS + "' prefix."); } // no namespace found, we validate the prefix final String pfx = prefix == null ? NS_PREFIX_DEFAULT : prefix; String reason; // http://www.w3.org/TR/REC-xml-names/#xmlReserved // checkNamespacePrefix no longer checks for xml prefix if (NS_PREFIX_XML.equals(pfx)) { // The xml namespace prefix was in the map. attempts to rebind it are illegal throw new IllegalNameException(uri, "Namespace prefix", "The prefix " + NS_PREFIX_XML + " (any case) can only be bound to " + "only the '" + NS_URI_XML + "' uri."); } // http://www.w3.org/TR/REC-xml-names/#xmlReserved // checkNamespacePrefix no longer checks for xmlns prefix if (NS_PREFIX_XMLNS.equals(pfx)) { // The xml namespace prefix was in the map. attempts to rebind it are illegal throw new IllegalNameException(uri, "Namespace prefix", "The prefix " + NS_PREFIX_XMLNS + " (any case) can only be bound to " + "only the '" + NS_URI_XMLNS + "' uri."); } if ((reason = Verifier.checkNamespacePrefix(pfx)) != null) { throw new IllegalNameException(pfx, "Namespace prefix", reason); } // OK, good bet that we have a new Namespace. ns = new Namespace(pfx, uri); final Namespace prev = urimap.putIfAbsent(pfx, ns); if (prev != null) { // someone registered the same namespace as us while we were busy // validating. Use their registered copy. ns = prev; } return ns; }