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