Package com.cedarsoftware.ncube.formatters

Source Code of com.cedarsoftware.ncube.formatters.JsonFormatter

package com.cedarsoftware.ncube.formatters;

import com.cedarsoftware.ncube.Axis;
import com.cedarsoftware.ncube.BinaryUrlCmd;
import com.cedarsoftware.ncube.Column;
import com.cedarsoftware.ncube.GroovyExpression;
import com.cedarsoftware.ncube.GroovyMethod;
import com.cedarsoftware.ncube.GroovyTemplate;
import com.cedarsoftware.ncube.NCube;
import com.cedarsoftware.ncube.Range;
import com.cedarsoftware.ncube.RangeSet;
import com.cedarsoftware.ncube.StringUrlCmd;
import com.cedarsoftware.ncube.UrlCommandCell;
import com.cedarsoftware.ncube.proximity.LatLon;
import com.cedarsoftware.ncube.proximity.Point2D;
import com.cedarsoftware.ncube.proximity.Point3D;
import com.cedarsoftware.util.SafeSimpleDateFormat;
import com.cedarsoftware.util.StringUtilities;
import com.cedarsoftware.util.io.JsonWriter;

import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Format an NCube into an JSON document
*
* @author John DeRegnaucourt (jdereg@gmail.com)
*         <br/>
*         Copyright (c) Cedar Software LLC
*         <br/><br/>
*         Licensed under the Apache License, Version 2.0 (the "License");
*         you may not use this file except in compliance with the License.
*         You may obtain axis copy of the License at
*         <br/><br/>
*         http://www.apache.org/licenses/LICENSE-2.0
*         <br/><br/>
*         Unless required by applicable law or agreed to in writing, software
*         distributed under the License is distributed on an "AS IS" BASIS,
*         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*         See the License for the specific language governing permissions and
*         limitations under the License.
*/
public class JsonFormatter implements NCubeFormatter
{
    private StringBuilder builder = new StringBuilder();
    static final SafeSimpleDateFormat dateFormat = new SafeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
    private String quotedStringFormat = "\"%s\"";
    private String name;
    private Map<Long, Object> userIds = new HashMap<Long, Object>();
    private Map<Long, Long> generatedIds = new HashMap<Long, Long>();
    private long idCounter;

    public JsonFormatter()
    {
    }

    /**
     * Use this API to generate axis JSON view of this NCube.
     * @return String containing axis JSON view of this NCube.
     */
    public String format(NCube ncube)
    {
        try
        {
            name = ncube.getName();
            builder.setLength(0);
            userIds.clear();
            generatedIds.clear();
            idCounter = 0;
            walkIds(ncube.getAxes());

            startObject();

            writeAttribute("ncube", name, true);
            writeAttribute("ruleMode", ncube.getRuleMode(), true);
            if (ncube.getDefaultCellValue() != null)
            {
                writeValue("defaultCellValue", ncube.getDefaultCellValue());
                comma();
            }
            writeAxes(ncube.getAxes());
            writeCells(ncube.getCellMap());

            endObject();

            return builder.toString();
        }
        catch (Exception e)
        {
            throw new IllegalStateException(String.format("Unable to format NCube '%s' into JSON", ncube.getName()), e);
        }
    }

    public void walkIds(List<Axis> axes)
    {
        Set<Comparable> set = new HashSet<Comparable>();
        for (Axis item : axes)
        {
            for (Column c : item.getColumnsWithoutDefault())
            {
                if ((c.getValue() instanceof String) || (c.getValue() instanceof Long))
                {
                    if (!set.contains(c.getValue()))
                    {
                        set.add(c.getValue());
                        userIds.put(c.getId(), c.getValue());
                    }
                }
            }
        }
    }

    public void startArray() {
        builder.append("[");
    }

    public void endArray() {
        builder.append("]");
    }

    public void startObject() {
        builder.append("{");
    }

    public void endObject() {
        builder.append("}");
    }

    public void comma() {
        builder.append(",");
    }

    public void writeAxes(List<Axis> axes) throws IOException
    {
        builder.append(String.format(quotedStringFormat, "axes"));
        builder.append(':');
        startArray();
        for (Axis item : axes)
        {
            writeAxis(item);
            comma();
        }
        uncomma();
        endArray();
        comma();
    }

    // default is false, so no need to write those out.
    public void writeAxis(Axis axis) throws IOException
    {
        startObject();

        // required inputs
        writeAttribute("name", axis.getName(), true);
        writeAttribute("type", axis.getType().name(), true);
        writeAttribute("valueType", axis.getValueType().name(), true);

        //  optional inputs that can use defaults
        writeAttribute("preferredOrder", axis.getColumnOrder(), true);
        writeAttribute("hasDefault", axis.hasDefaultColumn(), true);
        writeAttribute("multiMatch", axis.isMultiMatch(), true);

        writeColumns(axis.getColumns());
        endObject();
    }

