]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java
Clarify system roles
[lgpl/argeo-commons.git] / org.argeo.api.acr / src / org / argeo / api / acr / CrAttributeType.java
1 package org.argeo.api.acr;
2
3 import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
4
5 import java.net.URI;
6 import java.net.URISyntaxException;
7 import java.time.Instant;
8 import java.time.format.DateTimeParseException;
9 import java.util.Arrays;
10 import java.util.Base64;
11 import java.util.List;
12 import java.util.UUID;
13
14 import javax.xml.namespace.QName;
15
16 /**
17 * Minimal standard attribute types that MUST be supported. All related classes
18 * belong to java.base and can be implicitly derived form a given
19 * <code>String<code>.
20 */
21 public enum CrAttributeType {
22 BOOLEAN(Boolean.class, W3C_XML_SCHEMA_NS_URI, "boolean", new BooleanFormatter()), //
23 INTEGER(Integer.class, W3C_XML_SCHEMA_NS_URI, "integer", new IntegerFormatter()), //
24 LONG(Long.class, W3C_XML_SCHEMA_NS_URI, "long", new LongFormatter()), //
25 DOUBLE(Double.class, W3C_XML_SCHEMA_NS_URI, "double", new DoubleFormatter()), //
26 // we do not support short and float, like recent additions to Java
27 // (e.g. optional primitives)
28 DATE_TIME(Instant.class, W3C_XML_SCHEMA_NS_URI, "dateTime", new InstantFormatter()), //
29 UUID(UUID.class, CrName.CR_NAMESPACE_URI, "uuid", new UuidFormatter()), //
30 ANY_URI(URI.class, W3C_XML_SCHEMA_NS_URI, "anyUri", new UriFormatter()), //
31 STRING(String.class, W3C_XML_SCHEMA_NS_URI, "string", new StringFormatter()), //
32 ;
33
34 private final Class<?> clss;
35 private final AttributeFormatter<?> formatter;
36
37 private final ContentName qName;
38
39 private <T> CrAttributeType(Class<T> clss, String namespaceUri, String localName, AttributeFormatter<T> formatter) {
40 this.clss = clss;
41 this.formatter = formatter;
42
43 qName = new ContentName(namespaceUri, localName, RuntimeNamespaceContext.getNamespaceContext());
44 }
45
46 public QName getqName() {
47 return qName;
48 }
49
50 public Class<?> getClss() {
51 return clss;
52 }
53
54 public AttributeFormatter<?> getFormatter() {
55 return formatter;
56 }
57
58 // @Override
59 // public String getDefaultPrefix() {
60 // if (equals(UUID))
61 // return CrName.CR_DEFAULT_PREFIX;
62 // else
63 // return "xs";
64 // }
65 //
66 // @Override
67 // public String getNamespaceURI() {
68 // if (equals(UUID))
69 // return CrName.CR_NAMESPACE_URI;
70 // else
71 // return XMLConstants.W3C_XML_SCHEMA_NS_URI;
72 // }
73
74 /** Default parsing procedure from a String to an object. */
75 public static Object parse(String str) {
76 if (str == null)
77 throw new IllegalArgumentException("String cannot be null");
78 // order IS important
79 try {
80 if (str.length() == 4 || str.length() == 5)
81 return BOOLEAN.getFormatter().parse(str);
82 } catch (IllegalArgumentException e) {
83 // silent
84 }
85 try {
86 return INTEGER.getFormatter().parse(str);
87 } catch (IllegalArgumentException e) {
88 // silent
89 }
90 try {
91 return LONG.getFormatter().parse(str);
92 } catch (IllegalArgumentException e) {
93 // silent
94 }
95 try {
96 return DOUBLE.getFormatter().parse(str);
97 } catch (IllegalArgumentException e) {
98 // silent
99 }
100 try {
101 return DATE_TIME.getFormatter().parse(str);
102 } catch (IllegalArgumentException e) {
103 // silent
104 }
105 try {
106 if (str.length() == 36)
107 return UUID.getFormatter().parse(str);
108 } catch (IllegalArgumentException e) {
109 // silent
110 }
111 try {
112 java.net.URI uri = (java.net.URI) ANY_URI.getFormatter().parse(str);
113 if (uri.getScheme() != null)
114 return uri;
115 String path = uri.getPath();
116 if (path.indexOf('/') >= 0)
117 return uri;
118 // if it is not clearly a path, we will consider it as a string
119 // because their is no way to distinguish between 'any_string'
120 // and 'any_file_name'.
121 // Note that providing ./any_file_name would result in an equivalent URI
122 } catch (IllegalArgumentException e) {
123 // silent
124 }
125
126 // TODO support QName as a type? It would require a NamespaceContext
127 // see https://www.oreilly.com/library/view/xml-schema/0596002521/re91.html
128
129 // default
130 return STRING.getFormatter().parse(str);
131 }
132
133 /** Utility to convert a data: URI to bytes. */
134 public static byte[] bytesFromDataURI(URI uri) {
135 if (!"data".equals(uri.getScheme()))
136 throw new IllegalArgumentException("URI must have 'data' as a scheme");
137 String schemeSpecificPart = uri.getSchemeSpecificPart();
138 int commaIndex = schemeSpecificPart.indexOf(',');
139 String prefix = schemeSpecificPart.substring(0, commaIndex);
140 List<String> info = Arrays.asList(prefix.split(";"));
141 if (!info.contains("base64"))
142 throw new IllegalArgumentException("URI must specify base64");
143
144 String base64Str = schemeSpecificPart.substring(commaIndex + 1);
145 return Base64.getDecoder().decode(base64Str);
146
147 }
148
149 /** Utility to convert bytes to a data: URI. */
150 public static URI bytesToDataURI(byte[] arr) {
151 String base64Str = Base64.getEncoder().encodeToString(arr);
152 try {
153 final String PREFIX = "data:application/octet-stream;base64,";
154 return new URI(PREFIX + base64Str);
155 } catch (URISyntaxException e) {
156 throw new IllegalStateException("Cannot serialize bytes a Base64 data URI", e);
157 }
158
159 }
160
161 static class BooleanFormatter implements AttributeFormatter<Boolean> {
162
163 /**
164 * @param str must be exactly equals to either 'true' or 'false' (different
165 * contract than {@link Boolean#parseBoolean(String)}.
166 */
167 @Override
168 public Boolean parse(String str) throws IllegalArgumentException {
169 if ("true".equals(str))
170 return Boolean.TRUE;
171 if ("false".equals(str))
172 return Boolean.FALSE;
173 throw new IllegalArgumentException("Argument is neither 'true' or 'false' : " + str);
174 }
175 }
176
177 static class IntegerFormatter implements AttributeFormatter<Integer> {
178 @Override
179 public Integer parse(String str) throws NumberFormatException {
180 return Integer.parseInt(str);
181 }
182 }
183
184 static class LongFormatter implements AttributeFormatter<Long> {
185 @Override
186 public Long parse(String str) throws NumberFormatException {
187 return Long.parseLong(str);
188 }
189 }
190
191 static class DoubleFormatter implements AttributeFormatter<Double> {
192
193 @Override
194 public Double parse(String str) throws NumberFormatException {
195 return Double.parseDouble(str);
196 }
197 }
198
199 static class InstantFormatter implements AttributeFormatter<Instant> {
200
201 @Override
202 public Instant parse(String str) throws IllegalArgumentException {
203 try {
204 return Instant.parse(str);
205 } catch (DateTimeParseException e) {
206 throw new IllegalArgumentException("Cannot parse '" + str + "' as an instant", e);
207 }
208 }
209 }
210
211 static class UuidFormatter implements AttributeFormatter<UUID> {
212
213 @Override
214 public UUID parse(String str) throws IllegalArgumentException {
215 return java.util.UUID.fromString(str);
216 }
217 }
218
219 static class UriFormatter implements AttributeFormatter<URI> {
220
221 @Override
222 public URI parse(String str) throws IllegalArgumentException {
223 try {
224 return new URI(str);
225 } catch (URISyntaxException e) {
226 throw new IllegalArgumentException("Cannot parse " + str + " as an URI.", e);
227 }
228 }
229
230 }
231
232 static class StringFormatter implements AttributeFormatter<String> {
233
234 @Override
235 public String parse(String str) {
236 return str;
237 }
238
239 @Override
240 public String format(String obj) {
241 return obj;
242 }
243
244 }
245
246 }