From 865fc51900459b888938cc0d6943673ee6f20d09 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sat, 22 Jan 2022 05:49:03 +0100 Subject: [PATCH] Work on typing --- .../src/org/argeo/api/gcr/Content.java | 27 +++++- .../src/org/argeo/api/gcr/ContentName.java | 17 ++-- .../argeo/api/gcr/ContentNameSupplier.java | 18 ++-- .../org/argeo/api/gcr/CrAttributeType.java | 34 ++++++-- .../argeo/api/gcr/spi/AbstractContent.java | 82 +++++++++++++------ .../src/org/argeo/cms/jcr/gcr/JcrContent.java | 7 +- .../src/org/argeo/cms/gcr/fs/FsContent.java | 18 ++-- .../src/org/argeo/cms/gcr/xml/DomContent.java | 9 +- 8 files changed, 154 insertions(+), 58 deletions(-) diff --git a/org.argeo.api/src/org/argeo/api/gcr/Content.java b/org.argeo.api/src/org/argeo/api/gcr/Content.java index e35f42f27..fa4d50713 100644 --- a/org.argeo.api/src/org/argeo/api/gcr/Content.java +++ b/org.argeo.api/src/org/argeo/api/gcr/Content.java @@ -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, Map { * ATTRIBUTES OPERATIONS */ - A get(QName key, Class clss) throws IllegalArgumentException; + Optional get(QName key, Class clss); default Object get(String key) { if (key.indexOf(':') >= 0) @@ -40,6 +42,29 @@ public interface Content extends Iterable, Map { return remove(new QName(XMLConstants.NULL_NS_URI, key, XMLConstants.DEFAULT_NS_PREFIX)); } + Class getType(QName key); + + boolean isMultiple(QName key); + + Optional> getMultiple(QName key, Class clss); + + default List getMultiple(QName key) { + Class type; + try { + type = (Class) getType(key); + } catch (ClassCastException e) { + throw new IllegalArgumentException("Requested type is not the default type"); + } + Optional> 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 */ diff --git a/org.argeo.api/src/org/argeo/api/gcr/ContentName.java b/org.argeo.api/src/org/argeo/api/gcr/ContentName.java index 5acd53a00..b23042528 100644 --- a/org.argeo.api/src/org/argeo/api/gcr/ContentName.java +++ b/org.argeo.api/src/org/argeo/api/gcr/ContentName.java @@ -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) { diff --git a/org.argeo.api/src/org/argeo/api/gcr/ContentNameSupplier.java b/org.argeo.api/src/org/argeo/api/gcr/ContentNameSupplier.java index b6d802049..0c1020148 100644 --- a/org.argeo.api/src/org/argeo/api/gcr/ContentNameSupplier.java +++ b/org.argeo.api/src/org/argeo/api/gcr/ContentNameSupplier.java @@ -10,6 +10,10 @@ import javax.xml.namespace.NamespaceContext; public interface ContentNameSupplier extends Supplier, NamespaceContext { String name(); + String getNamespaceURI(); + + String getDefaultPrefix(); + @Override default ContentName get() { return toContentName(); @@ -21,13 +25,13 @@ public interface ContentNameSupplier extends Supplier, 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); diff --git a/org.argeo.api/src/org/argeo/api/gcr/CrAttributeType.java b/org.argeo.api/src/org/argeo/api/gcr/CrAttributeType.java index 28f1bc0f6..1e3445653 100644 --- a/org.argeo.api/src/org/argeo/api/gcr/CrAttributeType.java +++ b/org.argeo.api/src/org/argeo/api/gcr/CrAttributeType.java @@ -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 * String. */ -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 CrAttributeType(Class clss, AttributeFormatter 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(); diff --git a/org.argeo.api/src/org/argeo/api/gcr/spi/AbstractContent.java b/org.argeo.api/src/org/argeo/api/gcr/spi/AbstractContent.java index 10f36acdf..2d3bcde93 100644 --- a/org.argeo.api/src/org/argeo/api/gcr/spi/AbstractContent.java +++ b/org.argeo.api/src/org/argeo/api/gcr/spi/AbstractContent.java @@ -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 implements Content { + /* + * ATTRIBUTES OPERATIONS + */ + protected abstract Iterable keys(); + + protected abstract void removeAttr(QName key); + @Override public Set> entrySet() { -// Set> result = new HashSet<>(); -// for (String key : keys()) { -// Entry entry = new Entry() { -// -// @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> result = new AttrSet(); return result; } - protected abstract Iterable 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 Optional> getMultiple(QName key, Class clss) { + Object value = get(key); + if (value == null) + return null; + if (value instanceof List) { + try { + List res = (List) value; + return Optional.of(res); + } catch (ClassCastException e) { + List 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 impleme public Entry next() { key = keys.next(); // TODO check type - Object value = get(key, Object.class); - AbstractMap.SimpleEntry entry = new SimpleEntry<>(key, value); + Optional value = get(key, Object.class); + assert !value.isEmpty(); + AbstractMap.SimpleEntry entry = new SimpleEntry<>(key, value.get()); return entry; } diff --git a/org.argeo.cms.jcr/src/org/argeo/cms/jcr/gcr/JcrContent.java b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/gcr/JcrContent.java index ddef0078e..af63ead61 100644 --- a/org.argeo.cms.jcr/src/org/argeo/cms/jcr/gcr/JcrContent.java +++ b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/gcr/JcrContent.java @@ -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 get(QName key, Class clss) { + public Optional get(QName key, Class 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 diff --git a/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContent.java b/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContent.java index 5efa65959..0ba24093f 100644 --- a/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContent.java +++ b/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContent.java @@ -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 get(QName key, Class clss) { + public Optional get(QName key, Class 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 diff --git a/org.argeo.cms/src/org/argeo/cms/gcr/xml/DomContent.java b/org.argeo.cms/src/org/argeo/cms/gcr/xml/DomContent.java index 57e6ba7ce..0b68a772c 100644 --- a/org.argeo.cms/src/org/argeo/cms/gcr/xml/DomContent.java +++ b/org.argeo.cms/src/org/argeo/cms/gcr/xml/DomContent.java @@ -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 get(QName key, Class clss) { + public Optional get(QName key, Class 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() { -- 2.30.2