First draft of GCR API
authorMathieu Baudier <mbaudier@argeo.org>
Sat, 11 Dec 2021 18:19:57 +0000 (19:19 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Sat, 11 Dec 2021 18:19:57 +0000 (19:19 +0100)
org.argeo.api/src/org/argeo/api/gcr/AbstractContent.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/gcr/Content.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/gcr/ContentSession.java
org.argeo.api/src/org/argeo/api/gcr/Contents.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/gcr/xml/DomContent.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/gcr/xml/DomContentSession.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/gcr/xml/ElementIterator.java [new file with mode: 0644]

diff --git a/org.argeo.api/src/org/argeo/api/gcr/AbstractContent.java b/org.argeo.api/src/org/argeo/api/gcr/AbstractContent.java
new file mode 100644 (file)
index 0000000..a54ff1e
--- /dev/null
@@ -0,0 +1,38 @@
+package org.argeo.api.gcr;
+
+import java.util.AbstractMap;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class AbstractContent extends AbstractMap<String, Object> implements Content {
+
+       @Override
+       public Set<Entry<String, 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() {
+                                       // TODO check type
+                                       return attr(key);
+                               }
+
+                               @Override
+                               public Object setValue(Object value) {
+                                       throw new UnsupportedOperationException();
+                               }
+
+                       };
+                       result.add(entry);
+               }
+               return result;
+       }
+
+       protected abstract Iterable<String> keys();
+}
diff --git a/org.argeo.api/src/org/argeo/api/gcr/Content.java b/org.argeo.api/src/org/argeo/api/gcr/Content.java
new file mode 100644 (file)
index 0000000..c5cee9f
--- /dev/null
@@ -0,0 +1,48 @@
+package org.argeo.api.gcr;
+
+import java.util.Map;
+
+public interface Content extends Iterable<Content>, Map<String, Object> {
+
+       String getName();
+
+//     Iterable<String> keys();
+
+       <A> A get(String key, Class<A> clss);
+
+       ContentSession getSession();
+
+       /*
+        * DEFAULT METHODS
+        */
+       default <A> A adapt(Class<A> clss) {
+               return null;
+       }
+
+       /*
+        * CONVENIENCE METHODS
+        */
+       default String attr(String key) {
+               return get(key, String.class);
+       }
+
+       default String attr(Enum<?> key) {
+               return attr(key.name());
+       }
+
+       default <A> A get(Enum<?> key, Class<A> clss) {
+               return get(key.name(), clss);
+       }
+
+       /*
+        * EXPERIMENTAL UNSUPPORTED
+        */
+       default boolean hasText() {
+               return false;
+       }
+
+       default String getText() {
+               throw new UnsupportedOperationException();
+       }
+
+}
index cd2543f8e88a9191e50529d141db50fba25f742e..78325b135b41ed8a63607fd48af815065cd3dfe5 100644 (file)
@@ -1,5 +1,7 @@
 package org.argeo.api.gcr;
 