    public void writeColumns(List<Column> columns) throws IOException
    {
        builder.append("\"columns\":");
        startArray();
        boolean commaWritten = false;

        for (Column item : columns)
        {
            if (!item.isDefault())
            {
                commaWritten = true;
                writeColumn(item);
                comma();
            }
        }

        if (commaWritten)
        {
            uncomma();
        }

        endArray();
    }

    public void writeColumn(Column c) throws IOException
    {
        startObject();

        //  Check to see if id exists anywhere. then optimize
        Object o = userIds.get(c.getId());
        String columnType = getColumnType(c.getValue());
        if (o != null && o.equals(c.getValue()))
        {
            writeType(columnType);
            writeId(c.getId(), false);
        }
        else
        {
            writeId(c.getId(), true);
            writeType(columnType);
            if (c.getValue() instanceof UrlCommandCell) {
                writeCommandCell((UrlCommandCell)c.getValue());
            } else {
                writeValue("value", c.getValue());
            }
        }
        endObject();
    }

    public void writeCommandCell(UrlCommandCell cmd) throws IOException
    {
        if (!cmd.isCacheable()) {
            writeAttribute("cache", cmd.isCacheable(), true);
        }
        if (cmd.getUrl() != null) {
            writeAttribute("url", cmd.getUrl(), false);
        }
        else
        {
            if (cmd.getCmd() == null)
            {
                throw new IllegalStateException("Value and URL cannot both be null on command cell, n-cube: " + name);
            }
            writeAttribute("value", cmd.getCmd(), false);
        }
    }

    /**
     * According to parseJsonValue reading in, if your item is one of the following end types it does not need to
     * specify the end type:  String, Long, Boolean, Double.  These items will all be picked up automatically
     * so to save on those types I don't write out the type.
     * @param type Type to write, if null don't write anything because its axis default type
     */
    public void writeType(String type) throws IOException
    {
        if (type == null) {
            return;
        }

        writeAttribute("type", type, true);
    }

    private static String getColumnType(Object o)
    {
        if (o instanceof Range || o instanceof RangeSet) {
            return null;
        }

        if (o instanceof LatLon) {
            return "latlon";
        }

        if (o instanceof Point2D) {
            return "point2d";
        }

        if (o instanceof Point3D) {
            return "point3d";
        }

        return getCellType(o, "column");
    }

    static String getCellType(Object cell, String type)
    {
        if (cell == null || (cell instanceof String) || (cell instanceof Double) || (cell instanceof Long) || (cell instanceof Boolean)) {
            return null;
        }

        if (cell instanceof BigDecimal) {
            return "bigdec";
        }

        if (cell instanceof Float) {
            return "float";
        }

        if (cell instanceof Integer) {
            return "int";
        }

        if (cell instanceof BigInteger) {
            return "bigint";
        }

        if (cell instanceof Byte) {
            return "byte";
        }

        if (cell instanceof Short) {
            return "short";
        }

        if (cell instanceof Date) {
            return "date";
        }

        if (cell instanceof BinaryUrlCmd || cell instanceof byte[]) {
            return "binary";
        }

        if (cell instanceof GroovyExpression || cell instanceof Collection || cell.getClass().isArray()) {
            return "exp";
        }

        if (cell instanceof GroovyMethod) {
            return "method";
        }

        if (cell instanceof GroovyTemplate) {
            return "template";
        }

        if (cell instanceof StringUrlCmd) {
            return "string";
        }

        throw new IllegalArgumentException(String.format("Unsupported %s Type:  %s", cell.getClass().getName(), type));
    }

    public void writeCells(Map<Set<Column>, ?> cells) throws IOException
    {
        builder.append("\"cells\":");
        if (cells == null || cells.isEmpty()) {
            builder.append("[]");
            return;
        }
        startArray();
        for (Map.Entry<Set<Column>, ?> cell : cells.entrySet())
        {
            startObject();
            writeIds(cell);
            writeType(getCellType(cell.getValue(), "cell"));

            if ((cell.getValue() instanceof UrlCommandCell)) {
                writeCommandCell((UrlCommandCell)cell.getValue());
            } else {
                writeValue("value", cell.getValue());
            }
            endObject();
            comma();
        }
        uncomma();
        endArray();
    }


    public void writeIds(Map.Entry<Set<Column>, ?> item) throws IOException
    {
        builder.append("\"id\":");
        startArray();

        boolean commaWritten = false;

        for (Column c : item.getKey())
        {
            if (!c.isDefault())
            {
                commaWritten = true;
                writeIdValue(c.getId(), true);
            }
        }

        if (commaWritten)
        {
            uncomma();
        }
        endArray();
        comma();
    }

    private void uncomma()
    {
        builder.setLength(builder.length() - 1);
    }

