+ return STRING.getFormatter().parse(namespaceContext, str);
+ }
+
+ /**
+ * Cast well know java types based on {@link Object#toString()} of the provided
+ * object.
+ *
+ */
+ public static <T> Optional<T> cast(Class<T> clss, Object value) {
+ return cast(RuntimeNamespaceContext.getNamespaceContext(), clss, value);
+ }
+
+ /**
+ * Cast well know java types based on {@link Object#toString()} of the provided
+ * object.
+ *
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Optional<T> cast(NamespaceContext namespaceContext, Class<T> clss, Object value) {
+ // if value is null, optional is empty
+ if (value == null)
+ return Optional.empty();
+
+ // if a default has been explicitly requested by passing Object.class
+ // we parse the related String
+ if (clss.isAssignableFrom(Object.class)) {
+ return Optional.of((T) parse(value.toString()));
+ }
+
+ // if value can be cast directly, let's do it
+ if (value.getClass().isAssignableFrom(clss)) {
+ return Optional.of(((T) value));
+ }
+
+ // let's cast between various numbers (possibly losing precision)
+ if (value instanceof Number number) {
+ if (Long.class.isAssignableFrom(clss))
+ return Optional.of((T) (Long) number.longValue());
+ else if (Integer.class.isAssignableFrom(clss))
+ return Optional.of((T) (Integer) number.intValue());
+ else if (Double.class.isAssignableFrom(clss))
+ return Optional.of((T) (Double) number.doubleValue());
+ }
+
+ // let's now try with the string representation
+ String strValue = value instanceof String ? (String) value : value.toString();
+
+ if (String.class.isAssignableFrom(clss)) {
+ return Optional.of((T) strValue);
+ }
+ if (QName.class.isAssignableFrom(clss)) {
+ return Optional.of((T) NamespaceUtils.parsePrefixedName(namespaceContext, strValue));
+ }
+ // Numbers
+ else if (Long.class.isAssignableFrom(clss)) {
+ if (value instanceof Long)
+ return Optional.of((T) value);
+ return Optional.of((T) Long.valueOf(strValue));
+ } else if (Integer.class.isAssignableFrom(clss)) {
+ if (value instanceof Integer)
+ return Optional.of((T) value);
+ return Optional.of((T) Integer.valueOf(strValue));
+ } else if (Double.class.isAssignableFrom(clss)) {
+ if (value instanceof Double)
+ return Optional.of((T) value);
+ return Optional.of((T) Double.valueOf(strValue));
+ }
+
+ // let's now try to parse the string representation to a well-known type
+ Object parsedValue = parse(strValue);
+ if (parsedValue.getClass().isAssignableFrom(clss)) {
+ return Optional.of(((T) value));
+ }
+ throw new ClassCastException("Cannot convert " + value.getClass() + " to " + clss);
+ }
+
+ /** 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<String> 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);
+ }
+