-public interface ContentSession {
+import java.util.function.Supplier;
+
+public interface ContentSession extends Supplier<Content> {
 
 }
diff --git a/org.argeo.api/src/org/argeo/api/gcr/Contents.java b/org.argeo.api/src/org/argeo/api/gcr/Contents.java
new file mode 100644 (file)
index 0000000..62b1e9b
--- /dev/null
@@ -0,0 +1,44 @@
+package org.argeo.api.gcr;
+
+import java.io.PrintStream;
+import java.util.function.BiConsumer;
+
+public class Contents {
+       public static void traverse(Content content, BiConsumer<Content, Integer> doIt) {
+               traverse(content, doIt, 0);
+       }
+
+       public static void traverse(Content content, BiConsumer<Content, Integer> doIt, int currentDepth) {
+               doIt.accept(content, currentDepth);
+               int nextDepth = currentDepth + 1;
+               for (Content child : content) {
+                       traverse(child, doIt, nextDepth);
+               }
+       }
+
+       public static void print(Content content, PrintStream out, int depth, boolean printText) {
+               StringBuilder sb = new StringBuilder();
+               for (int i = 0; i < depth; i++) {
+                       sb.append("  ");
+               }
+               String prefix = sb.toString();
+               out.println(prefix + content.getName());
+               for (String key : content.keySet()) {
+                       out.println(prefix + " " + key + "=" + content.get(key));
+               }
+               if (printText) {
+                       if (content.hasText()) {
+                               out.println("<![CDATA[" + content.getText().trim() + "]]>");
+                       }
+               }
+       }
+
+       public static <T> boolean isString(T t) {
+               return t instanceof String;
+       }
+
+       /** Singleton. */
+       private Contents() {
+
+       }
+}
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
new file mode 100644 (file)
index 0000000..19bae2d
--- /dev/null
@@ -0,0 +1,108 @@
+package org.argeo.cms.gcr.xml;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.argeo.api.gcr.AbstractContent;
+import org.argeo.api.gcr.Content;
+import org.argeo.api.gcr.ContentSession;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+public class DomContent extends AbstractContent implements Content {
+
+       private final DomContentSession contentSession;
+       private final Element element;
+
+//     private String text = null;
+       private Boolean hasText = null;
+
+       public DomContent(DomContentSession contentSession, Element element) {
+               this.contentSession = contentSession;
+               this.element = element;
+       }
+
+       @Override
+       public Iterator<Content> iterator() {
+               NodeList nodeList = element.getChildNodes();
+               return new ElementIterator(contentSession, nodeList);
+       }
+
+       @Override
+       public String getName() {
+               return element.getNodeName();
+       }
+
+       @Override
+       public Iterable<String> keys() {
+               // TODO implement an iterator?
+               Set<String> result = new HashSet<>();
+               NamedNodeMap attributes = element.getAttributes();
+               for (int i = 0; i < attributes.getLength(); i++) {
+                       Attr attr = (Attr) attributes.item(i);
+                       String attrName = attr.getNodeName();
+                       result.add(attrName);
+               }
+               return result;
+       }
+
+       @Override
+       public <A> A get(String key, Class<A> clss) {
+               if (element.hasAttribute(key)) {
+                       String value = element.getAttribute(key);
+                       if (clss.isAssignableFrom(String.class))
+                               return (A) value;
+                       else
+                               throw new IllegalArgumentException();
+               } else
+                       return null;
+       }
+
+       @Override
+       public ContentSession getSession() {
+               return contentSession;
+       }
+
+       @Override
+       public boolean hasText() {
+//             return element instanceof Text;
+               if (hasText != null)
+                       return hasText;
+               NodeList nodeList = element.getChildNodes();
+               if (nodeList.getLength() > 1) {
+                       hasText = false;
+                       return hasText;
+               }
+               nodes: for (int i = 0; i < nodeList.getLength(); i++) {
+                       Node node = nodeList.item(i);
+                       if (node instanceof Text) {
+                               Text text =(Text) node;
+                               if (!text.isElementContentWhitespace()) {
+                                       hasText = true;
+                                       break nodes;
+                               }
+                       }
+               }
+               if (hasText == null)
+                       hasText = false;
+               return hasText;
+//             if (text != null)
+//                     return true;
+//             text = element.getTextContent();
+//             return text != null;
+       }
+
+       @Override
+       public String getText() {
+               if (hasText())
+                       return element.getTextContent();
+               else
+                       return null;
+       }
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/gcr/xml/DomContentSession.java b/org.argeo.cms/src/org/argeo/cms/gcr/xml/DomContentSession.java
new file mode 100644 (file)
index 0000000..ededa6c
--- /dev/null
@@ -0,0 +1,50 @@
+package org.argeo.cms.gcr.xml;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.argeo.api.gcr.Content;
+import org.argeo.api.gcr.ContentSession;
+import org.argeo.api.gcr.Contents;
+import org.w3c.dom.Document;
+
+public class DomContentSession implements ContentSession {
+       private Document document;
+
+       public DomContentSession(Document document) {
+               this.document = document;
+               this.document.normalizeDocument();
+       }
+
+       @Override
+       public Content get() {
+               return new DomContent(this, document.getDocumentElement());
+       }
+
+       public static void main(String args[]) throws Exception {
+               HashMap<String, Object> map = new HashMap<>();
+               map.put(null, "test");
+               System.out.println(map.get(null));
+
+               Set<String> set = new HashSet<>();
+               set.add(null);
+
+               DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+               DocumentBuilder dBuilder = factory.newDocumentBuilder();
+               Path testFile;
+               testFile = Paths.get(System.getProperty("user.home") + "/dev/git/unstable/argeo-commons/pom.xml");
+               testFile = Paths.get(System.getProperty("user.home") + "/tmp/test.xml");
+               Document doc = dBuilder.parse(Files.newInputStream(testFile));
+
+               DomContentSession contentSession = new DomContentSession(doc);
+               Contents.traverse(contentSession.get(), (c, d) -> Contents.print(c, System.out, d, true));
+
+       }
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/gcr/xml/ElementIterator.java b/org.argeo.cms/src/org/argeo/cms/gcr/xml/ElementIterator.java
new file mode 100644 (file)
index 0000000..a5bde1c
--- /dev/null
@@ -0,0 +1,54 @@
+package org.argeo.cms.gcr.xml;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.argeo.api.gcr.Content;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class ElementIterator implements Iterator<Content> {
+       private final DomContentSession contentSession;
+       private final NodeList nodeList;
+
+       private int currentIndex;
+       private final int length;
+       private Element nextElement = null;
+
+       public ElementIterator(DomContentSession contentSession, NodeList nodeList) {
+               this.contentSession = contentSession;
+               this.nodeList = nodeList;
+
+               this.length = nodeList.getLength();
+               this.currentIndex = 0;
+               this.nextElement = findNext();
+       }
+
+       private Element findNext() {
+               while (currentIndex < length) {
+                       Node node = nodeList.item(currentIndex);
+                       if (node instanceof Element) {
+                               return (Element) node;
+                       }
+                       currentIndex++;
+               }
+               return null;
+       }
+
+       @Override
+       public boolean hasNext() {
+               return nextElement != null;
+       }
+
+       @Override
+       public Content next() {
+               if (nextElement == null)
+                       throw new NoSuchElementException();
+               DomContent result = new DomContent(contentSession, nextElement);
+               currentIndex++;
+               nextElement = findNext();
+               return result;
+       }
+
+}