package tc.oc.pgm.xml.parser; import javax.annotation.Nullable; import com.google.common.reflect.TypeParameter; import com.google.common.reflect.TypeToken; import com.google.inject.TypeLiteral; import tc.oc.commons.core.reflect.Types; import tc.oc.pgm.xml.InvalidXMLException; import tc.oc.pgm.xml.Node; /** * Parser for a type {@link T} that can be represented as a single {@link String} * * The source {@link Node} and {@link String} can be specified seperately, * allowing multiple instances to be parsed from a single {@link Node}. */ public abstract class PrimitiveParser<T> implements Parser<T> { protected final @Nullable TypeToken<T> type; protected PrimitiveParser(@Nullable TypeToken<T> type) { this.type = type; } protected PrimitiveParser() { this(null); } @Override public TypeToken<T> paramToken() { return type != null ? type : Parser.super.paramToken(); } @Override public T parseInternal(Node node) throws InvalidXMLException { try { return parseInternal(node, node.getValue()); } catch(FormatException e) { throw wrapFormatException(node, e); } } protected abstract T parseInternal(Node node, String text) throws FormatException, InvalidXMLException; public T parse(Node node, String text) throws InvalidXMLException { return InvalidXMLException.offeringNode(node, () -> { try { return parseInternal(node, text); } catch(FormatException e) { throw wrapFormatException(node, e); } }); } protected InvalidXMLException wrapFormatException(Node node, FormatException e) { String message = "Invalid " + readableTypeName() + " format"; if(e.getMessage() != null) { message += ": " + e.getMessage(); } return new InvalidXMLException(message, node); } public static class FormatException extends Exception { public FormatException() {} public FormatException(@Nullable String message) { super(message); } } static <T> TypeToken<PrimitiveParser<T>> typeOf(TypeToken<T> type) { return new TypeToken<PrimitiveParser<T>>(){}.where(new TypeParameter<T>(){}, type); } static <T> TypeLiteral<PrimitiveParser<T>> typeOf(TypeLiteral<T> type) { return Types.toLiteral(typeOf(Types.toToken(type))); } }