From 117eaabc86f6c09eff9a4b971ac137d51f45e953 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sat, 17 Jun 2023 12:50:24 +0200 Subject: [PATCH] Improve ACR attribute typing. --- .../org/argeo/api/acr/AttributeFormatter.java | 12 +- .../src/org/argeo/api/acr/ContentName.java | 2 +- .../src/org/argeo/api/acr/ContentSession.java | 1 - .../org/argeo/api/acr/CrAttributeType.java | 140 ++++++++++++++---- .../src/org/argeo/api/acr/NamespaceUtils.java | 126 ++++++++++++++-- .../api/acr/RuntimeNamespaceContext.java | 39 +++-- .../org/argeo/cms/acr/AbstractContent.java | 21 ++- .../src/org/argeo/cms/acr/fs/FsContent.java | 47 +++--- .../src/org/argeo/cms/acr/xml/DomContent.java | 10 +- 9 files changed, 294 insertions(+), 104 deletions(-) diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/AttributeFormatter.java b/org.argeo.api.acr/src/org/argeo/api/acr/AttributeFormatter.java index c7023f1a3..9f338965f 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/AttributeFormatter.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/AttributeFormatter.java @@ -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 * parse(obj.toString()).equals(obj) is verified. @@ -9,7 +11,15 @@ package org.argeo.api.acr; */ public interface AttributeFormatter { /** 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) { diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java b/org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java index 113f98da0..92cd795ce 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/ContentName.java @@ -83,7 +83,7 @@ public class ContentName extends QName { */ @Override - public String toString() { + public final String toString() { return toQNameString(); } diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java b/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java index b8ecd98da..7a6e67981 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/ContentSession.java @@ -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; diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java b/org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java index 3e12fb1c8..3e0dddee4 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java @@ -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 Optional cast(Class 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 Optional cast(NamespaceContext namespaceContext, Class 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 { @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 { @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 { @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 { @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 { @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 { @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 { + + @Override + public QName parse(NamespaceContext namespaceContext, String str) throws IllegalArgumentException { + return NamespaceUtils.parsePrefixedName(namespaceContext, str); + } + + } + static class StringFormatter implements AttributeFormatter { @Override - public String parse(String str) { + public String parse(NamespaceContext namespaceContext, String str) { return str; } diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/NamespaceUtils.java b/org.argeo.api.acr/src/org/argeo/api/acr/NamespaceUtils.java index df582868b..e845560b0 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/NamespaceUtils.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/NamespaceUtils.java @@ -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_COMPARATOR = new Comparator() { + + @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_COMPARATOR = new Comparator() { - - @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 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 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 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 getPrefixes(Function> mapping, String namespaceURI) { Iterator standard = NamespaceUtils.getStandardPrefixes(namespaceURI); if (standard != null) diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/RuntimeNamespaceContext.java b/org.argeo.api.acr/src/org/argeo/api/acr/RuntimeNamespaceContext.java index 1c55156ee..fe50fd0c3 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/RuntimeNamespaceContext.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/RuntimeNamespaceContext.java @@ -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 prefixes = new TreeMap<>(); private NavigableMap 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 getPrefixes(String namespaceURI) { + public Iterator 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 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 prefixes = INSTANCE.prefixes; NavigableMap 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); } } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java b/org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java index c6e07e638..54d741015 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java @@ -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 impleme if (value == null) return new ArrayList<>(); if (value instanceof List) { - if (isDefaultAttrTypeRequested(clss)) + if (clss.isAssignableFrom(Object.class)) return (List) value; List 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 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 impleme @Override public Optional get(QName key, Class clss) { - return null; + return Optional.empty(); } protected void removeAttr(QName key) { diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java index 8fca831b0..4b94abf1e 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java @@ -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 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 getContentClasses() { - List res = new ArrayList<>(); - List value = getMultiple(DName.resourcetype.qName(), String.class); - for (String s : value) { - QName name = NamespaceUtils.parsePrefixedName(provider, s); - res.add(name); - } +// List res = new ArrayList<>(); +// List value = getMultiple(DName.resourcetype.qName(), String.class); +// for (String s : value) { +// QName name = NamespaceUtils.parsePrefixedName(provider, s); +// res.add(name); +// } + List res = getMultiple(DName.resourcetype.qName(), QName.class); if (Files.isDirectory(path)) res.add(DName.collection.qName()); return res; diff --git a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java index 1520bc2cf..22c157029 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java @@ -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 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 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(); } -- 2.30.2