<commit_before>package com.cedarsoftware.ncube.formatters

import com.cedarsoftware.ncube.Axis
import com.cedarsoftware.ncube.CellInfo
import com.cedarsoftware.ncube.Column
import com.cedarsoftware.ncube.CommandCell
import com.cedarsoftware.ncube.NCube
import com.cedarsoftware.ncube.Range
import com.cedarsoftware.ncube.RangeSet
import com.cedarsoftware.ncube.proximity.LatLon
import com.cedarsoftware.ncube.proximity.Point2D
import com.cedarsoftware.ncube.proximity.Point3D
import com.cedarsoftware.util.Converter
import com.cedarsoftware.util.StringUtilities
import com.cedarsoftware.util.io.JsonWriter
import groovy.transform.CompileStatic

import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_APP
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_AXIS_NAME
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_BRANCH
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_CUBE_NAME
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_STATUS
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_TENANT
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_VERSION
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_APP
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_BRANCH
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_CUBE_NAME
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_METHOD_NAME
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_STATUS
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_VERSION

/**
 * Format an NCube into an JSON document
 *
 * @author <NAME> (<EMAIL>), <NAME> (<EMAIL>)
 *         <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.
 */
@CompileStatic
public class JsonFormatter extends BaseJsonFormatter implements NCubeFormatter
{
    private static final String MAX_INT = Long.toString(Integer.MAX_VALUE)

    public JsonFormatter() {}

    public JsonFormatter(OutputStream stream) { super(stream) }

    /**
     * Use this API to generate JSON view of this NCube.
     */
    public String format(NCube ncube, Map<String, Object> options = null)
    {
        if (!(builder instanceof StringWriter))
        {
            throw new IllegalStateException("Builder is not a StringWriter.  Use formatCube(ncube) to write to your stream.")
        }

        formatCube(ncube, options)
        return builder.toString()
    }

