Teaser Image

qnoid

Markos Charatzas - London, UK




/**
 * A collection of pairs ordered based on the order of their {@link Element}s
 * 
 * @see ElementBuilder
 */
public class PairSet implements Iterable<Pair<?>>
{
    /**
     * Create a new JSON object with the specified pairs
     * 
     * @param pairs the pairs to the JSON object
     * @return a JsonObject with the specified pairs
     */
    public static final PairSet of(Pair<?>... pairs)
    {
        PairSet pairSet = new PairSet();
        
        for (Pair<?> pair : pairs) {
            pairSet.add(pair);
        }
        
    return pairSet;
    }    

    /*
     * Using TreeMap implementation for sorting
     */
    private final Map<Element<?>, Pair<?>> pairs = 
        new TreeMap<Element<?>, Pair<?>>();
    
    /**
     * Adds a new pair value
     * @param <T> the pair type
     * @param pair the pair to add
     */
    public <T> PairSet add(Pair<T> pair)
    {
        this.pairs.put(pair.element(), pair);
    return this;
    }
    
    @Override
    public Iterator<Pair<?>> iterator(){
    return this.pairs.values().iterator();
    }
        
    /**
     * 
     * @param <T>
     * @param element the type of the pair
     * @return the pair instance
     * @see Element#type()
     */
    public <T> T get(Element<T> element) {
    return element.type().cast( this.pairs.get(element).value() );
    }

    public int size(){
    return this.pairs.size();
    }
}


/**
 * A non-complex JSON object
 */
public class JsonObject 
{
    public static JsonObject of(Pair<?>... pairs) {
    return new JsonObject( PairSet.of(pairs) );
    }

    private static final Element<Type> LAST_ELEMENT = Elements.TYPE;
    
    private static final String LEFT_BRACE = "{";
    private static final String RIGHT_BRACE = "}";
    private static final String COMMA = ",";
    
    private final PairSet pairs;

    JsonObject(PairSet pairs) 
    {
        this.pairs = pairs;
    }
    
    private boolean isLast(Pair<?> pair){
    return LAST_ELEMENT.equals(pair.element());
    }
    
    private void appendEveryPairOf(StringBuilder builder, PairSet pairs)
    {
        for (Pair<?> pair : pairs) 
        {
            builder.append( pair );            
            appendCommaIfNotLast(builder, pair);
        }
    }

    private void appendCommaIfNotLast(StringBuilder builder, Pair<?> pair)
    {
        boolean isLast = isLast(pair);
        
        if(!isLast){
            builder.append( COMMA );
        }
    }

    /**
     * 
     * @param <T>
     * @param element the element which is present in the JSON object
     * @return its value
     */
    public <T> T valueOf(Element<T> element){
    return this.pairs.get(element);        
    }
    
    @Override
    public String toString()
    {
      StringBuilder builder = new StringBuilder();
        
      builder.append(LEFT_BRACE);
        
      appendEveryPairOf(builder, this.pairs);
        
      builder.append(RIGHT_BRACE);
        
    return builder.toString();
    }
}


/**
 */
@RunWith(Theories.class)
public class PairSetTest
{
    private static final Integer ID_VALUE = 1;
    
    private static final Pair<Integer> ID_PAIR = 
        Pair.newPair(Elements.ID, ID_VALUE);
    
    private static final String NAME_VALUE = "Kyle Bragger";
    
    private static final Pair<String> NAME_PAIR = 
        Pair.newPair(Elements.NAME, NAME_VALUE);
    
    private static final Date DATE_VALUE = 
        Calendar.getInstance().getTime();
    
    private static final Pair<Date> DATE_PAIR = 
        Pair.newPair(Elements.DATE, DATE_VALUE);
    
    private static final Type TYPE_VALUE = Type.DEVELOPER;
    
    private static final Pair<Type> TYPE_PAIR = 
        Pair.newPair(Elements.TYPE, TYPE_VALUE);
    
    private static final Pair<?>[] PAIRS = 
        new Pair<?>[]{
            ID_PAIR, 
            NAME_PAIR, 
            DATE_PAIR, 
            TYPE_PAIR};
    
