Start splitting Content in more abstract interfaces
[lgpl/argeo-commons.git] / org.argeo.api.acr / src / org / argeo / api / acr / Content.java
index f52ab31b8f25223e109b4781707502f924cd5ebd..d6af2fe3de301e044f3f95e468b853d31a89c6a0 100644 (file)
@@ -13,9 +13,12 @@ import java.util.concurrent.CompletableFuture;
 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 base of a repository path. */
+       String ROOT_PATH = "/";
 
        QName getName();
 
@@ -82,31 +85,38 @@ public interface Content extends Iterable<Content>, Map<QName, Object> {
                }
                List<A> res = getMultiple(key, type);
                return res;
-//             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
         */
-//     default CompletionStage<Content> edit(Consumer<Content> work) {
-//             return CompletableFuture.supplyAsync(() -> {
-//                     work.accept(this);
-//                     return this;
-//             }).minimalCompletionStage();
-//     }
+       /** Adds a new empty {@link Content} to this {@link Content}. */
+       Content add(QName name, QName... contentClass);
 
-       Content add(QName name, QName... classes);
+       default Content add(QName name, QNamed... contentClass) {
+               return add(name, toQNames(contentClass));
+       }
+
+       /**
+        * 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 Content add(String name, QName... classes) {
                return add(unqualified(name), classes);
        }
 
+       default Content add(String name, Map<QName, Object> attrs, QName... classes) {
+               return add(unqualified(name), attrs, classes);
+       }
+
        void remove();
 
        /*
@@ -130,10 +140,7 @@ public interface Content extends Iterable<Content>, Map<QName, Object> {
 
        /** AND */
        default boolean isContentClass(QNamed... contentClass) {
-               List<QName> lst = new ArrayList<>();
-               for (QNamed qNamed : contentClass)
-                       lst.add(qNamed.qName());
-               return isContentClass(lst.toArray(new QName[lst.size()]));
+               return isContentClass(toQNames(contentClass));
        }
 
        /** OR */
@@ -148,10 +155,14 @@ public interface Content extends Iterable<Content>, Map<QName, Object> {
 
        /** OR */
        default boolean hasContentClass(QNamed... contentClass) {
-               List<QName> lst = new ArrayList<>();
-               for (QNamed qNamed : contentClass)
-                       lst.add(qNamed.qName());
-               return hasContentClass(lst.toArray(new QName[lst.size()]));
+               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;
        }
 
        /*
@@ -222,6 +233,14 @@ public interface Content extends Iterable<Content>, Map<QName, Object> {
                return res;
        }
 
+       default List<Content> children(QNamed name) {
+               return children(name.qName());
+       }
+
+       default Optional<Content> soleChild(QNamed name) {
+               return soleChild(name.qName());
+       }
+
        default Optional<Content> soleChild(QName name) {
                List<Content> res = children(name);
                if (res.isEmpty())
@@ -231,6 +250,10 @@ public interface Content extends Iterable<Content>, Map<QName, Object> {
                return Optional.of(res.get(0));
        }
 
+       default Content soleOrAddChild(QName name, QName... classes) {
+               return soleChild(name).orElseGet(() -> this.add(name, classes));
+       }
+
        default Content child(QName name) {
                return soleChild(name).orElseThrow();
        }
@@ -242,29 +265,51 @@ public interface Content extends Iterable<Content>, Map<QName, Object> {
        /*
         * ATTR AS STRING
         */
+       /**
+        * Convenience method returning an attribute as a {@link String}.
+        * 
+        * @param key the attribute name
+        * @return the attribute value as a {@link String} or <code>null</code>.
+        * 
+        * @see Object#toString()
+        */
        default String attr(QName key) {
-               // TODO check String type?
-               Object obj = get(key);
-               if (obj == null)
-                       return null;
-               return obj.toString();
+               return get(key, String.class).orElse(null);
        }
 
+       /**
+        * Convenience method returning an attribute as a {@link String}.
+        * 
+        * @param key the attribute name
+        * @return the attribute value as a {@link String} or <code>null</code>.
+        * 
+        * @see Object#toString()
+        */
        default String attr(QNamed key) {
                return attr(key.qName());
        }
 
+       /**
+        * Convenience method returning an attribute as a {@link String}.
+        * 
+        * @param key the attribute name
+        * @return the attribute value as a {@link String} or <code>null</code>.
+        * 
+        * @see Object#toString()
+        */
        default String attr(String key) {
                return attr(unqualified(key));
        }
-//
-//     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);
-//     }
+
+       /*
+        * CONTEXT
+        */
+       /**
+        * A content within this repository
+        * 
+        * @param path either an absolute path or a path relative to this content
+        */
+       Optional<Content> getContent(String path);
 
        /*
         * EXPERIMENTAL UNSUPPORTED