]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java
Introduce UUID identified and openForEdit/freeze cycle
[lgpl/argeo-commons.git] / org.argeo.api.acr / src / org / argeo / api / acr / ContentName.java
1 package org.argeo.api.acr;
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) {
45 super(namespaceURI, localPart, checkPrefix(RuntimeNamespaceContext.getNamespaceContext(), namespaceURI));
46 }
47
48 public ContentName(String namespaceURI, String localPart, NamespaceContext nsContext) {
49 super(namespaceURI, localPart, checkPrefix(nsContext, namespaceURI));
50 }
51
52 private static String checkPrefix(NamespaceContext nsContext, String namespaceURI) {
53 Objects.requireNonNull(nsContext, "Namespace context cannot be null");
54 Objects.requireNonNull(namespaceURI, "Namespace URI cannot be null");
55 String prefix = nsContext.getPrefix(namespaceURI);
56 if (prefix == null)
57 throw new IllegalStateException("No prefix found for " + namespaceURI + " from context " + nsContext);
58 return prefix;
59 }
60
61 ContentName(String namespaceURI, String localPart, String prefix) {
62 super(namespaceURI, localPart, prefix);
63 }
64
65 public ContentName(String localPart) {
66 super(XMLConstants.NULL_NS_URI, localPart, XMLConstants.DEFAULT_NS_PREFIX);
67 }
68
69 public ContentName(QName qName, NamespaceContext nsContext) {
70 this(qName.getNamespaceURI(), qName.getLocalPart(), nsContext);
71 }
72
73 public String toQNameString() {
74 return super.toString();
75 }
76
77 public String toPrefixedString() {
78 return toPrefixedString(this);
79 }
80
81 /*
82 * OBJECT METHOS
83 */
84
85 @Override
86 public final String toString() {
87 return toQNameString();
88 }
89
90 @Override
91 protected Object clone() throws CloneNotSupportedException {
92 return new ContentName(getNamespaceURI(), getLocalPart(), getPrefix());
93 }
94
95 public static String toPrefixedString(QName name) {
96 String prefix = name.getPrefix();
97 assert prefix != null;
98 return "".equals(prefix) ? name.getLocalPart() : prefix + ":" + name.getLocalPart();
99 }
100 // ContentNamespace getNamespace();
101 //
102 // String getName();
103
104 public static UUID namespaceUuid(String namespaceURI) {
105 if (XMLConstants.NULL_NS_URI.equals(namespaceURI))
106 return NULL_NS_UUID;
107 Objects.requireNonNull(namespaceURI, "Namespace URI cannot be null");
108 synchronized (namespaceUuids) {
109 UUID namespaceUuid = namespaceUuids.get(namespaceURI);
110 if (namespaceUuid == null) {
111 namespaceUuid = nameUUIDv3(ContentName.XMLNS_UUID,
112 namespaceURI.toString().getBytes(StandardCharsets.UTF_8));
113 namespaceUuids.put(namespaceURI, namespaceUuid);
114 }
115 return namespaceUuid;
116 }
117 }
118
119 public static UUID nameUuid(String namespaceURI, QName name) {
120 return nameUuid(name.getNamespaceURI(), name.getLocalPart());
121 }
122
123 public static UUID nameUuid(String namespaceURI, String name) {
124 Objects.requireNonNull(namespaceURI, "Namespace cannot be null");
125 Objects.requireNonNull(name, "Name cannot be null");
126 synchronized (nameUuids) {
127 String key = XMLConstants.NULL_NS_URI.equals(namespaceURI) ? name : "{" + namespaceURI + "}" + name;
128 UUID nameUuid = nameUuids.get(key);
129 if (nameUuid == null) {
130 UUID namespaceUuid = namespaceUuid(namespaceURI);
131 nameUuid = nameUUIDv3(namespaceUuid, name.getBytes(StandardCharsets.UTF_8));
132 namespaceUuids.put(key, nameUuid);
133 }
134 return nameUuid;
135 }
136 }
137
138 /*
139 * CANONICAL IMPLEMENTATION based on java.util.UUID.nameUUIDFromBytes(byte[])
140 */
141 static UUID nameUUIDv3(UUID namespace, byte[] name) {
142 byte[] arr = new byte[name.length + 16];
143 ContentName.copyUuidBytes(namespace, arr, 0);
144 System.arraycopy(name, 0, arr, 16, name.length);
145 return UUID.nameUUIDFromBytes(arr);
146 }
147
148 static void copyUuidBytes(UUID uuid, byte[] arr, int offset) {
149 long msb = uuid.getMostSignificantBits();
150 long lsb = uuid.getLeastSignificantBits();
151 assert arr.length >= 16 + offset;
152 for (int i = offset; i < 8 + offset; i++)
153 arr[i] = (byte) ((msb >> ((7 - i) * 8)) & 0xff);
154 for (int i = 8 + offset; i < 16 + offset; i++)
155 arr[i] = (byte) ((lsb >> ((15 - i) * 8)) & 0xff);
156 }
157
158 /*
159 * UTILITIES
160 */
161
162 public static boolean contains(QName[] classes, QName name) {
163 for (QName clss : classes) {
164 if (clss.equals(name))
165 return true;
166 }
167 return false;
168 }
169 }