package com.googlecode.totallylazy.xml.streaming; import com.googlecode.totallylazy.Option; import com.googlecode.totallylazy.Sequence; import com.googlecode.totallylazy.Sequences; import com.googlecode.totallylazy.collections.PersistentList; import com.googlecode.totallylazy.collections.PersistentMap; import javax.xml.stream.events.Characters; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import static com.googlecode.totallylazy.Option.none; import static com.googlecode.totallylazy.Option.some; import static com.googlecode.totallylazy.collections.PersistentList.constructors.empty; import static com.googlecode.totallylazy.collections.PersistentList.constructors.reverse; public class Context implements Node { private final PersistentList<Node> path; private final Sequence<XMLEvent> remainder; public Context(Sequence<XMLEvent> remainder) { this(remainder, empty()); } public Context(Sequence<XMLEvent> remainder, PersistentList<Node> path) { this.remainder = remainder; this.path = path; } public Context push(Node node) { return new Context(remainder.tail(), path.headOption().is(Node::isText) ? path.tail().cons(node) : path.cons(node)); } public Context skip() { return new Context(remainder.tail(), path); } @Override public String toString() { return Sequences.toString(path(), "/"); } public Option<Node> current() { return path.headOption(); } public PersistentList<Node> path() {return reverse(path);} public boolean isEmpty() { return path.isEmpty(); } public Option<Context> next() { if (remainder.isEmpty()) return none(Context.class); XMLEvent head = remainder.head(); if (head instanceof StartElement) return some(push(Element.element((StartElement) head))); if (head instanceof Characters) return some(push(Text.text((Characters) head))); if (head instanceof EndElement) return pop(((EndElement) head).getName().getLocalPart()); return skip().next(); } private Option<Context> pop(String name) { return path.tails().toSequence(). find(path -> path.headOption().is(XPath.name(name))). flatMap(newPath -> new Context(remainder.tail(), newPath.tail()).next()); } public Sequence<Context> relative() { return Xml.contexts(new Context(remainder)).tail(); } @Override public String name() { return path.head().name(); } @Override public String text() { if(isText()) return path.head().text(); return relative(). filter(XPath.xpath(XPath.descendant(XPath.text()))). map(Context::text). toString(""); } @Override public boolean isText() { return path.head().isText(); } @Override public boolean isElement() { return path.head().isElement(); } @Override public PersistentMap<String, String> attributes() { return path.head().attributes(); } }