Work on typing
authorMathieu Baudier <mbaudier@argeo.org>
Sat, 22 Jan 2022 04:49:03 +0000 (05:49 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Sat, 22 Jan 2022 04:49:03 +0000 (05:49 +0100)
org.argeo.api/src/org/argeo/api/gcr/Content.java
org.argeo.api/src/org/argeo/api/gcr/ContentName.java
org.argeo.api/src/org/argeo/api/gcr/ContentNameSupplier.java
org.argeo.api/src/org/argeo/api/gcr/CrAttributeType.java
org.argeo.api/src/org/argeo/api/gcr/spi/AbstractContent.java
org.argeo.cms.jcr/src/org/argeo/cms/jcr/gcr/JcrContent.java
org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContent.java
org.argeo.cms/src/org/argeo/cms/gcr/xml/DomContent.java

index e35f42f27ab7519d1122790b009ac3b65121d6ff..fa4d5071330d87f08b3eca2c984a9ee0e229c3b4 100644 (file)
@@ -1,6 +1,8 @@
 package org.argeo.api.gcr;
 
+import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 import javax.xml.XMLConstants;
 import javax.xml.namespace.QName;
@@ -20,7 +22,7 @@ public interface Content extends Iterable<Content>, Map<QName, Object> {
         * ATTRIBUTES OPERATIONS
         */
 
-       <A> A get(QName key, Class<A> clss) throws IllegalArgumentException;
+       <A> Optional<A> get(QName key, Class<A> clss);
 
        default Object get(String key) {
                if (key.indexOf(':') >= 0)
@@ -40,6 +42,29 @@ public interface Content extends Iterable<Content>, Map<QName, Object> {
                return remove(new QName(XMLConstants.NULL_NS_URI, key, XMLConstants.DEFAULT_NS_PREFIX));
        }
 
+       Class<?> getType(QName key);
+
+       boolean isMultiple(QName key);
+
+       <A> Optional<List<A>> getMultiple(QName key, Class<A> clss);
+
+       default <A> List<A> getMultiple(QName key) {
+               Class<A> type;
+               try {
+                       type = (Class<A>) getType(key);
+               } catch (ClassCastException e) {
+                       throw new IllegalArgumentException("Requested type is not the default type");
+               }
+               Optional<List<A>> res = getMultiple(key, type);
+               if (res == null)
+                       return null;
+               else {
+                       if (res.isEmpty())
+                               throw new IllegalStateException("Metadata " + key + " is not availabel as list of type " + type);
+                       return res.get();
+               }
+       }
+
        /*
         * CONTENT OPERATIONS
         */
index 5acd53a00a8b506cb4c5e5b9f0a0867500784775..b230425288236a9671d28d17484cebc6b1e57918 100644 (file)
@@ -42,17 +42,24 @@ public class ContentName extends QName {
 //     private final UUID uuid;
 
        public ContentName(String namespaceURI, String localPart, NamespaceContext nsContext) {
-               this(namespaceURI, localPart, nsContext.getPrefix(namespaceURI));
+               super(namespaceURI, localPart, checkPrefix(nsContext, namespaceURI));
        }
 
-       protected ContentName(String namespaceURI, String localPart, String prefix) {
-               super(namespaceURI, localPart, prefix);
+       private static String checkPrefix(NamespaceContext nsContext, String namespaceURI) {
+               Objects.requireNonNull(nsContext, "Namespace context cannot be null");
+               Objects.requireNonNull(namespaceURI, "Namespace URI cannot be null");
+               String prefix = nsContext.getNamespaceURI(namespaceURI);
                if (prefix == null)
-                       throw new IllegalArgumentException("Prefix annot be null");
+                       throw new IllegalStateException("No prefix found for " + namespaceURI + " from context " + nsContext);
+               return prefix;
+       }
+
+       ContentName(String namespaceURI, String localPart, String prefix) {
+               super(namespaceURI, localPart, prefix);
        }
 
        public ContentName(String localPart) {
-               this(XMLConstants.NULL_NS_URI, localPart, XMLConstants.DEFAULT_NS_PREFIX);
+               super(XMLConstants.NULL_NS_URI, localPart, XMLConstants.DEFAULT_NS_PREFIX);
        }
 
        public ContentName(QName qName, NamespaceContext nsContext) {
index b6d802049cf85fe72882c2107d441c73965c10b2..0c10201484f64d88b1b577a4955d06fab8ab29c7 100644 (file)
@@ -10,6 +10,10 @@ import javax.xml.namespace.NamespaceContext;
 public interface ContentNameSupplier extends Supplier<ContentName>, NamespaceContext {
        String name();
 
+       String getNamespaceURI();
+
+       String getDefaultPrefix();
+
        @Override
        default ContentName get() {
                return toContentName();
@@ -21,13 +25,13 @@ public interface ContentNameSupplier extends Supplier<ContentName>, NamespaceCon
                return new ContentName(getNamespaceURI(), camlName, this);
        }
 
-       default String getNamespaceURI() {
-               return XMLConstants.NULL_NS_URI;
-       }
-
-       default String getDefaultPrefix() {
-               return XMLConstants.DEFAULT_NS_PREFIX;
-       }
+//     default String getNamespaceURI() {
+//             return XMLConstants.NULL_NS_URI;
+//     }
+//
+//     default String getDefaultPrefix() {
+//             return XMLConstants.DEFAULT_NS_PREFIX;
+//     }
 
 //     static ContentName toContentName(String namespaceURI, String localName, String prefix) {
 //             CompositeString cs = new CompositeString(localName);
index 28f1bc0f6b3af02744326425624336aac8e0f341..1e3445653e624fd0bd6a30166b49dd432513c7e2 100644 (file)
@@ -6,32 +6,34 @@ import java.time.Instant;
 import java.time.format.DateTimeParseException;
 import java.util.UUID;
 
+import javax.xml.XMLConstants;
+
 /**
  * Minimal standard attribute types that MUST be supported. All related classes
  * belong to java.base and can be implicitly derived form a given
  * <code>String<code>.
  */
-public enum CrAttributeType {
+public enum CrAttributeType implements ContentNameSupplier {
        BOOLEAN(Boolean.class, new BooleanFormatter()), //
        INTEGER(Integer.class, new IntegerFormatter()), //
        LONG(Long.class, new LongFormatter()), //
        DOUBLE(Double.class, new DoubleFormatter()), //
        // we do not support short and float, like recent additions to Java
        // (e.g. optional primitives)
-       INSTANT(Instant.class, new InstantFormatter()), //
+       DATE_TIME(Instant.class, new InstantFormatter()), //
        UUID(UUID.class, new UuidFormatter()), //
-       URI(URI.class, new UriFormatter()), //
+       ANY_URI(URI.class, new UriFormatter()), //
        STRING(String.class, new StringFormatter()), //
        ;
 
+       private final Class<?> clss;
+       private final AttributeFormatter<?> formatter;
+
        private <T> CrAttributeType(Class<T> clss, AttributeFormatter<T> formatter) {
                this.clss = clss;
                this.formatter = formatter;
        }
 
-       private final Class<?> clss;
-       private final AttributeFormatter<?> formatter;
-
        public Class<?> getClss() {
                return clss;
        }
@@ -40,6 +42,22 @@ public enum CrAttributeType {
                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;
+       }
+
        public static Object parse(String str) {
                if (str == null)
                        throw new IllegalArgumentException("String cannot be null");
@@ -66,7 +84,7 @@ public enum CrAttributeType {
                        // silent
                }
                try {
-                       return INSTANT.getFormatter().parse(str);
+                       return DATE_TIME.getFormatter().parse(str);
                } catch (IllegalArgumentException e) {
                        // silent
                }
@@ -77,7 +95,7 @@ public enum CrAttributeType {
                        // silent
                }
                try {
-                       java.net.URI uri = (java.net.URI) URI.getFormatter().parse(str);
+                       java.net.URI uri = (java.net.URI) ANY_URI.getFormatter().parse(str);
                        if (uri.getScheme() != null)
                                return uri;
                        String path = uri.getPath();
index 10f36acdf0509ac117871235faa92aea3bba8b9f..2d3bcde93c9f23228e2f00c187f2802f903ee936 100644 (file)
@@ -3,9 +3,11 @@ package org.argeo.api.gcr.spi;
 import java.util.AbstractMap;
 import java.util.AbstractSet;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 import javax.xml.namespace.QName;
@@ -15,37 +17,64 @@ import org.argeo.api.gcr.CrName;
 
 public abstract class AbstractContent extends AbstractMap<QName, Object> implements Content {
 
+       /*
+        * ATTRIBUTES OPERATIONS
+        */
+       protected abstract Iterable<QName> keys();
+
+       protected abstract void removeAttr(QName key);
+
        @Override
        public Set<Entry<QName, Object>> entrySet() {
-//             Set<Entry<String, Object>> result = new HashSet<>();
-//             for (String key : keys()) {
-//                     Entry<String, Object> entry = new Entry<String, Object>() {
-//
-//                             @Override
-//                             public String getKey() {
-//                                     return key;
-//                             }
-//
-//                             @Override
-//                             public Object getValue() {
-//                                     return get(key, Object.class);
-//                             }
-//
-//                             @Override
-//                             public Object setValue(Object value) {
-//                                     throw new UnsupportedOperationException();
-//                             }
-//
-//                     };
-//                     result.add(entry);
-//             }
                Set<Entry<QName, Object>> result = new AttrSet();
                return result;
        }
 
-       protected abstract Iterable<QName> keys();
+       @Override
+       public Class<?> getType(QName key) {
+               return String.class;
+       }
 
-       protected abstract void removeAttr(QName key);
+       @Override
+       public boolean isMultiple(QName key) {
+               return false;
+       }
+
+       @Override
+       public <A> Optional<List<A>> getMultiple(QName key, Class<A> clss) {
+               Object value = get(key);
+               if (value == null)
+                       return null;
+               if (value instanceof List) {
+                       try {
+                               List<A> res = (List<A>) value;
+                               return Optional.of(res);
+                       } catch (ClassCastException e) {
+                               List<A> res = new ArrayList<>();
+                               List<?> lst = (List<?>) value;
+                               try {
+                                       for (Object o : lst) {
+                                               A item = (A) o;
+                                               res.add(item);
+                                       }
+                                       return Optional.of(res);
+                               } catch (ClassCastException e1) {
+                                       return Optional.empty();
+                               }
+                       }
+               } else {// singleton
+                       try {
+                               A res = (A) value;
+                               return Optional.of(Collections.singletonList(res));
+                       } catch (ClassCastException e) {
+                               return Optional.empty();
+                       }
+               }
+       }
+
+       /*
+        * CONTENT OPERATIONS
+        */
 
        @Override
        public String getPath() {
@@ -103,8 +132,9 @@ public abstract class AbstractContent extends AbstractMap<QName, Object> impleme
                                public Entry<QName, Object> next() {
                                        key = keys.next();
                                        // TODO check type
-                                       Object value = get(key, Object.class);
-                                       AbstractMap.SimpleEntry<QName, Object> entry = new SimpleEntry<>(key, value);
+                                       Optional<?> value = get(key, Object.class);
+                                       assert !value.isEmpty();
+                                       AbstractMap.SimpleEntry<QName, Object> entry = new SimpleEntry<>(key, value.get());
                                        return entry;
                                }
 
index ddef0078ef7264ca683fb45e333b602e2c6ea56d..af63ead61721ed3296d33d2e31c203367740e024 100644 (file)
@@ -2,6 +2,7 @@ package org.argeo.cms.jcr.gcr;
 
 import java.util.Calendar;
 import java.util.Iterator;
+import java.util.Optional;
 
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
@@ -37,11 +38,11 @@ public class JcrContent extends AbstractContent {
        }
 
        @Override
-       public <A> A get(QName key, Class<A> clss) {
+       public <A> Optional<A> get(QName key, Class<A> clss) {
                if (isDefaultAttrTypeRequested(clss)) {
-                       return (A) get(jcrNode, key.toString());
+                       return Optional.of((A) get(jcrNode, key.toString()));
                }
-               return (A) Jcr.get(jcrNode, key.toString());
+               return Optional.of((A) Jcr.get(jcrNode, key.toString()));
        }
 
        @Override
index 5efa65959b50a86affbf5e0b3e0b5bcc246c339d..0ba24093ffbe9a838b63a0583ecfc79d9de4f69e 100644 (file)
@@ -13,6 +13,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 import javax.xml.namespace.QName;
@@ -80,7 +81,7 @@ public class FsContent extends AbstractContent implements ProvidedContent {
         */
 
        @Override
-       public <A> A get(QName key, Class<A> clss) {
+       public <A> Optional<A> get(QName key, Class<A> clss) {
                Object value;
                try {
                        // We need to add user: when accessing via Files#getAttribute
@@ -88,19 +89,26 @@ public class FsContent extends AbstractContent implements ProvidedContent {
                } catch (IOException e) {
                        throw new ContentResourceException("Cannot retrieve attribute " + key + " for " + path, e);
                }
+               A res = null;
                if (value instanceof FileTime) {
                        if (clss.isAssignableFrom(FileTime.class))
-                               return (A) value;
+                               res = (A) value;
                        Instant instant = ((FileTime) value).toInstant();
                        if (Object.class.isAssignableFrom(clss)) {// plain object requested
-                               return (A) instant;
+                               res = (A) instant;
                        }
                        // TODO perform trivial file conversion to other formats
                }
                if (value instanceof byte[]) {
-                       return (A) new String((byte[]) value, StandardCharsets.UTF_8);
+                       res = (A) new String((byte[]) value, StandardCharsets.UTF_8);
                }
-               return (A) value;
+               if (res == null)
+                       try {
+                               res = (A) value;
+                       } catch (ClassCastException e) {
+                               return Optional.empty();
+                       }
+               return Optional.of(res);
        }
 
        @Override
index 57e6ba7cecea5974af10b9776a98caa58d6a7458..0b68a772c623067b8a772b36579b662cea281f57 100644 (file)
@@ -2,6 +2,7 @@ package org.argeo.cms.gcr.xml;
 
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Optional;
 import java.util.Set;
 
 import javax.xml.XMLConstants;
@@ -98,15 +99,15 @@ public class DomContent extends AbstractContent implements ProvidedContent {
        }
 
        @Override
-       public <A> A get(QName key, Class<A> clss) {
+       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 (A) value;
+                               return Optional.of((A) value);
                        else
-                               throw new IllegalArgumentException();
+                               return Optional.empty();
                } else
                        return null;
        }
@@ -121,6 +122,8 @@ public class DomContent extends AbstractContent implements ProvidedContent {
                                value.toString());
                return previous;
        }
+       
+       
 
        @Override
        public boolean hasText() {