package org.argeo.api.acr;
+import static org.argeo.api.acr.NamespaceUtils.unqualified;
+
+import java.io.Closeable;
+import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
-import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
/**
- * A semi-structured content, with attributes, within a hierarchical structure.
+ * A semi-structured content, with attributes, within a hierarchical structure
+ * whose nodes are named.
*/
-public interface Content extends Iterable<Content>, Map<QName, Object> {
+public interface Content extends QualifiedData<Content> {
+ /** The path separator: '/' */
+ char PATH_SEPARATOR = '/';
- QName getName();
+ /** The base of a repository path. */
+ String ROOT_PATH = Character.toString(PATH_SEPARATOR);
String getPath();
- Content getParent();
+ /** MUST be {@link Content#PATH_SEPARATOR}. */
+ default char getPathSeparator() {
+ return PATH_SEPARATOR;
+ }
/*
- * ATTRIBUTES OPERATIONS
+ * CONTENT OPERATIONS
*/
+ /** Adds a new empty {@link Content} to this {@link Content}. */
+ Content add(QName name, QName... contentClass);
- <A> Optional<A> get(QName key, Class<A> clss);
+ default Content add(QName name, QNamed... contentClass) {
+ return add(name, toQNames(contentClass));
+ }
- default Object get(String key) {
- if (key.indexOf(':') >= 0)
- throw new IllegalArgumentException("Name " + key + " has a prefix");
- return get(new QName(XMLConstants.NULL_NS_URI, key, XMLConstants.DEFAULT_NS_PREFIX));
+ /**
+ * Adds a new {@link Content} to this {@link Content}, setting the provided
+ * attributes. The provided attributes can be used as hints by the
+ * implementation. In particular, setting {@link DName#getcontenttype} will
+ * imply that this content has a file semantic.
+ */
+ default Content add(QName name, Map<QName, Object> attrs, QName... classes) {
+ Content child = add(name, classes);
+ putAll(attrs);
+ return child;
}
- default Object put(String key, Object value) {
- if (key.indexOf(':') >= 0)
- throw new IllegalArgumentException("Name " + key + " has a prefix");
- return put(new QName(XMLConstants.NULL_NS_URI, key, XMLConstants.DEFAULT_NS_PREFIX), value);
+ default Content add(String name, QName... classes) {
+ return add(unqualified(name), classes);
}
- default Object remove(String key) {
- if (key.indexOf(':') >= 0)
- throw new IllegalArgumentException("Name " + key + " has a prefix");
- return remove(new QName(XMLConstants.NULL_NS_URI, key, XMLConstants.DEFAULT_NS_PREFIX));
+ default Content add(String name, Map<QName, Object> attrs, QName... classes) {
+ return add(unqualified(name), attrs, classes);
}
- Class<?> getType(QName key);
+ void remove();
- boolean isMultiple(QName key);
+ /*
+ * TYPING
+ */
+ List<QName> getContentClasses();
- <A> Optional<List<A>> getMultiple(QName key, Class<A> clss);
+ default void addContentClasses(QName... contentClass) {
+ throw new UnsupportedOperationException("Adding content classes to " + getPath() + " is not supported");
+ }
- 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");
+ /** AND */
+ default boolean isContentClass(QName... contentClass) {
+ List<QName> contentClasses = getContentClasses();
+ for (QName cClass : contentClass) {
+ if (!contentClasses.contains(cClass))
+ return false;
}
- 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();
+ return true;
+ }
+
+ /** AND */
+ default boolean isContentClass(QNamed... contentClass) {
+ return isContentClass(toQNames(contentClass));
+ }
+
+ /** OR */
+ default boolean hasContentClass(QName... contentClass) {
+ List<QName> contentClasses = getContentClasses();
+ for (QName cClass : contentClass) {
+ if (contentClasses.contains(cClass))
+ return true;
}
+ return false;
+ }
+
+ /** OR */
+ default boolean hasContentClass(QNamed... contentClass) {
+ return hasContentClass(toQNames(contentClass));
+ }
+
+ static QName[] toQNames(QNamed... names) {
+ QName[] res = new QName[names.length];
+ for (int i = 0; i < names.length; i++)
+ res[i] = names[i].qName();
+ return res;
}
/*
- * CONTENT OPERATIONS
+ * DEFAULT METHODS
*/
- Content add(QName name, QName... classes);
-
- default Content add(String name, QName... classes) {
- if (name.indexOf(':') >= 0)
- throw new IllegalArgumentException("Name " + name + " has a prefix");
- return add(new QName(XMLConstants.NULL_NS_URI, name, XMLConstants.DEFAULT_NS_PREFIX), classes);
+ default <C extends Closeable> C open(Class<C> clss) throws IOException {
+ throw new UnsupportedOperationException("Cannot open content " + this + " as " + clss.getName());
}
- void remove();
+ default <A> CompletableFuture<A> write(Class<A> clss) {
+ throw new UnsupportedOperationException("Cannot write content " + this + " as " + clss.getName());
+ }
/*
- * DEFAULT METHODS
+ * CHILDREN
*/
- default <A> A adapt(Class<A> clss) throws IllegalArgumentException {
- throw new IllegalArgumentException("Cannot adapt content " + this + " to " + clss.getName());
+ default Content anyOrAddChild(QName name, QName... classes) {
+ Content child = anyChild(name);
+ if (child != null)
+ return child;
+ return this.add(name, classes);
}
- default <C extends AutoCloseable> C open(Class<C> clss) throws Exception, IllegalArgumentException {
- throw new IllegalArgumentException("Cannot open content " + this + " as " + clss.getName());
+ default Content anyOrAddChild(String name, QName... classes) {
+ return anyOrAddChild(unqualified(name), classes);
+ }
+
+ default Content soleOrAddChild(QName name, QName... classes) {
+ return soleChild(name).orElseGet(() -> this.add(name, classes));
}
/*
- * CONVENIENCE METHODS
+ * CONTEXT
+ */
+ /**
+ * A content within this repository
+ *
+ * @param path either an absolute path or a path relative to this content
*/
-// default String attr(String key) {
-// return get(key, String.class);
-// }
-//
-// default String attr(Object key) {
-// return key != null ? attr(key.toString()) : attr(null);
-// }
-//
-// default <A> A get(Object key, Class<A> clss) {
-// return key != null ? get(key.toString(), clss) : get(null, clss);
-// }
+ Optional<Content> getContent(String path);
/*
* EXPERIMENTAL UNSUPPORTED