package org.argeo.api.acr; import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; import java.net.URI; import java.net.URISyntaxException; import java.time.Instant; import java.time.format.DateTimeParseException; import java.util.Arrays; import java.util.Base64; import java.util.List; import java.util.UUID; import javax.xml.namespace.QName; /** * Minimal standard attribute types that MUST be supported. All related classes * belong to java.base and can be implicitly derived form a given * String. */ public enum CrAttributeType { BOOLEAN(Boolean.class, W3C_XML_SCHEMA_NS_URI, "boolean", new BooleanFormatter()), // INTEGER(Integer.class, W3C_XML_SCHEMA_NS_URI, "integer", new IntegerFormatter()), // LONG(Long.class, W3C_XML_SCHEMA_NS_URI, "long", new LongFormatter()), // DOUBLE(Double.class, W3C_XML_SCHEMA_NS_URI, "double", new DoubleFormatter()), // // we do not support short and float, like recent additions to Java // (e.g. optional primitives) DATE_TIME(Instant.class, W3C_XML_SCHEMA_NS_URI, "dateTime", new InstantFormatter()), // UUID(UUID.class, CrName.CR_NAMESPACE_URI, "uuid", new UuidFormatter()), // ANY_URI(URI.class, W3C_XML_SCHEMA_NS_URI, "anyUri", new UriFormatter()), // STRING(String.class, W3C_XML_SCHEMA_NS_URI, "string", new StringFormatter()), // ; private final Class clss; private final AttributeFormatter formatter; private final ContentName qName; private CrAttributeType(Class clss, String namespaceUri, String localName, AttributeFormatter formatter) { this.clss = clss; this.formatter = formatter; qName = new ContentName(namespaceUri, localName, RuntimeNamespaceContext.getNamespaceContext()); } public QName getqName() { return qName; } public Class getClss() { return clss; } public AttributeFormatter getFormatter() { return formatter; } // @Override // public String getDefaultPrefix() { // if (equals(UUID)) // return CrName.CR_DEFAULT_PREFIX; // else // return "xs"; // } // // @Override // public String getNamespaceURI() { // if (equals(UUID)) // return CrName.CR_NAMESPACE_URI; // else // return XMLConstants.W3C_XML_SCHEMA_NS_URI; // } /** Default parsing procedure from a String to an object. */ public static Object parse(String str) { if (str == null) throw new IllegalArgumentException("String cannot be null"); // order IS important try { if (str.length() == 4 || str.length() == 5) return BOOLEAN.getFormatter().parse(str); } catch (IllegalArgumentException e) { // silent } try { return INTEGER.getFormatter().parse(str); } catch (IllegalArgumentException e) { // silent } try { return LONG.getFormatter().parse(str); } catch (IllegalArgumentException e) { // silent } try { return DOUBLE.getFormatter().parse(str); } catch (IllegalArgumentException e) { // silent } try { return DATE_TIME.getFormatter().parse(str); } catch (IllegalArgumentException e) { // silent } try { if (str.length() == 36) return UUID.getFormatter().parse(str); } catch (IllegalArgumentException e) { // silent } try { java.net.URI uri = (java.net.URI) ANY_URI.getFormatter().parse(str); if (uri.getScheme() != null) return uri; String path = uri.getPath(); if (path.indexOf('/') >= 0) return uri; // if it is not clearly a path, we will consider it as a string // because their is no way to distinguish between 'any_string' // and 'any_file_name'. // Note that providing ./any_file_name would result in an equivalent URI } catch (IllegalArgumentException e) { // silent } // TODO support QName as a type? It would require a NamespaceContext // see https://www.oreilly.com/library/view/xml-schema/0596002521/re91.html // default return STRING.getFormatter().parse(str); } /** Utility to convert a data: URI to bytes. */ public static byte[] bytesFromDataURI(URI uri) { if (!"data".equals(uri.getScheme())) throw new IllegalArgumentException("URI must have 'data' as a scheme"); String schemeSpecificPart = uri.getSchemeSpecificPart(); int commaIndex = schemeSpecificPart.indexOf(','); String prefix = schemeSpecificPart.substring(0, commaIndex); List info = Arrays.asList(prefix.split(";")); if (!info.contains("base64")) throw new IllegalArgumentException("URI must specify base64"); String base64Str = schemeSpecificPart.substring(commaIndex + 1); return Base64.getDecoder().decode(base64Str); } /** Utility to convert bytes to a data: URI. */ public static URI bytesToDataURI(byte[] arr) { String base64Str = Base64.getEncoder().encodeToString(arr); try { final String PREFIX = "data:application/octet-stream;base64,"; return new URI(PREFIX + base64Str); } catch (URISyntaxException e) { throw new IllegalStateException("Cannot serialize bytes a Base64 data URI", e); } } static class BooleanFormatter implements AttributeFormatter { /** * @param str must be exactly equals to either 'true' or 'false' (different * contract than {@link Boolean#parseBoolean(String)}. */ @Override public Boolean parse(String str) throws IllegalArgumentException { if ("true".equals(str)) return Boolean.TRUE; if ("false".equals(str)) return Boolean.FALSE; throw new IllegalArgumentException("Argument is neither 'true' or 'false' : " + str); } } static class IntegerFormatter implements AttributeFormatter { @Override public Integer parse(String str) throws NumberFormatException { return Integer.parseInt(str); } } static class LongFormatter implements AttributeFormatter { @Override public Long parse(String str) throws NumberFormatException { return Long.parseLong(str); } } static class DoubleFormatter implements AttributeFormatter { @Override public Double parse(String str) throws NumberFormatException { return Double.parseDouble(str); } } static class InstantFormatter implements AttributeFormatter { @Override public Instant parse(String str) throws IllegalArgumentException { try { return Instant.parse(str); } catch (DateTimeParseException e) { throw new IllegalArgumentException("Cannot parse '" + str + "' as an instant", e); } } } static class UuidFormatter implements AttributeFormatter { @Override public UUID parse(String str) throws IllegalArgumentException { return java.util.UUID.fromString(str); } } static class UriFormatter implements AttributeFormatter { @Override public URI parse(String str) throws IllegalArgumentException { try { return new URI(str); } catch (URISyntaxException e) { throw new IllegalArgumentException("Cannot parse " + str + " as an URI.", e); } } } static class StringFormatter implements AttributeFormatter { @Override public String parse(String str) { return str; } @Override public String format(String obj) { return obj; } } }