    void writeGroovyObject(Object o)
    {
        if (o instanceof String)
        {
            builder.append("'");
            builder.append(o.toString());
            builder.append("'");
        }
        else if (o instanceof GroovyExpression)
        {
            builder.append("'");
            builder.append(((GroovyExpression) o).getCmd());
            builder.append("'");
        }
        else if (o instanceof Boolean)
        {
            builder.append((Boolean) o ? "true" : "false");
        }
        else if (o instanceof Double)
        {
            builder.append(String.format("%f", (Double) o));
            builder.append('d');
        }
        else if (o instanceof Integer)
        {
            builder.append(o);
            builder.append('i');
        }
        else if (o instanceof Long)
        {
            builder.append(o);
            builder.append('L');
        }
        else if (o instanceof BigDecimal)
        {
            builder.append(((BigDecimal) o).toPlainString());
            builder.append('g');
        }
        else if (o instanceof BigInteger)
        {
            builder.append(o);
            builder.append('g');
        }
        else if (o instanceof Byte)
        {
            builder.append(o);
            builder.append("as Byte");
        }
        else if (o instanceof Float)
        {
            builder.append(String.format("%f", (Float) o));
            builder.append('f');
        }
        else if (o instanceof Short)
        {
            builder.append(o);
            builder.append("as Short");
        }
        else
        {
            throw new IllegalArgumentException("Unknown Groovy Type : " + o.getClass());
        }
    }

    public void writeObject(Object o) throws IOException
    {
        if (o == null)
        {
            builder.append("null");
        }
        else if (o instanceof String)
        {
            StringWriter w = new StringWriter();
            JsonWriter.writeJsonUtf8String(o.toString(), w);
            builder.append(w.toString());
        }
        else if (o instanceof Date)
        {
            builder.append(String.format(quotedStringFormat, dateFormat.format(o)));
        }
        else if (o instanceof LatLon)
        {
            LatLon l = (LatLon)o;
            builder.append(String.format("\"%f,%f\"", l.getLat(), l.getLon()));
        }
        else if (o instanceof Point2D)
        {
            Point2D l = (Point2D)o;
            String twoDoubleFormat = "\"%f,%f\"";
            builder.append(String.format(twoDoubleFormat, l.getX(), l.getY()));
        }
        else if (o instanceof Point3D)
        {
            Point3D p = (Point3D)o;
            builder.append(String.format("\"%f,%f,%f\"", p.getX(), p.getY(), p.getZ()));
        }
        else if (o instanceof Range)
        {
            Range r = (Range)o;
            startArray();
            writeObject(r.getLow());
            comma();
            writeObject(r.getHigh());
            endArray();
        }
        else if (o instanceof RangeSet)
        {
            RangeSet r = (RangeSet)o;
            Iterator i = r.iterator();
            startArray();
            while (i.hasNext()) {
                writeObject(i.next());
                comma();
            }
            uncomma();
            endArray();
        }
        else if (o instanceof byte[])
        {
            builder.append(String.format(quotedStringFormat, StringUtilities.encode((byte[]) o)));
        }
        else if (o.getClass().isArray())
        {
            builder.append("\"");
            startArray();
            int len = Array.getLength(o);
            for (int i=0; i< len; i++) {
                writeGroovyObject(Array.get(o, i));
                comma();
            }
            uncomma();
            endArray();
            builder.append(" as Object[]");
            builder.append("\"");
        }
        else
        {
            builder.append(o.toString());
        }
    }

    public void writeValue(String attr, Object o) throws IOException
    {
        builder.append(String.format(quotedStringFormat, attr));
        builder.append(':');
        writeObject(o);
    }

    public void writeId(Long longId, boolean addComma) throws IOException
    {
        builder.append(String.format(quotedStringFormat, "id"));
        builder.append(':');
        writeIdValue(longId, addComma);
    }

    public void writeIdValue(Long longId, boolean addComma) throws IOException
    {
        Object userId = userIds.get(longId);

        if (userId != null) {
            writeObject(userId);
        } else {
            Long generatedId = generatedIds.get(longId);

            if (generatedId == null) {
                generatedId = ++idCounter;
                generatedIds.put(longId, generatedId);
            }

            builder.append(generatedId);
            builder.append(".0");
        }

        if (addComma) {
            comma();
        }
    }

    public void writeAttribute(String attr, Object value, boolean includeComma) throws IOException
    {
        if (value instanceof String)
        {
            StringWriter w = new StringWriter();
            JsonWriter.writeJsonUtf8String((String)value, w);
            value = w.toString();
        }
        builder.append(String.format(quotedStringFormat, attr));
        builder.append(":");
        builder.append(value.toString());
        if (includeComma)
        {
            builder.append(",");
        }
    }
}
TOP

Related Classes of com.cedarsoftware.ncube.formatters.JsonFormatter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.