package se.dolkow.tangiblexml; import android.support.annotation.NonNull; import android.util.Log; import android.util.Pair; import org.xmlpull.v1.XmlPullParser; import java.lang.reflect.Field; import static se.dolkow.tangiblexml.Util.TAG; /** * A node that dynamically creates an object and uses its @TangibleField fields to populate it. */ final class TangibleNode<T,V> extends ParserNode<T> { private final @NonNull Class<? extends V> what; private final @NonNull Putter<T,V> putter; private final @NonNull InnerNode<V> valueRoot; TangibleNode(@NonNull Putter<T, V> putter, @NonNull Class<? extends V> what) throws SetupException { this.putter = putter; this.what = what; try { what.getConstructor(); } catch (NoSuchMethodException e) { String msg = what.getName() + " doesn't have an accessible no-arg constructor"; throw new SetupException(msg); } valueRoot = NodeFactory.createTree(what); } @Override protected void prepare(@NonNull T target) { // do nothing. } @Override public void parse(@NonNull XmlPullParser xml, @NonNull T target) throws InputException, InvalidFieldException, ValueCountException, ConversionException, NotSupportedException, ReflectionException { if (Parser.debug) { Log.d(TAG, "trying to parse " + what.getSimpleName() + " from " + xml.getName()); } final V result; try { result = what.newInstance(); } catch (InstantiationException e) { throw new ReflectionException("Failed to create instance of " + what.getName(), e); } catch (IllegalAccessException e) { throw new ReflectionException("Failed to create instance of " + what.getName(), e); } valueRoot.parse(xml, result); TangibleFieldCache cache = TangibleFieldCache.getInstance(); try { for (Pair<Field, TangibleField> pair : cache.get(result.getClass())) { Field f = pair.first; TangibleField a = pair.second; if (a.required() && f.get(result) == null) { String msg = "Required field '" + f + "' missing"; throw new ValueCountException(msg); } } } catch (IllegalAccessException e) { throw new ReflectionException(e); } putter.put(target, result); } @Override public void dump(@NonNull StringBuilder sb, int indent, @NonNull String prefix) { super.dump(sb, indent, prefix); valueRoot.dump(sb, indent+2, prefix+":content"); } }