X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.api%2Fsrc%2Forg%2Fargeo%2Fapi%2Fgcr%2FContentName.java;h=5acd53a00a8b506cb4c5e5b9f0a0867500784775;hb=e5a22cdc7d0f4918f2740c626e1ab6384bd5ee44;hp=ddc80c5b621aae1f7766b3d63d2062a1490a671f;hpb=51efb630db7314b67654a03d1bd983b45aa2f1ed;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.api/src/org/argeo/api/gcr/ContentName.java b/org.argeo.api/src/org/argeo/api/gcr/ContentName.java index ddc80c5b6..5acd53a00 100644 --- a/org.argeo.api/src/org/argeo/api/gcr/ContentName.java +++ b/org.argeo.api/src/org/argeo/api/gcr/ContentName.java @@ -1,17 +1,156 @@ package org.argeo.api.gcr; +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; import java.util.UUID; -public interface ContentName { - UUID getUuid(); +import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; + +/** + * A {@link QName} which MUST have prefix and whose {@link #toString()} method + * returns the prefixed form (prefix:localPart). + */ +public class ContentName extends QName { + private static final long serialVersionUID = 5722920985400306100L; + public final static UUID NIL_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000"); + /** + * The UUID v3 of http://www.w3.org/2000/xmlns/ within the standard DNS + * namespace, to be used as a base for the namespaces. + * + * @see https://www.w3.org/TR/xml-names/#ns-decl + */ + // uuidgen --md5 --namespace @dns --name http://www.w3.org/2000/xmlns/ + // NOTE : must be declared before default namespaces + public final static UUID XMLNS_UUID = UUID.fromString("4b352aad-ba1c-3139-b9d3-41e5816f6088"); + // uuidgen --md5 --namespace 4b352aad-ba1c-3139-b9d3-41e5816f6088 --name "" + public final static UUID NULL_NS_UUID = UUID.fromString("f07726e3-99c8-3178-b758-a86ed41f300d"); + + private final static Map namespaceUuids = Collections.synchronizedMap(new TreeMap<>()); + private final static Map nameUuids = Collections.synchronizedMap(new TreeMap<>()); + + static { + assert NULL_NS_UUID.equals(nameUUIDv3(XMLNS_UUID, XMLConstants.NULL_NS_URI.getBytes(UTF_8))); + } + +// private final UUID uuid; + + public ContentName(String namespaceURI, String localPart, NamespaceContext nsContext) { + this(namespaceURI, localPart, nsContext.getPrefix(namespaceURI)); + } + + protected ContentName(String namespaceURI, String localPart, String prefix) { + super(namespaceURI, localPart, prefix); + if (prefix == null) + throw new IllegalArgumentException("Prefix annot be null"); + } + + public ContentName(String localPart) { + this(XMLConstants.NULL_NS_URI, localPart, XMLConstants.DEFAULT_NS_PREFIX); + } + + public ContentName(QName qName, NamespaceContext nsContext) { + this(qName.getNamespaceURI(), qName.getLocalPart(), nsContext); + } + + public String toQNameString() { + return super.toString(); + } + + public String toPrefixedString() { + return toPrefixedString(this); + } + + /* + * OBJECT METHOS + */ + + @Override + public String toString() { + return toPrefixedString(); + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return new ContentName(getNamespaceURI(), getLocalPart(), getPrefix()); + } + + public static String toPrefixedString(QName name) { + String prefix = name.getPrefix(); + assert prefix != null; + return "".equals(prefix) ? name.getLocalPart() : prefix + ":" + name.getLocalPart(); + } +// ContentNamespace getNamespace(); +// +// String getName(); + + public static UUID namespaceUuid(String namespaceURI) { + if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) + return NULL_NS_UUID; + Objects.requireNonNull(namespaceURI, "Namespace URI cannot be null"); + synchronized (namespaceUuids) { + UUID namespaceUuid = namespaceUuids.get(namespaceURI); + if (namespaceUuid == null) { + namespaceUuid = nameUUIDv3(ContentName.XMLNS_UUID, + namespaceURI.toString().getBytes(StandardCharsets.UTF_8)); + namespaceUuids.put(namespaceURI, namespaceUuid); + } + return namespaceUuid; + } + } + + public static UUID nameUuid(String namespaceURI, QName name) { + return nameUuid(name.getNamespaceURI(), name.getLocalPart()); + } - ContentNamespace getNamespace(); + public static UUID nameUuid(String namespaceURI, String name) { + Objects.requireNonNull(namespaceURI, "Namespace cannot be null"); + Objects.requireNonNull(name, "Name cannot be null"); + synchronized (nameUuids) { + String key = XMLConstants.NULL_NS_URI.equals(namespaceURI) ? name : "{" + namespaceURI + "}" + name; + UUID nameUuid = nameUuids.get(key); + if (nameUuid == null) { + UUID namespaceUuid = namespaceUuid(namespaceURI); + nameUuid = nameUUIDv3(namespaceUuid, name.getBytes(StandardCharsets.UTF_8)); + namespaceUuids.put(key, nameUuid); + } + return nameUuid; + } + } + + /* + * CANONICAL IMPLEMENTATION based on java.util.UUID.nameUUIDFromBytes(byte[]) + */ + static UUID nameUUIDv3(UUID namespace, byte[] name) { + byte[] arr = new byte[name.length + 16]; + ContentName.copyUuidBytes(namespace, arr, 0); + System.arraycopy(name, 0, arr, 16, name.length); + return UUID.nameUUIDFromBytes(arr); + } + + static void copyUuidBytes(UUID uuid, byte[] arr, int offset) { + long msb = uuid.getMostSignificantBits(); + long lsb = uuid.getLeastSignificantBits(); + assert arr.length >= 16 + offset; + for (int i = offset; i < 8 + offset; i++) + arr[i] = (byte) ((msb >> ((7 - i) * 8)) & 0xff); + for (int i = 8 + offset; i < 16 + offset; i++) + arr[i] = (byte) ((lsb >> ((15 - i) * 8)) & 0xff); + } - String getName(); + /* + * UTILITIES + */ - static boolean contains(ContentName[] classes, ContentName name) { - for (ContentName clss : classes) { - if (clss.getUuid().equals(name.getUuid())) + public static boolean contains(QName[] classes, QName name) { + for (QName clss : classes) { + if (clss.equals(name)) return true; } return false;