--- /dev/null
+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();
+}
--- /dev/null
+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();
+ }
+
+}
package org.argeo.api.gcr;
-public interface ContentSession {
+import java.util.function.Supplier;
+
+public interface ContentSession extends Supplier<Content> {
}
--- /dev/null
+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() {
+
+ }
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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));
+
+ }
+}
--- /dev/null
+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;
+ }
+
+}