    @DataPoints
    public static final Element<?>[] ELEMENTS = 
        {Elements.ID, Elements.NAME, Elements.TYPE, Elements.DATE};
    
    /**
     * @param pairs
     * @param element
     */
    private <T> void assertNotNull(PairSet pairs, Element<T> element) 
    {
        T value = pairs.get(element);
        
        Assert.assertNotNull(value);
    }

    @Test
    public void of() throws Exception
    {
        PairSet pairs = PairSet.of( PairSetTest.PAIRS ); 
        
        Assert.assertEquals(4, pairs.size());        
    }
    
    @Theory
    public void get(Element<?> element) throws Exception
    {
        PairSet pairs = PairSet.of( PairSetTest.PAIRS );
        
        assertNotNull(pairs, element);
    }
}


/**
 */
public class JsonObjectTest 
{
    private static final Integer ID_VALUE = 1;
    
    private static final Pair<Integer> ID_PAIR = 
        Pair.newPair(Elements.ID, ID_VALUE);
    
    private static final String NAME_VALUE = "Kyle Bragger";
    
    private static final Pair<String> NAME_PAIR = 
        Pair.newPair(Elements.NAME, NAME_VALUE);
    
    private static final Date DATE_VALUE = 
        Calendar.getInstance().getTime();
    
    private static final Pair<Date> DATE_PAIR = 
        Pair.newPair(Elements.DATE, DATE_VALUE);
    
    private static final Type TYPE_VALUE = Type.DEVELOPER;
    
    private static final Pair<Type> TYPE_PAIR = 
        Pair.newPair(Elements.TYPE, TYPE_VALUE);
    
    private static final Pair<?>[] PAIRS = 
        new Pair<?>[]{
            ID_PAIR, 
            NAME_PAIR, 
            DATE_PAIR, 
            TYPE_PAIR};
    
    private static final String toString =
        String.format({%s,%s,%s,%s}", ID_PAIR, NAME_PAIR, DATE_PAIR, TYPE_PAIR);

    @Test
    public void string() throws Exception 
    {
    JsonObject jsonObject = JsonObject.of( PAIRS );
        
    Assert.assertEquals(toString, jsonObject.toString());
    }
    
    @Test
    public void valueOfId() throws Exception 
    {
          JsonObject jsonObject = JsonObject.of( PAIRS );
        
      Assert.assertEquals(ID_VALUE, jsonObject.valueOf(Elements.ID));
    }

    @Test
    public void valueOfName() throws Exception 
    {
      JsonObject jsonObject = JsonObject.of( PAIRS );
        
      Assert.assertEquals(NAME_VALUE, jsonObject.valueOf(Elements.NAME));
    }

    @Test
    public void valueOfType() throws Exception 
    {
      JsonObject jsonObject = JsonObject.of( PAIRS );
        
      Assert.assertEquals(TYPE_VALUE, jsonObject.valueOf(Elements.TYPE));
    }

    @Test
    public void valueOfDate() throws Exception 
    {
      JsonObject jsonObject = JsonObject.of( PAIRS );
        
      Assert.assertEquals(DATE_VALUE, jsonObject.valueOf(Elements.DATE));
    }
}

This is the final post of the "How to think of JSON" series using heterogeneous containers.

In this example the PairSet plays the role of such a container backed by a TreeMap. The TreeMap implementation is used to keep our elements sorted based on their natural ordering which in this case merely helps us assert the JsonObject#toString method.

Note how the PairSet class also implements the Iterable interface thus the

    Iterator<T> iterator();

method which allows us to use the enhanced for loop.

        for (Pair<?> pair : pairs) 
        {
            builder.append( pair );            
            appendCommaIfNotLast(builder, pair);
        }

Make sure you incorporate it in classes that represent collections of objects.

As it all comes together (Element, ElementFormat, Pair, PairSet) we end up with a JsonObject class which not only allows us to query for any JSON element to get its value back to the right type but get a properly formatted String representation.

Once acquainted feel free to leave your comments below on how heterogeneous containers think can aid your design.

Source