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