]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.api/src/org/argeo/api/gcr/ContentName.java
5acd53a00a8b506cb4c5e5b9f0a0867500784775
[lgpl/argeo-commons.git] / org.argeo.api / src / org / argeo / api / gcr / ContentName.java
1 package org.argeo.api.gcr;
2
3 import static java.nio.charset.StandardCharsets.UTF_8;
4
5 import java.nio.charset.StandardCharsets;
6 import java.util.Collections;
7 import java.util.Map;
8 import java.util.Objects;
9 import java.util.TreeMap;
10 import java.util.UUID;
11
12 import javax.xml.XMLConstants;
13 import javax.xml.namespace.NamespaceContext;
14 import javax.xml.namespace.QName;
15
16 /**
17 * A {@link QName} which MUST have prefix and whose {@link #toString()} method
18 * returns the prefixed form (prefix:localPart).
19 */
20 public class ContentName extends QName {
21 private static final long serialVersionUID = 5722920985400306100L;
22 public final static UUID NIL_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000");
23 /**
24 * The UUID v3 of http://www.w3.org/2000/xmlns/ within the standard DNS
25 * namespace, to be used as a base for the namespaces.
26 *
27 * @see https://www.w3.org/TR/xml-names/#ns-decl
28 */
29 // uuidgen --md5 --namespace @dns --name http://www.w3.org/2000/xmlns/
30 // NOTE : must be declared before default namespaces
31 public final static UUID XMLNS_UUID = UUID.fromString("4b352aad-ba1c-3139-b9d3-41e5816f6088");
32 // uuidgen --md5 --namespace 4b352aad-ba1c-3139-b9d3-41e5816f6088 --name ""
33 public final static UUID NULL_NS_UUID = UUID.fromString("f07726e3-99c8-3178-b758-a86ed41f300d");
34
35 private final static Map<String, UUID> namespaceUuids = Collections.synchronizedMap(new TreeMap<>());
36 private final static Map<String, UUID> nameUuids = Collections.synchronizedMap(new TreeMap<>());
37
38 static {
39 assert NULL_NS_UUID.equals(nameUUIDv3(XMLNS_UUID, XMLConstants.NULL_NS_URI.getBytes(UTF_8)));
40 }
41
42 // private final UUID uuid;
43
44 public ContentName(String namespaceURI, String localPart, NamespaceContext nsContext) {
45 this(namespaceURI, localPart, nsContext.getPrefix(namespaceURI));
46 }
47
48 protected ContentName(String namespaceURI, String localPart, String prefix) {
49 super(namespaceURI, localPart, prefix);
50 if (prefix == null)
51 throw new IllegalArgumentException("Prefix annot be null");
52 }
53
54 public ContentName(String localPart) {
55 this(XMLConstants.NULL_NS_URI, localPart, XMLConstants.DEFAULT_NS_PREFIX);
56 }
57
58 public ContentName(QName qName, NamespaceContext nsContext) {
59 this(qName.getNamespaceURI(), qName.getLocalPart(), nsContext);
60 }
61
62 public String toQNameString() {
63 return super.toString();
64 }
65
66 public String toPrefixedString() {
67 return toPrefixedString(this);
68 }
69
70 /*
71 * OBJECT METHOS
72 */
73
74 @Override
75 public String toString() {
76 return toPrefixedString();
77 }
78
79 @Override
80 protected Object clone() throws CloneNotSupportedException {
81 return new ContentName(getNamespaceURI(), getLocalPart(), getPrefix());
82 }
83
84 public static String toPrefixedString(QName name) {
85 String prefix = name.getPrefix();
86 assert prefix != null;
87 return "".equals(prefix) ? name.getLocalPart() : prefix + ":" + name.getLocalPart();
88 }
89 // ContentNamespace getNamespace();
90 //
91 // String getName();
92
93 public static UUID namespaceUuid(String namespaceURI) {
94 if (XMLConstants.NULL_NS_URI.equals(namespaceURI))
95 return NULL_NS_UUID;
96 Objects.requireNonNull(namespaceURI, "Namespace URI cannot be null");
97 synchronized (namespaceUuids) {
98 UUID namespaceUuid = namespaceUuids.get(namespaceURI);
99 if (namespaceUuid == null) {
100 namespaceUuid = nameUUIDv3(ContentName.XMLNS_UUID,
101 namespaceURI.toString().getBytes(StandardCharsets.UTF_8));
102 namespaceUuids.put(namespaceURI, namespaceUuid);
103 }
104 return namespaceUuid;
105 }
106 }
107
108 public static UUID nameUuid(String namespaceURI, QName name) {
109 return nameUuid(name.getNamespaceURI(), name.getLocalPart());
110 }
111
112 public static UUID nameUuid(String namespaceURI, String name) {
113 Objects.requireNonNull(namespaceURI, "Namespace cannot be null");
114 Objects.requireNonNull(name, "Name cannot be null");
115 synchronized (nameUuids) {
116 String key = XMLConstants.NULL_NS_URI.equals(namespaceURI) ? name : "{" + namespaceURI + "}" + name;
117 UUID nameUuid = nameUuids.get(key);
118 if (nameUuid == null) {
119 UUID namespaceUuid = namespaceUuid(namespaceURI);
120 nameUuid = nameUUIDv3(namespaceUuid, name.getBytes(StandardCharsets.UTF_8));
121 namespaceUuids.put(key, nameUuid);
122 }
123 return nameUuid;
124 }
125 }
126
127 /*
128 * CANONICAL IMPLEMENTATION based on java.util.UUID.nameUUIDFromBytes(byte[])
129 */
130 static UUID nameUUIDv3(UUID namespace, byte[] name) {
131 byte[] arr = new byte[name.length + 16];
132 ContentName.copyUuidBytes(namespace, arr, 0);
133 System.arraycopy(name, 0, arr, 16, name.length);
134 return UUID.nameUUIDFromBytes(arr);
135 }
136
137 static void copyUuidBytes(UUID uuid, byte[] arr, int offset) {
138 long msb = uuid.getMostSignificantBits();
139 long lsb = uuid.getLeastSignificantBits();
140 assert arr.length >= 16 + offset;
141 for (int i = offset; i < 8 + offset; i++)
142 arr[i] = (byte) ((msb >> ((7 - i) * 8)) & 0xff);
143 for (int i = 8 + offset; i < 16 + offset; i++)
144 arr[i] = (byte) ((lsb >> ((15 - i) * 8)) & 0xff);
145 }
146
147 /*
148 * UTILITIES
149 */
150
151 public static boolean contains(QName[] classes, QName name) {
152 for (QName clss : classes) {
153 if (clss.equals(name))
154 return true;
155 }
156 return false;
157 }
158 }