package se.dolkow.tangiblexml;
import android.support.annotation.NonNull;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
/**
* A node that parses its children, just passing the target along.
*/
final class InnerNode<T> extends ParserNode<T> {
final HashMap<String,ParserNode<T>> children = new HashMap<>();
@Override
protected void prepare(@NonNull T target)
throws NotSupportedException, ValueCountException, ReflectionException {
for (ParserNode<T> child : children.values()) {
child.prepare(target);
}
}
@Override
public void parse(@NonNull XmlPullParser xml, @NonNull T target)
throws InputException, NotSupportedException, ValueCountException,
ReflectionException, ConversionException, InvalidFieldException {
try {
Util.consume(xml, START_TAG);
for (ParserNode node : children.values()) {
//noinspection unchecked
node.prepare(target);
}
while (xml.getEventType() != END_TAG) {
if (xml.getEventType() != START_TAG) {
xml.next();
continue;
}
String name = xml.getName();
ParserNode child = children.get(name);
if (child == null) {
Util.skip(xml);
} else {
//noinspection unchecked
child.parse(xml, target);
}
}
Util.consume(xml, END_TAG);
} catch (XmlPullParserException e) {
throw new InputException(e);
} catch (IOException e) {
throw new InputException(e);
}
}
@Override
public void dump(@NonNull StringBuilder sb, int indent, @NonNull String prefix) {
super.dump(sb, indent, prefix);
for (Map.Entry<String,ParserNode<T>> entry : children.entrySet()) {
entry.getValue().dump(sb, indent+2, entry.getKey());
}
}
public <V> void extend(@NonNull String[] pathParts, @NonNull Putter<T,V> putter,
@NonNull Type leafType) throws SetupException {
final int N = pathParts.length;
if (N == 0) {
throw new SetupException("BUG: node path is empty");
}
InnerNode<T> parent = this;
for (int i=0; i<N-1; ++i) {
String part = pathParts[i];
if (part.length() == 0) {
String msg = "BUG: empty component in " + Arrays.toString(pathParts);
throw new SetupException(msg);
}
ParserNode child = parent.children.get(part);
if (child == null) {
child = new InnerNode<>();
//noinspection unchecked
parent.children.put(part, child);
} else if (child.getClass() != InnerNode.class) {
// if it's not *exactly* that class, we have a problem
String arr = Arrays.toString(pathParts);
String msg = "BUG: conflicting uses of " + part + " in " + arr;
throw new SetupException(msg);
}
//noinspection unchecked
parent = (InnerNode)child;
}
String part = pathParts[N-1];
if (part.length() == 0) {
throw new SetupException("BUG: empty component in " + Arrays.toString(pathParts));
}
ParserNode old = parent.children.put(part, NodeFactory.create(putter, leafType));
if (old != null) {
String msg = "BUG: double use of " + part + " in " + Arrays.toString(pathParts);
throw new SetupException(msg);
}
}
}