    public void formatCube(NCube ncube, Map<String, Object> options)
    {
        if (ncube == null)
        {
            throw new IllegalArgumentException("Cube to format cannot be null")
        }
        if (options == null)
        {
            options = [:]
        }

        String name = ncube.getName()
        try
        {
            startObject()
            writeObjectKeyValue("ncube", name, true)
            Object defCellValue = ncube.getDefaultCellValue()

            if (defCellValue != null)
            {
                String valType = CellInfo.getType(defCellValue, "defaultCell")
                if (valType != null)
                {
                    writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE_TYPE, valType, true)
                }
                if (defCellValue instanceof CommandCell)
                {
                    CommandCell cmd = (CommandCell) defCellValue

                    if (cmd.isCacheable())
                    {   // Only write 'cache' when the cache value is true.  Leave false be default.
                        writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE_CACHE, true, true)
                    }
                    if (cmd.getUrl() != null)
                    {
                        writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE_URL, cmd.getUrl(), true)
                    }
                    else
                    {
                        writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE, cmd.getCmd(), true)
                    }
                }
                else
                {
                    writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE, defCellValue, true)
                }
            }

            if (ncube.getMetaProperties().size() > 0)
            {
                writeMetaProperties(ncube.getMetaProperties())
                comma()
            }
            writeAxes(ncube.getAxes(), options)
            writeCells(ncube.getCellMap() as Map, options)
            endObject()
            closeStream()
        }
        catch (Exception e)
        {
            throw new IllegalStateException(String.format("Unable to format NCube '%s' into JSON", name), e)
        }
    }

    private void writeMetaProperties(Map<String, Object> metaProps) throws IOException
    {
        if (metaProps.size() < 1)
        {
            return
        }

        Iterator<Map.Entry<String, Object>> i = metaProps.entrySet().iterator()
        while (i.hasNext())
        {
            Map.Entry<String, Object> entry = i.next()
            final String key = entry.key
            Object value = entry.value

            if (value instanceof String || value instanceof Boolean || value instanceof Long || value == null)
            {   // Allows for simple key ==> value associations to be written when value is JSON supported type
                writeObjectKeyValue(key, value, false)
            }
            else if (value instanceof Double)
            {   // Doubles are also supported natively in JSON
                writeObjectKey(key)
                append(Converter.convert(value, String.class) as String)
            }
            else if (value instanceof CellInfo)
            {   // If meta-prop value already a cell info, then write it out as-is
                CellInfo cell = (CellInfo) value
                writeObjectKey(key)
                startObject()
                writeType(cell.dataType)
                writeCellValue(cell.recreate())
                endObject()
            }
            else
            {
                // Convert unknown value to CellInfo (handles, all primitives, CommandCells, and Point2D, Point3D, and LatLon)
                CellInfo cell = new CellInfo(value)
                writeObjectKey(key)
                startObject()
                writeType(cell.dataType)
                writeCellValue(cell.recreate())
                endObject()
            }

            if (i.hasNext())
            {
                comma()
            }
        }<commit_msg>updated meta-properties value-side to only use primitive values for String and Boolean.  All others will output CellInfo.<commit_after>package com.cedarsoftware.ncube.formatters

import com.cedarsoftware.ncube.Axis
import com.cedarsoftware.ncube.CellInfo
import com.cedarsoftware.ncube.Column
import com.cedarsoftware.ncube.CommandCell
import com.cedarsoftware.ncube.NCube
import com.cedarsoftware.ncube.Range
import com.cedarsoftware.ncube.RangeSet
import com.cedarsoftware.ncube.proximity.LatLon
import com.cedarsoftware.ncube.proximity.Point2D
import com.cedarsoftware.ncube.proximity.Point3D
import com.cedarsoftware.util.Converter
import com.cedarsoftware.util.StringUtilities
import com.cedarsoftware.util.io.JsonWriter
import groovy.transform.CompileStatic

import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_APP
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_AXIS_NAME
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_BRANCH
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_CUBE_NAME
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_STATUS
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_TENANT
import static com.cedarsoftware.ncube.ReferenceAxisLoader.REF_VERSION
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_APP
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_BRANCH
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_CUBE_NAME
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_METHOD_NAME
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_STATUS
import static com.cedarsoftware.ncube.ReferenceAxisLoader.TRANSFORM_VERSION

/**
 * Format an NCube into an JSON document
 *
 * @author <NAME> (<EMAIL>), <NAME> (<EMAIL>)
 *         <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.
 */
@CompileStatic
public class JsonFormatter extends BaseJsonFormatter implements NCubeFormatter
{
    private static final String MAX_INT = Long.toString(Integer.MAX_VALUE)

    public JsonFormatter() {}

    public JsonFormatter(OutputStream stream) { super(stream) }

    /**
     * Use this API to generate JSON view of this NCube.
     */
    public String format(NCube ncube, Map<String, Object> options = null)
    {
        if (!(builder instanceof StringWriter))
        {
            throw new IllegalStateException("Builder is not a StringWriter.  Use formatCube(ncube) to write to your stream.")
        }

        formatCube(ncube, options)
        return builder.toString()
    }

    public void formatCube(NCube ncube, Map<String, Object> options)
    {
        if (ncube == null)
        {
            throw new IllegalArgumentException("Cube to format cannot be null")
        }
        if (options == null)
        {
            options = [:]
        }

        String name = ncube.getName()
        try
        {
            startObject()
            writeObjectKeyValue("ncube", name, true)
            Object defCellValue = ncube.getDefaultCellValue()

            if (defCellValue != null)
            {
                String valType = CellInfo.getType(defCellValue, "defaultCell")
                if (valType != null)
                {
                    writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE_TYPE, valType, true)
                }
                if (defCellValue instanceof CommandCell)
                {
                    CommandCell cmd = (CommandCell) defCellValue

                    if (cmd.isCacheable())
                    {   // Only write 'cache' when the cache value is true.  Leave false be default.
                        writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE_CACHE, true, true)
                    }
                    if (cmd.getUrl() != null)
                    {
                        writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE_URL, cmd.getUrl(), true)
                    }
                    else
                    {
                        writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE, cmd.getCmd(), true)
                    }
                }
                else
                {
                    writeObjectKeyValue(NCube.DEFAULT_CELL_VALUE, defCellValue, true)
                }
            }

            if (ncube.getMetaProperties().size() > 0)
            {
                writeMetaProperties(ncube.getMetaProperties())
                comma()
            }
            writeAxes(ncube.getAxes(), options)
            writeCells(ncube.getCellMap() as Map, options)
            endObject()
            closeStream()
        }
        catch (Exception e)
        {
            throw new IllegalStateException(String.format("Unable to format NCube '%s' into JSON", name), e)
        }
    }

    private void writeMetaProperties(Map<String, Object> metaProps) throws IOException
    {
        if (metaProps.size() < 1)
        {
            return
        }

        Iterator<Map.Entry<String, Object>> i = metaProps.entrySet().iterator()
        while (i.hasNext())
        {
            Map.Entry<String, Object> entry = i.next()
            final String key = entry.key
            Object value = entry.value

            if (value instanceof String || value instanceof Boolean || value == null)
            {   // Allows for simple key ==> value associations to be written when value is JSON supported type
                writeObjectKeyValue(key, value, false)
            }
            else if (value instanceof CellInfo)
            {   // If meta-prop value already a cell info, then write it out as-is
                CellInfo cell = (CellInfo) value
                writeObjectKey(key)
                startObject()
                writeType(cell.dataType)
                writeCellValue(cell.recreate())
                endObject()
            }
            else
            {
                // Convert unknown value to CellInfo (handles, all primitives, CommandCells, and Point2D, Point3D, and LatLon)
                CellInfo cell = new CellInfo(value)
                writeObjectKey(key)
                startObject()
                writeType(cell.dataType)
                writeCellValue(cell.recreate())
                endObject()
            }

            if (i.hasNext())
            {
                comma()
            }
        }
{'fraction_non_alphanumeric': 0.07783837382250867, 'fraction_numerical': 0.001416530915787237, 'mean_word_length': 2.0378657487091223, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2894226', 'n_tokens_mistral': 3791, 'n_tokens_neox': 3613, 'n_words': 936}