package org.andork.spec.json; import java.math.BigDecimal; import java.math.BigInteger; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; /** * Base class for {@link JsonSpecObject} format specifications. To create a specification for a new type, simply create a subclass that calls one of the * constructors with the appropriate {@link Attribute}s and child specs. * * @author james.a.edwards */ public abstract class JsonSpec<S extends JsonSpec<S>> { private final Map<String, Attribute<?>> attributes = new HashMap<String, Attribute<?>>( ); /** * Creates a Spec with the attributes. * * @param attributes * the attributes. */ protected JsonSpec( Attribute<?> ... attributes ) { if( attributes != null ) { for( Attribute<?> Attribute : attributes ) { this.attributes.put( Attribute.getName( ) , Attribute ); } } } public JsonSpecObject<S> fromJson( JsonObject json ) throws Exception { JsonSpecObject<S> result = newObject( ); for( Map.Entry<String, JsonElement> entry : json.entrySet( ) ) { Attribute attrib = getAttribute( entry.getKey( ) ); if( attrib != null ) { result.set( attrib , attrib.getFormat( ).parse( entry.getValue( ) ) ); } } return result; } public JsonSpecObject<S> newObject( ) { return JsonSpecObject.newInstance( ( S ) this ); } /** * Converts an attribute value to and from {@link String} for JSON. * * @author james.a.edwards * * @param <T> * the type of the attribute value. */ public static interface Format<T> { public JsonElement format( T t ); public T parse( JsonElement s ) throws Exception; } /** * Defines an attribute that can be parsed by {@link JsonSpecObject#parseXml(String)}. * * @author james.a.edwards * * @param <T> * the type of the attribute value. */ public static class Attribute<T> { private final String name; private final Format<T> format; /** * Creates an attribute with the given name and format. * * @param name * the name used in XML. * @param format * the format for converting from the attribute value to and from a {@code String} for XML. */ public Attribute( String name , Format<T> format ) { super( ); this.name = name; this.format = format; } /** * Gets the attribute name used in XML. * * @return the attribute name. */ public String getName( ) { return name; } /** * Gets the format for converting from the attribute value to and from a {@code String} for XML. * * @return the format. */ public Format<T> getFormat( ) { return format; } } public static class StringFormat implements Format<String> { public JsonElement format( String t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( t ); } public String parse( JsonElement s ) { return s == null || s == JsonNull.INSTANCE ? null : s.getAsString( ); } } public static class EnumFormat<E extends Enum<E>> implements Format<E> { Class<E> cls; private EnumFormat( Class<E> cls ) { this.cls = cls; } public static <E extends Enum<E>> EnumFormat<E> newInstance( Class<E> cls ) { return new EnumFormat<E>( cls ); } @Override public JsonElement format( E t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( t.name( ) ); } @Override public E parse( JsonElement s ) throws Exception { return s == null || s == JsonNull.INSTANCE ? null : Enum.valueOf( cls , s.getAsString( ) ); } } public static class BooleanFormat implements Format<Boolean> { public JsonElement format( Boolean t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( t ); } public Boolean parse( JsonElement s ) { return s == null || s == JsonNull.INSTANCE ? null : s.getAsBoolean( ); } } public static class IntegerFormat implements Format<Integer> { public JsonElement format( Integer t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( t ); } public Integer parse( JsonElement s ) { return s == null || s == JsonNull.INSTANCE ? null : s.getAsInt( ); } } public static class LongFormat implements Format<Long> { public JsonElement format( Long t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( t ); } public Long parse( JsonElement s ) { return s == null || s == JsonNull.INSTANCE ? null : s.getAsLong( ); } } public static class FloatFormat implements Format<Float> { public JsonElement format( Float t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( t ); } public Float parse( JsonElement s ) { return s == null || s == JsonNull.INSTANCE ? null : s.getAsFloat( ); } } public static class DoubleFormat implements Format<Double> { public JsonElement format( Double t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( t ); } public Double parse( JsonElement s ) { return s == null || s == JsonNull.INSTANCE ? null : s.getAsDouble( ); } } public static class BigIntegerFormat implements Format<BigInteger> { public JsonElement format( BigInteger t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( t ); } public BigInteger parse( JsonElement s ) { return s == null || s == JsonNull.INSTANCE ? null : s.getAsBigInteger( ); } } public static class BigDecimalFormat implements Format<BigDecimal> { public JsonElement format( BigDecimal t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( t ); } public BigDecimal parse( JsonElement s ) { return s == null || s == JsonNull.INSTANCE ? null : s.getAsBigDecimal( ); } } public static class DateFormat implements Format<Date> { java.text.DateFormat format; public DateFormat( java.text.DateFormat format ) { this.format = format; } public DateFormat( String simpleDateFormatPattern ) { this( new SimpleDateFormat( simpleDateFormatPattern ) ); } @Override public synchronized JsonElement format( Date t ) { return t == null ? JsonNull.INSTANCE : new JsonPrimitive( format.format( t ) ); } @Override public synchronized Date parse( JsonElement s ) throws Exception { return s == null || s == JsonNull.INSTANCE ? null : format.parse( s.getAsString( ) ); } } public static class SpecObjectFormat<S extends JsonSpec<S>> implements Format<JsonSpecObject<S>> { S spec; private SpecObjectFormat( S spec ) { super( ); this.spec = spec; } public static <S extends JsonSpec<S>> SpecObjectFormat<S> newInstance( S spec ) { return new SpecObjectFormat<S>( spec ); } @Override public JsonElement format( JsonSpecObject<S> t ) { return t == null ? JsonNull.INSTANCE : t.toJson( ); } @Override public JsonSpecObject<S> parse( JsonElement s ) throws Exception { return s == null || s == JsonNull.INSTANCE ? null : spec.fromJson( ( JsonObject ) s ); } } public static class SpecObjectStringFormat<S extends JsonSpec<S>> implements org.andork.util.Format<JsonSpecObject<S>> { S spec; JsonParser parser = new JsonParser( ); public SpecObjectStringFormat( S spec ) { super( ); this.spec = spec; } public static <S extends JsonSpec<S>> SpecObjectFormat<S> newInstance( S spec ) { return new SpecObjectFormat<S>( spec ); } @Override public String format( JsonSpecObject<S> t ) { return t == null ? null : t.toJson( ).toString( ); } @Override public JsonSpecObject<S> parse( String s ) throws Exception { if( s == null || "".equals( s ) ) { return spec.newObject( ); } return s == null || "".equals( s ) ? null : spec.fromJson( ( JsonObject ) parser.parse( s ) ); } } public static class SpecArrayListFormat<E> implements Format<JsonSpecArrayList<E>> { Format<? super E> format; private SpecArrayListFormat( Format<? super E> format ) { super( ); this.format = format; } public static <E> SpecArrayListFormat<E> newInstance( Format<? super E> format ) { return new SpecArrayListFormat<E>( format ); } @Override public JsonElement format( JsonSpecArrayList<E> t ) { return t == null ? JsonNull.INSTANCE : t.toJson( ); } @Override public JsonSpecArrayList<E> parse( JsonElement s ) throws Exception { return s == null || s == JsonNull.INSTANCE ? null : JsonSpecArrayList.fromJson( ( JsonArray ) s , format ); } } /** * Gets the {@link Attribute} with the given name. * * @param name * the name of the attribute to get. * @return the {@code Attribute} with the given name, or {@code null} if none is part of this spec. */ public Attribute<?> getAttribute( String name ) { return attributes.get( name ); } /** * Gets all {@link Attribute}s in this spec. * * @return an unmodifiable {@link Collection} of {@code Attribute}s. */ public Collection<Attribute<?>> getAttributes( ) { return Collections.unmodifiableCollection( attributes.values( ) ); } public static Attribute<String> stringAttribute( String name ) { return new Attribute<String>( name , new StringFormat( ) ); } public static <E extends Enum<E>> Attribute<E> enumAttribute( String name , Class<E> cls ) { return new Attribute<E>( name , EnumFormat.newInstance( cls ) ); } public static Attribute<Boolean> booleanAttribute( String name ) { return new Attribute<Boolean>( name , new BooleanFormat( ) ); } public static Attribute<Integer> integerAttribute( String name ) { return new Attribute<Integer>( name , new IntegerFormat( ) ); } public static Attribute<Long> longAttribute( String name ) { return new Attribute<Long>( name , new LongFormat( ) ); } public static Attribute<Float> floatAttribute( String name ) { return new Attribute<Float>( name , new FloatFormat( ) ); } public static Attribute<Double> doubleAttribute( String name ) { return new Attribute<Double>( name , new DoubleFormat( ) ); } public static Attribute<BigInteger> bigIntegerAttribute( String name ) { return new Attribute<BigInteger>( name , new BigIntegerFormat( ) ); } public static Attribute<BigDecimal> bigDecimalAttribute( String name ) { return new Attribute<BigDecimal>( name , new BigDecimalFormat( ) ); } public static <S extends JsonSpec<S>> Attribute<JsonSpecObject<S>> specObjectAttribute( String name , S spec ) { return new Attribute<JsonSpecObject<S>>( name , SpecObjectFormat.newInstance( spec ) ); } public static <E> Attribute<JsonSpecArrayList<E>> specArrayListAttribute( String name , Format<? super E> format ) { return new Attribute<JsonSpecArrayList<E>>( name , SpecArrayListFormat.newInstance( format ) ); } }