Improve ACR attribute typing.
authorMathieu Baudier <mbaudier@argeo.org>
Sat, 17 Jun 2023 10:50:24 +0000 (12:50 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sat, 17 Jun 2023 10:50:24 +0000 (12:50 +0200)
org.argeo.api.acr/src/org/argeo/api/acr/AttributeFormatter.java
org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java
org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java
org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java
org.argeo.api.acr/src/org/argeo/api/acr/NamespaceUtils.java
org.argeo.api.acr/src/org/argeo/api/acr/RuntimeNamespaceContext.java
org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java
org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java
org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java

index c7023f1a37a4a58b2582fc62e4f9f7d509fb7336..9f338965f829d2781649cb7fe192f9e2afa11a28 100644 (file)
@@ -1,5 +1,7 @@
 package org.argeo.api.acr;
 
+import javax.xml.namespace.NamespaceContext;
+
 /**
  * An attribute type MUST consistently parse a string to an object so that
  * <code>parse(obj.toString()).equals(obj)</code> is verified.
@@ -9,7 +11,15 @@ package org.argeo.api.acr;
  */
 public interface AttributeFormatter<T> {
        /** Parses a String to a Java object. */
-       T parse(String str) throws IllegalArgumentException;
+       default T parse(String str) throws IllegalArgumentException {
+               return parse(RuntimeNamespaceContext.getNamespaceContext(), str);
+       }
+
+       /**
+        * Parses a String to a Java object, possibly using the namespace context to
+        * resolve QName or CURIE.
+        */
+       T parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException;
 
        /** Default implementation returns {@link Object#toString()} on the argument. */
        default String format(T obj) {
index 113f98da0d6350d07279d261e2a99b443b25541a..92cd795ce6191f869c7437c142bc2a7c307ddd17 100644 (file)
@@ -83,7 +83,7 @@ public class ContentName extends QName {
         */
 
        @Override
-       public String toString() {
+       public final String toString() {
                return toQNameString();
        }
 
index b8ecd98da97ae29b548fffef0cb44f01ad668d32..7a6e67981d71899643bd5ad53bc115b530877a1f 100644 (file)
@@ -3,7 +3,6 @@ package org.argeo.api.acr;
 import java.util.Locale;
 import java.util.concurrent.CompletionStage;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 import javax.security.auth.Subject;
index 3e12fb1c87067fd188bfa34cac4c44794c9ec5d2..3e0dddee4c55ed6cf6f6f81e836d8941dca3fd52 100644 (file)
@@ -9,10 +9,10 @@ import java.time.format.DateTimeParseException;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.List;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.UUID;
 
+import javax.xml.namespace.NamespaceContext;
 import javax.xml.namespace.QName;
 
 /**
@@ -29,6 +29,7 @@ public enum CrAttributeType {
        // (e.g. optional primitives)
        DATE_TIME(Instant.class, W3C_XML_SCHEMA_NS_URI, "dateTime", new InstantFormatter()), //
        UUID(UUID.class, ArgeoNamespace.CR_NAMESPACE_URI, "uuid", new UuidFormatter()), //
+       QNAME(QName.class, W3C_XML_SCHEMA_NS_URI, "QName", new QNameFormatter()), //
        ANY_URI(URI.class, W3C_XML_SCHEMA_NS_URI, "anyUri", new UriFormatter()), //
        STRING(String.class, W3C_XML_SCHEMA_NS_URI, "string", new StringFormatter()), //
        ;
@@ -45,7 +46,7 @@ public enum CrAttributeType {
                qName = new ContentName(namespaceUri, localName, RuntimeNamespaceContext.getNamespaceContext());
        }
 
-       public QName getqName() {
+       public QName getQName() {
                return qName;
        }
 
@@ -75,43 +76,71 @@ public enum CrAttributeType {
 
        /** Default parsing procedure from a String to an object. */
        public static Object parse(String str) {
+               return parse(RuntimeNamespaceContext.getNamespaceContext(), str);
+       }
+
+       /** Default parsing procedure from a String to an object. */
+       public static Object parse(NamespaceContext namespaceContext, 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);
+                               return BOOLEAN.getFormatter().parse(namespaceContext, str);
                } catch (IllegalArgumentException e) {
                        // silent
                }
                try {
-                       return INTEGER.getFormatter().parse(str);
+                       return INTEGER.getFormatter().parse(namespaceContext, str);
                } catch (IllegalArgumentException e) {
                        // silent
                }
                try {
-                       return LONG.getFormatter().parse(str);
+                       return LONG.getFormatter().parse(namespaceContext, str);
                } catch (IllegalArgumentException e) {
                        // silent
                }
                try {
-                       return DOUBLE.getFormatter().parse(str);
+                       return DOUBLE.getFormatter().parse(namespaceContext, str);
                } catch (IllegalArgumentException e) {
                        // silent
                }
                try {
-                       return DATE_TIME.getFormatter().parse(str);
+                       return DATE_TIME.getFormatter().parse(namespaceContext, str);
                } catch (IllegalArgumentException e) {
                        // silent
                }
                try {
                        if (str.length() == 36)
-                               return UUID.getFormatter().parse(str);
+                               return UUID.getFormatter().parse(namespaceContext, str);
+               } catch (IllegalArgumentException e) {
+                       // silent
+               }
+
+               // CURIE
+               if (str.startsWith("[") && str.endsWith("]")) {
+                       try {
+                               if (str.indexOf(":") >= 0) {
+                                       QName qName = (QName) QNAME.getFormatter().parse(namespaceContext, str);
+                                       return (java.net.URI) ANY_URI.getFormatter().parse(qName.getNamespaceURI() + qName.getLocalPart());
+                               }
+                       } catch (IllegalArgumentException e) {
+                               // silent
+                       }
+               }
+
+               try {
+                       if (str.indexOf(":") >= 0) {
+                               QName qName = (QName) QNAME.getFormatter().parse(namespaceContext, str);
+                               // note: this QName may not be valid
+                               // note: CURIE should be explicitly defined with surrounding brackets
+                               return qName;
+                       }
                } catch (IllegalArgumentException e) {
                        // silent
                }
                try {
-                       java.net.URI uri = (java.net.URI) ANY_URI.getFormatter().parse(str);
+                       java.net.URI uri = (java.net.URI) ANY_URI.getFormatter().parse(namespaceContext, str);
                        if (uri.getScheme() != null)
                                return uri;
                        String path = uri.getPath();
@@ -129,7 +158,7 @@ public enum CrAttributeType {
                // see https://www.oreilly.com/library/view/xml-schema/0596002521/re91.html
 
                // default
-               return STRING.getFormatter().parse(str);
+               return STRING.getFormatter().parse(namespaceContext, str);
        }
 
        /**
@@ -137,34 +166,72 @@ public enum CrAttributeType {
         * object.
         * 
         */
-       @SuppressWarnings("unchecked")
        public static <T> Optional<T> cast(Class<T> clss, Object value) {
-               // TODO Or should we?
-               Objects.requireNonNull(value, "Cannot cast a null 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) value.toString());
+                       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(value.toString()));
+                       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(value.toString()));
+                       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(value.toString()));
+                       return Optional.of((T) Double.valueOf(strValue));
                }
-               // Numbers
-//             else if (Number.class.isAssignableFrom(clss)) {
-//                     if (value instanceof Number)
-//                             return Optional.of((T) value);
-//                     return Optional.of((T) Number.valueOf(value.toString()));
-//             }
-               return Optional.empty();
+
+               // 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. */
@@ -202,7 +269,7 @@ public enum CrAttributeType {
                 *            contract than {@link Boolean#parseBoolean(String)}.
                 */
                @Override
-               public Boolean parse(String str) throws IllegalArgumentException {
+               public Boolean parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
                        if ("true".equals(str))
                                return Boolean.TRUE;
                        if ("false".equals(str))
@@ -213,14 +280,14 @@ public enum CrAttributeType {
 
        static class IntegerFormatter implements AttributeFormatter<Integer> {
                @Override
-               public Integer parse(String str) throws NumberFormatException {
+               public Integer parse(NamespaceContext namespaceContext, String str) throws NumberFormatException {
                        return Integer.parseInt(str);
                }
        }
 
        static class LongFormatter implements AttributeFormatter<Long> {
                @Override
-               public Long parse(String str) throws NumberFormatException {
+               public Long parse(NamespaceContext namespaceContext, String str) throws NumberFormatException {
                        return Long.parseLong(str);
                }
        }
@@ -228,7 +295,7 @@ public enum CrAttributeType {
        static class DoubleFormatter implements AttributeFormatter<Double> {
 
                @Override
-               public Double parse(String str) throws NumberFormatException {
+               public Double parse(NamespaceContext namespaceContext, String str) throws NumberFormatException {
                        return Double.parseDouble(str);
                }
        }
@@ -236,7 +303,7 @@ public enum CrAttributeType {
        static class InstantFormatter implements AttributeFormatter<Instant> {
 
                @Override
-               public Instant parse(String str) throws IllegalArgumentException {
+               public Instant parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
                        try {
                                return Instant.parse(str);
                        } catch (DateTimeParseException e) {
@@ -248,7 +315,7 @@ public enum CrAttributeType {
        static class UuidFormatter implements AttributeFormatter<UUID> {
 
                @Override
-               public UUID parse(String str) throws IllegalArgumentException {
+               public UUID parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
                        return java.util.UUID.fromString(str);
                }
        }
@@ -256,7 +323,7 @@ public enum CrAttributeType {
        static class UriFormatter implements AttributeFormatter<URI> {
 
                @Override
-               public URI parse(String str) throws IllegalArgumentException {
+               public URI parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
                        try {
                                return new URI(str);
                        } catch (URISyntaxException e) {
@@ -266,10 +333,19 @@ public enum CrAttributeType {
 
        }
 
+       static class QNameFormatter implements AttributeFormatter<QName> {
+
+               @Override
+               public QName parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException {
+                       return NamespaceUtils.parsePrefixedName(namespaceContext, str);
+               }
+
+       }
+
        static class StringFormatter implements AttributeFormatter<String> {
 
                @Override
-               public String parse(String str) {
+               public String parse(NamespaceContext namespaceContext, String str) {
                        return str;
                }
 
index df582868b349798da88d124264d5f846c432feec..e845560b032cc261fb4485e0fdc19b32d3a8456d 100644 (file)
@@ -11,12 +11,41 @@ import javax.xml.XMLConstants;
 import javax.xml.namespace.NamespaceContext;
 import javax.xml.namespace.QName;
 
+/** Static utilities around namespaces and prefixes. */
 public class NamespaceUtils {
+       /**
+        * A {@link Comparator} ordering by namespace (full URI) and then local part.
+        */
+       public final static Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() {
+
+               @Override
+               public int compare(QName qn1, QName qn2) {
+                       if (Objects.equals(qn1.getNamespaceURI(), qn2.getNamespaceURI())) {// same namespace
+                               return qn1.getLocalPart().compareTo(qn2.getLocalPart());
+                       } else {
+                               return qn1.getNamespaceURI().compareTo(qn2.getNamespaceURI());
+                       }
+               }
 
+       };
+
+       /**
+        * Return a {@link ContentName} from a prefixed name, using the default runtime
+        * {@link NamespaceContext}.
+        * 
+        * @see RuntimeNamespaceContext#getNamespaceContext()
+        */
        public static ContentName parsePrefixedName(String nameWithPrefix) {
                return parsePrefixedName(RuntimeNamespaceContext.getNamespaceContext(), nameWithPrefix);
        }
 
+       /**
+        * Return a {@link ContentName} from a prefixed name, using the provided
+        * {@link NamespaceContext}. Since {@link QName#QName(String, String)} does not
+        * validate, it can conceptually parse a CURIE.
+        * 
+        * @see https://en.wikipedia.org/wiki/CURIE
+        */
        public static ContentName parsePrefixedName(NamespaceContext nameSpaceContext, String nameWithPrefix) {
                Objects.requireNonNull(nameWithPrefix, "Name cannot be null");
                if (nameWithPrefix.charAt(0) == '{') {
@@ -31,14 +60,24 @@ public class NamespaceUtils {
                String localName = nameWithPrefix.substring(index + 1);
                String namespaceURI = nameSpaceContext.getNamespaceURI(prefix);
                if (XMLConstants.NULL_NS_URI.equals(namespaceURI))
-                       throw new IllegalStateException("Prefix " + prefix + " is unbound.");
+                       throw new IllegalArgumentException("Prefix " + prefix + " is unbound.");
                return new ContentName(namespaceURI, localName, prefix);
        }
 
+       /**
+        * The prefixed name of this {@link QName}, using the default runtime
+        * {@link NamespaceContext}.
+        * 
+        * @see RuntimeNamespaceContext#getNamespaceContext()
+        */
        public static String toPrefixedName(QName name) {
                return toPrefixedName(RuntimeNamespaceContext.getNamespaceContext(), name);
        }
 
+       /**
+        * The prefixed name of this {@link QName}, using the provided
+        * {@link NamespaceContext}.
+        */
        public static String toPrefixedName(NamespaceContext nameSpaceContext, QName name) {
                if (XMLConstants.NULL_NS_URI.equals(name.getNamespaceURI()))
                        return name.getLocalPart();
@@ -48,36 +87,56 @@ public class NamespaceUtils {
                return prefix + ":" + name.getLocalPart();
        }
 
-       public final static Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() {
-
-               @Override
-               public int compare(QName qn1, QName qn2) {
-                       if (Objects.equals(qn1.getNamespaceURI(), qn2.getNamespaceURI())) {// same namespace
-                               return qn1.getLocalPart().compareTo(qn2.getLocalPart());
-                       } else {
-                               return qn1.getNamespaceURI().compareTo(qn2.getNamespaceURI());
-                       }
-               }
-
-       };
-
+       /**
+        * Whether thei {@link QName} has a namespace, that is its namespace is not
+        * {@link XMLConstants#NULL_NS_URI}.
+        */
        public static boolean hasNamespace(QName qName) {
                return !qName.getNamespaceURI().equals(XMLConstants.NULL_NS_URI);
        }
 
-       public static void checkNoPrefix(String unqualified) {
+       /** Throws an exception if the provided string has a prefix. */
+       public static void checkNoPrefix(String unqualified) throws IllegalArgumentException {
                if (unqualified.indexOf(':') >= 0)
                        throw new IllegalArgumentException("Name " + unqualified + " has a prefix");
        }
 
+       /**
+        * Create an unqualified {@link QName}, checking that the argument does not
+        * contain a prefix.
+        */
        public static QName unqualified(String name) {
                checkNoPrefix(name);
                return new ContentName(XMLConstants.NULL_NS_URI, name, XMLConstants.DEFAULT_NS_PREFIX);
 
        }
 
+       /**
+        * The common (fully qualified) representation of this name, as defined in
+        * {@link QName#toString()}. This should be used when a fully qualified
+        * representation is required, as subclasses of {@link QName} may override the
+        * {@link QName#toString()} method.
+        * 
+        * @see ContentName#toString()
+        */
+       public static String toFullyQualified(QName name) {
+               if (name.getNamespaceURI().equals(XMLConstants.NULL_NS_URI)) {
+                       return name.getLocalPart();
+               } else {
+                       return "{" + name.getNamespaceURI() + "}" + name.getLocalPart();
+               }
+
+       }
+
        /*
-        * DEFAULT NAMESPACE CONTEXT OPERATIONS as specified in NamespaceContext
+        * STANDARD NAMESPACE CONTEXT OPERATIONS as specified in NamespaceContext
+        */
+       /**
+        * The standard prefix for well known namespaces as defined in
+        * {@link NamespaceContext}, or null if the namespace is not well-known. Helper
+        * method simplifying the implementation of a {@link NamespaceContext}.
+        * 
+        * @see NamespaceContext#getPrefix(String)
         */
        public static String getStandardPrefix(String namespaceURI) {
                if (namespaceURI == null)
@@ -89,6 +148,13 @@ public class NamespaceUtils {
                return null;
        }
 
+       /**
+        * The standard prefixes for well known namespaces as defined in
+        * {@link NamespaceContext}, or null if the namespace is not well-known. Helper
+        * method simplifying the implementation of a {@link NamespaceContext}.
+        * 
+        * @see NamespaceContext#getPrefixes(String)
+        */
        public static Iterator<String> getStandardPrefixes(String namespaceURI) {
                String prefix = getStandardPrefix(namespaceURI);
                if (prefix == null)
@@ -96,6 +162,13 @@ public class NamespaceUtils {
                return Collections.singleton(prefix).iterator();
        }
 
+       /**
+        * The standard URI for well known prefixes as defined in
+        * {@link NamespaceContext}, or null if the prefix is not well-known. Helper
+        * method simplifying the implementation of a {@link NamespaceContext}.
+        * 
+        * @see NamespaceContext#getNamespaceURI(String)
+        */
        public static String getStandardNamespaceURI(String prefix) {
                if (prefix == null)
                        throw new IllegalArgumentException("Prefix cannot be null");
@@ -106,6 +179,13 @@ public class NamespaceUtils {
                return null;
        }
 
+       /**
+        * The namespace URI for a given prefix, based on the provided mapping and the
+        * standard prefixes/URIs.
+        * 
+        * @return the namespace URI for this prefix, or
+        *         {@link XMLConstants#NULL_NS_URI} if unknown.
+        */
        public static String getNamespaceURI(Function<String, String> mapping, String prefix) {
                String namespaceURI = NamespaceUtils.getStandardNamespaceURI(prefix);
                if (namespaceURI != null)
@@ -118,6 +198,13 @@ public class NamespaceUtils {
                return XMLConstants.NULL_NS_URI;
        }
 
+       /**
+        * The prefix for a given namespace URI, based on the provided mapping and the
+        * standard prefixes/URIs.
+        * 
+        * @return the prefix for this namespace URI. What is returned or throws as
+        *         exception if the prefix is not found depdnds on the mapping function.
+        */
        public static String getPrefix(Function<String, String> mapping, String namespaceURI) {
                String prefix = NamespaceUtils.getStandardPrefix(namespaceURI);
                if (prefix != null)
@@ -127,6 +214,13 @@ public class NamespaceUtils {
                return mapping.apply(namespaceURI);
        }
 
+       /**
+        * The prefixes for a given namespace URI, based on the provided mapping and the
+        * standard prefixes/URIs.
+        * 
+        * @return the prefixes for this namespace URI. What is returned or throws as
+        *         exception if the prefix is not found depdnds on the mapping function.
+        */
        public static Iterator<String> getPrefixes(Function<String, Set<String>> mapping, String namespaceURI) {
                Iterator<String> standard = NamespaceUtils.getStandardPrefixes(namespaceURI);
                if (standard != null)
index 1c55156ee352b389c387c56f2ea30103dae2371c..fe50fd0c3b58058c55a16b0c3feef24486f64510 100644 (file)
@@ -10,8 +10,10 @@ import javax.xml.XMLConstants;
 import javax.xml.namespace.NamespaceContext;
 
 /**
- * Programmatically defined {@link NamespaceContext}, code contributing
- * namespaces MUST register here with a single default prefix.
+ * Programmatically defined {@link NamespaceContext}, which is valid at runtime
+ * (when the software is running). Code contributing namespaces MUST register
+ * here with a single default prefix, nad MUST make sure that stored data
+ * contains the fully qualified namespace URI.
  */
 public class RuntimeNamespaceContext implements NamespaceContext {
        public final static String XSD_DEFAULT_PREFIX = "xs";
@@ -20,35 +22,38 @@ public class RuntimeNamespaceContext implements NamespaceContext {
        private NavigableMap<String, String> prefixes = new TreeMap<>();
        private NavigableMap<String, String> namespaces = new TreeMap<>();
 
+       /*
+        * NAMESPACE CONTEXT IMPLEMENTATION
+        */
+
        @Override
-       public String getPrefix(String namespaceURI) {
+       public String getPrefix(String namespaceURI) throws IllegalArgumentException {
                return NamespaceUtils.getPrefix((ns) -> {
                        String prefix = namespaces.get(ns);
                        if (prefix == null)
-                               throw new IllegalStateException("Namespace " + ns + " is not registered.");
+                               throw new IllegalArgumentException("Namespace " + ns + " is not registered.");
                        return prefix;
                }, namespaceURI);
        }
 
        @Override
-       public String getNamespaceURI(String prefix) {
+       public String getNamespaceURI(String prefix) throws IllegalArgumentException {
                return NamespaceUtils.getNamespaceURI((p) -> {
                        String ns = prefixes.get(p);
                        if (ns == null)
-                               throw new IllegalStateException("Prefix " + p + " is not registered.");
+                               throw new IllegalArgumentException("Prefix " + p + " is not registered.");
                        return ns;
                }, prefix);
        }
 
        @Override
-       public Iterator<String> getPrefixes(String namespaceURI) {
+       public Iterator<String> getPrefixes(String namespaceURI) throws IllegalArgumentException {
                return Collections.singleton(getPrefix(namespaceURI)).iterator();
        }
 
        /*
         * STATIC
         */
-
        private final static RuntimeNamespaceContext INSTANCE = new RuntimeNamespaceContext();
 
        static {
@@ -66,30 +71,34 @@ public class RuntimeNamespaceContext implements NamespaceContext {
                register(ArgeoNamespace.ROLE_NAMESPACE_URI, ArgeoNamespace.ROLE_DEFAULT_PREFIX);
        }
 
+       /** The runtime namespace context instance. */
        public static NamespaceContext getNamespaceContext() {
                return INSTANCE;
        }
 
+       /** The registered prefixes. */
        public static Map<String, String> getPrefixes() {
                return Collections.unmodifiableNavigableMap(INSTANCE.prefixes);
        }
 
-       public synchronized static void register(String namespaceURI, String prefix) {
+       /** Registers a namespace URI / default prefix mapping. */
+       public synchronized static void register(String namespaceURI, String defaultPrefix) {
                NavigableMap<String, String> prefixes = INSTANCE.prefixes;
                NavigableMap<String, String> namespaces = INSTANCE.namespaces;
-               if (prefixes.containsKey(prefix)) {
-                       String ns = prefixes.get(prefix);
+               if (prefixes.containsKey(defaultPrefix)) {
+                       String ns = prefixes.get(defaultPrefix);
                        if (ns.equals(namespaceURI))
                                return; // ignore silently
-                       throw new IllegalStateException("Prefix " + prefix + " is already registered with namespace URI " + ns);
+                       throw new IllegalStateException(
+                                       "Prefix " + defaultPrefix + " is already registered with namespace URI " + ns);
                }
                if (namespaces.containsKey(namespaceURI)) {
                        String p = namespaces.get(namespaceURI);
-                       if (p.equals(prefix))
+                       if (p.equals(defaultPrefix))
                                return; // ignore silently
                        throw new IllegalStateException("Namespace " + namespaceURI + " is already registered with prefix " + p);
                }
-               prefixes.put(prefix, namespaceURI);
-               namespaces.put(namespaceURI, prefix);
+               prefixes.put(defaultPrefix, namespaceURI);
+               namespaces.put(namespaceURI, defaultPrefix);
        }
 }
index c6e07e638b9b24b7bd0ebee667446b5de25b05ed..54d7410155961e6f1522f88fff4a1eddf7509e97 100644 (file)
@@ -13,6 +13,7 @@ import java.util.Set;
 import javax.xml.namespace.QName;
 
 import org.argeo.api.acr.Content;
+import org.argeo.api.acr.CrAttributeType;
 import org.argeo.api.acr.CrName;
 import org.argeo.api.acr.NamespaceUtils;
 import org.argeo.api.acr.spi.ProvidedContent;
@@ -60,22 +61,18 @@ public abstract class AbstractContent extends AbstractMap<QName, Object> impleme
                if (value == null)
                        return new ArrayList<>();
                if (value instanceof List) {
-                       if (isDefaultAttrTypeRequested(clss))
+                       if (clss.isAssignableFrom(Object.class))
                                return (List<A>) value;
                        List<A> res = new ArrayList<>();
                        List<?> lst = (List<?>) value;
                        for (Object o : lst) {
-                               A item = clss.isAssignableFrom(String.class) ? (A) o.toString() : (A) o;
+                               A item = CrAttributeType.cast(clss, o).get();
                                res.add(item);
                        }
                        return res;
                } else {// singleton
-//                     try {
-                       A res = (A) value;
+                       A res = CrAttributeType.cast(clss, value).get();
                        return Collections.singletonList(res);
-//                     } catch (ClassCastException e) {
-//                             return Optional.empty();
-//                     }
                }
        }
 
@@ -151,10 +148,10 @@ public abstract class AbstractContent extends AbstractMap<QName, Object> impleme
        /*
         * UTILITIES
         */
-       protected boolean isDefaultAttrTypeRequested(Class<?> clss) {
-               // check whether clss is Object.class
-               return clss.isAssignableFrom(Object.class);
-       }
+//     protected boolean isDefaultAttrTypeRequested(Class<?> clss) {
+//             // check whether clss is Object.class
+//             return clss.isAssignableFrom(Object.class);
+//     }
 
 //     @Override
 //     public String toString() {
@@ -188,7 +185,7 @@ public abstract class AbstractContent extends AbstractMap<QName, Object> impleme
 
        @Override
        public <A> Optional<A> get(QName key, Class<A> clss) {
-               return null;
+               return Optional.empty();
        }
 
        protected void removeAttr(QName key) {
index 8fca831b024293084a07ded2ff93d9ad8a9085e4..4b94abf1eb5143b1ee5074d5f1b1126ae1de001b 100644 (file)
@@ -41,14 +41,13 @@ import org.argeo.api.acr.NamespaceUtils;
 import org.argeo.api.acr.spi.ContentProvider;
 import org.argeo.api.acr.spi.ProvidedContent;
 import org.argeo.api.acr.spi.ProvidedSession;
-import org.argeo.api.cms.CmsLog;
 import org.argeo.cms.acr.AbstractContent;
 import org.argeo.cms.acr.ContentUtils;
 import org.argeo.cms.util.FsUtils;
 
 /** Content persisted as a filesystem {@link Path}. */
 public class FsContent extends AbstractContent implements ProvidedContent {
-       private CmsLog log = CmsLog.getLog(FsContent.class);
+//     private CmsLog log = CmsLog.getLog(FsContent.class);
 
        final static String USER_ = "user:";
 
@@ -160,11 +159,15 @@ public class FsContent extends AbstractContent implements ProvidedContent {
                        String[] arr = str.split("\n");
 
                        if (arr.length == 1) {
-                               if (clss.isAssignableFrom(String.class)) {
-                                       res = (A) arr[0];
-                               } else {
-                                       res = (A) CrAttributeType.parse(arr[0]);
-                               }
+//                             if (clss.isAssignableFrom(String.class)) {
+//                                     res = (A) arr[0];
+//                             } else {
+//                                     res = (A) CrAttributeType.parse(arr[0]);
+//                             }
+//                             if (isDefaultAttrTypeRequested(clss))
+//                                     return Optional.of((A) CrAttributeType.parse(str));
+                               return CrAttributeType.cast(clss, str);
+
                        } else {
                                List<Object> lst = new ArrayList<>();
                                for (String s : arr) {
@@ -174,14 +177,15 @@ public class FsContent extends AbstractContent implements ProvidedContent {
                        }
                }
                if (res == null) {
-                       if (isDefaultAttrTypeRequested(clss))
-                               return Optional.of((A) CrAttributeType.parse(value.toString()));
-                       if (clss.isAssignableFrom(value.getClass()))
-                               return Optional.of((A) value);
-                       if (clss.isAssignableFrom(String.class))
-                               return Optional.of((A) value.toString());
-                       log.warn("Cannot interpret " + key + " in " + this);
-                       return Optional.empty();
+//                     if (isDefaultAttrTypeRequested(clss))
+//                             return Optional.of((A) CrAttributeType.parse(value.toString()));
+                       return CrAttributeType.cast(clss, value);
+//                     if (clss.isAssignableFrom(value.getClass()))
+//                             return Optional.of((A) value);
+//                     if (clss.isAssignableFrom(String.class))
+//                             return Optional.of((A) value.toString());
+//                     log.warn("Cannot interpret " + key + " in " + this);
+//                     return Optional.empty();
 //                     try {
 //                             res = (A) value;
 //                     } catch (ClassCastException e) {
@@ -365,12 +369,13 @@ public class FsContent extends AbstractContent implements ProvidedContent {
 
        @Override
        public List<QName> getContentClasses() {
-               List<QName> res = new ArrayList<>();
-               List<String> value = getMultiple(DName.resourcetype.qName(), String.class);
-               for (String s : value) {
-                       QName name = NamespaceUtils.parsePrefixedName(provider, s);
-                       res.add(name);
-               }
+//             List<QName> res = new ArrayList<>();
+//             List<String> value = getMultiple(DName.resourcetype.qName(), String.class);
+//             for (String s : value) {
+//                     QName name = NamespaceUtils.parsePrefixedName(provider, s);
+//                     res.add(name);
+//             }
+               List<QName> res = getMultiple(DName.resourcetype.qName(), QName.class);
                if (Files.isDirectory(path))
                        res.add(DName.collection.qName());
                return res;
index 1520bc2cf7cf48b49b40caf88d92dbcbb6e4becd..22c15702907485cc7f773843640dd4295c3ee2dd 100644 (file)
@@ -29,6 +29,7 @@ import javax.xml.transform.stream.StreamResult;
 
 import org.argeo.api.acr.Content;
 import org.argeo.api.acr.ContentName;
+import org.argeo.api.acr.CrAttributeType;
 import org.argeo.api.acr.CrName;
 import org.argeo.api.acr.spi.ProvidedContent;
 import org.argeo.api.acr.spi.ProvidedSession;
@@ -137,17 +138,16 @@ public class DomContent extends AbstractContent implements ProvidedContent {
                return result;
        }
 
-       @SuppressWarnings("unchecked")
+//     @SuppressWarnings("unchecked")
        @Override
        public <A> Optional<A> get(QName key, Class<A> clss) {
                String namespaceUriOrNull = XMLConstants.NULL_NS_URI.equals(key.getNamespaceURI()) ? null
                                : key.getNamespaceURI();
                if (element.hasAttributeNS(namespaceUriOrNull, key.getLocalPart())) {
                        String value = element.getAttributeNS(namespaceUriOrNull, key.getLocalPart());
-                       if (clss.isAssignableFrom(String.class))
-                               return Optional.of((A) value);
-                       else
-                               return Optional.empty();
+//                     if (isDefaultAttrTypeRequested(clss))
+//                             return Optional.of((A) CrAttributeType.parse(value));
+                       return CrAttributeType.cast(clss, value);
                } else
                        return Optional.empty();
        }