ofc4j.OFC.java Source code

Java tutorial

Introduction

Here is the source code for ofc4j.OFC.java

Source

/*
This file is part of OFC4J.
    
OFC4J is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
    
OFC4J is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
See <http://www.gnu.org/licenses/lgpl-3.0.txt>.
*/

package ofc4j;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import ofc4j.model.Chart;
import ofc4j.model.Text;
import ofc4j.model.Tooltip;
import ofc4j.model.axis.Axis;
import ofc4j.model.axis.Label;
import ofc4j.model.axis.XAxis;
import ofc4j.model.axis.XAxisLabels;
import ofc4j.model.axis.YAxis;
import ofc4j.model.elements.AreaHollowChart;
import ofc4j.model.elements.BarChart;
import ofc4j.model.elements.Element;
import ofc4j.model.elements.FilledBarChart;
import ofc4j.model.elements.HorizontalBarChart;
import ofc4j.model.elements.LineChart;
import ofc4j.model.elements.PieChart;
import ofc4j.model.elements.ScatterChart;
import ofc4j.model.elements.SketchBarChart;
import ofc4j.model.elements.StackedBarChart;
import ofc4j.model.metadata.Alias;
import ofc4j.model.metadata.Converter;
import ofc4j.org.json.JSONException;
import ofc4j.org.json.JSONObject;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConverterMatcher;
import com.thoughtworks.xstream.converters.SingleValueConverter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;

/**
 * <p>This is the class responsible for converting a Chart object into the JSON
 * string which feeds the charting widget.  There is no need to make explicit
 * use of this class, but if necessary, there are several ways to do so:</p>
 * <ol>
 * <li>The "instance" field contains a static instance of an OFC object.</li>
 * <li>The Chart object overrides toString() and uses this instance to render itself.</li>
 * <li>For tricky threading situations, you may prefer to create and manage instances of OFC yourself.</li>  
 * </ol>
 * <p>Theoretically, XStream (the JSON conversion library used here) is thread-safe, but
 * it does not hurt to have the option to synchronize or to have thread local instances,
 * whatever may be necessary.</p>
 */
public class OFC {
    private static final Class<?>[] models = new Class<?>[] { Chart.class, Axis.class, Text.class, XAxis.class,
            YAxis.class, XAxisLabels.class, Label.class, Element.class, Axis.class, BarChart.class, PieChart.class,
            HorizontalBarChart.class, LineChart.class, ScatterChart.class, AreaHollowChart.class,
            PieChart.Slice.class, HorizontalBarChart.Bar.class, Label.Rotation.class, ScatterChart.Point.class,
            FilledBarChart.class, SketchBarChart.class, StackedBarChart.class, StackedBarChart.StackValue.class,
            StackedBarChart.Stack.class, BarChart.Bar.class, FilledBarChart.Bar.class, SketchBarChart.Bar.class,
            LineChart.Dot.class, Tooltip.class };

    private final XStream converter = new XStream(new JsonHierarchicalStreamDriver());

    /**
     * Sole constructor.
     */
    public OFC() {
        for (Class<?> c : models) {
            doAlias(c);
            doRegisterConverter(c);
        }
    }

    public static OFC getInstance() {
        return LazyInstance.instance;
    }

    private void doAlias(Class<?> c) {
        if (c.isAnnotationPresent(Alias.class)) {
            converter.alias(c.getAnnotation(Alias.class).value(), c);
        }
        for (Field f : c.getDeclaredFields()) {
            if (f.isAnnotationPresent(Alias.class)) {
                converter.aliasField(f.getAnnotation(Alias.class).value(), c, f.getName());
            }
        }
    }

    private void doRegisterConverter(Class<?> c) {
        if (c.isAnnotationPresent(Converter.class)) {
            Class<? extends ConverterMatcher> clazz = c.getAnnotation(Converter.class).value();
            try {
                if (SingleValueConverter.class.isAssignableFrom(clazz)) {
                    converter.registerConverter((SingleValueConverter) clazz.newInstance());
                } else {
                    converter
                            .registerConverter((com.thoughtworks.xstream.converters.Converter) clazz.newInstance());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Use this method in your applications to send data back to the chart widget.
     * @param c the chart to render
     * @throws OFCException when rendering fails
     * @return the JSONified chart data
     */
    public String render(Chart c) throws OFCException {
        String json = converter.toXML(c);
        try {
            return new JSONObject(json).getString(Chart.class.getName());
        } catch (JSONException je) {
            throw new OFCException(json, je);
        }
    }

    /**
     * Use this method for debugging purposes.
     * @param c the chart to render
     * @param indentationLevel number of spaces to use for indentation
     * @throws OFCException when rendering fails
     * @return pretty-printed JSONified chart data
     */
    public String prettyPrint(Chart c, int indentationLevel) throws OFCException {
        String json = converter.toXML(c);
        try {
            return new JSONObject(json).getJSONObject(Chart.class.getName()).toString(indentationLevel);
        } catch (JSONException je) {
            throw new OFCException(json, je);
        }
    }

    /**
     * Convenience method for converting Collections to Arrays.  You can use this where the API
     * has limited support for collections:
     * getLabels().addLabels(OFC.toArray(stringList, String.class));
     * @param collection The collection to use
     * @param type The supertype for the collection.  This will commonly be Integer, Number, etc.
     * @return the array of the collection
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] toArray(Collection<T> collection, Class<? extends T> type) {
        return collection.toArray((T[]) Array.newInstance(type, collection.size()));
    }

    /**
     * Convenience method to generate labels from a collection of Objects, if so desired.
     * @param source the source collection holding Objects.
     * @return a collection of all the objects toString() method invoked
     */
    public static List<String> stringify(List<? extends Object> source) {
        List<String> strings = new ArrayList<String>(source.size());
        for (Object o : source) {
            strings.add(o.toString());
        }
        return strings;
    }

    /**
     * Convenience method to generate labels from an array of disparate Objects.
     * @param objects the array of objects to convert
     * @return a collection of all the objects toString() result
     */
    public static String[] stringify(Object... objects) {
        return stringify(Arrays.asList(objects)).toArray(new String[objects.length]);
    }
}

class LazyInstance {
    protected static final OFC instance = new OFC();
}