X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.api%2Fsrc%2Forg%2Fargeo%2Fapi%2Fgcr%2FContentNamespace.java;fp=org.argeo.api%2Fsrc%2Forg%2Fargeo%2Fapi%2Fgcr%2FContentNamespace.java;h=a59557180e09a2b704334bdd609e3ac2b84c3900;hb=51efb630db7314b67654a03d1bd983b45aa2f1ed;hp=0000000000000000000000000000000000000000;hpb=62f4b67f846f67cdfd96340684e9fcf73bbd14ef;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.api/src/org/argeo/api/gcr/ContentNamespace.java b/org.argeo.api/src/org/argeo/api/gcr/ContentNamespace.java new file mode 100644 index 000000000..a59557180 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/gcr/ContentNamespace.java @@ -0,0 +1,106 @@ +package org.argeo.api.gcr; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.UUID; + +/** + * An XML-style namespace with a related UUID v3. + * + * @see https://www.w3.org/TR/xml-names/ + */ +public class ContentNamespace { + private final UUID uuid; + private final URI uri; + + public ContentNamespace(String uri) { + try { + this.uri = new URI(uri); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Cannot interpret " + uri + " as URI", e); + } + this.uuid = namespaceUuid(this.uri); + } + + public ContentNamespace(URI uri) { + this.uri = uri; + this.uuid = namespaceUuid(uri); + } + + ContentNamespace(URI uri, UUID uuid) { + this.uri = uri; + assert uuid.equals(namespaceUuid(uri)); + this.uuid = uuid; + } + + /** Empty namespace */ + private ContentNamespace() { + try { + this.uri = new URI(""); + } catch (URISyntaxException e) { + throw new IllegalStateException("Cannot create empty URI"); + } + this.uuid = NIL_UUID; + } + + public UUID getUuid() { + return uuid; + } + + public URI getUri() { + return uri; + } + + public UUID nameUuid(String name) { + return nameUuid(getUuid(), name); + } + + + public final static UUID NIL_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000"); + public final static ContentNamespace EMPTY_NS = new ContentNamespace(); + + /** + * 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"); + public final static ContentNamespace CR_NS = new ContentNamespace("http://argeo.org/ns/cr"); + + + public static UUID namespaceUuid(URI namespaceUri) { + Objects.requireNonNull(namespaceUri, "Namespace URI cannot be null"); + return nameUUIDv3(XMLNS_UUID, namespaceUri.toString().getBytes(StandardCharsets.UTF_8)); + } + + public static UUID nameUuid(UUID namespace, String name) { + Objects.requireNonNull(namespace, "Namespace cannot be null"); + Objects.requireNonNull(namespace, "Name cannot be null"); + return nameUUIDv3(namespace, name.getBytes(StandardCharsets.UTF_8)); + } + + /* + * CANONICAL IMPLEMENTATION based on java.util.UUID.nameUUIDFromBytes(byte[]) + */ + private static UUID nameUUIDv3(UUID namespace, byte[] name) { + byte[] arr = new byte[name.length + 16]; + copyUuidBytes(namespace, arr, 0); + System.arraycopy(name, 0, arr, 16, name.length); + return UUID.nameUUIDFromBytes(arr); + } + + private 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); + } +}