com.opengamma.web.analytics.ViewportResultsJsonCsvWriter.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.web.analytics.ViewportResultsJsonCsvWriter.java

Source

/**
 * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.web.analytics;

import static com.opengamma.web.analytics.formatting.DataType.UNKNOWN;

import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;

import org.json.JSONObject;

import au.com.bytecode.opencsv.CSVWriter;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.calcnode.MissingValue;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.engine.view.AggregatedExecutionLog;
import com.opengamma.engine.view.ExecutionLog;
import com.opengamma.engine.view.ExecutionLogWithContext;
import com.opengamma.util.log.LogEvent;
import com.opengamma.util.log.LogLevel;
import com.opengamma.web.analytics.formatting.DataType;
import com.opengamma.web.analytics.formatting.ResultsFormatter;
import com.opengamma.web.analytics.formatting.TypeFormatter;
import com.opengamma.web.server.conversion.DoubleValueOptionalDecimalPlaceFormatter;

/**
 * Creates a JSON/CSV object from an instance of {@link ViewportResults}.
 */
public class ViewportResultsJsonCsvWriter {

    private static final String VERSION = "version";
    private static final String VALUE = "v";
    private static final String HISTORY = "h";
    private static final String TYPE = "t";
    private static final String DATA = "data";
    private static final String ERROR = "error";
    private static final String CALCULATION_DURATION = "calculationDuration";
    private static final String LOG_LEVEL = "logLevel";
    private static final String LOG_OUTPUT = "logOutput";
    private static final String EXCEPTION_CLASS = "exceptionClass";
    private static final String EXCEPTION_MESSAGE = "exceptionMessage";
    private static final String EXCEPTION_STACK_TRACE = "exceptionStackTrace";
    private static final String EVENTS = "events";
    private static final String LEVEL = "level";
    private static final String MESSAGE = "message";
    private static final String FUNCTION_NAME = "functionName";
    private static final String TARGET = "target";

    private final ResultsFormatter _formatter;
    private final DoubleValueOptionalDecimalPlaceFormatter _durationFormatter = new DoubleValueOptionalDecimalPlaceFormatter();

    public ViewportResultsJsonCsvWriter(ResultsFormatter formatter) {
        _formatter = formatter;
    }

    public String getCsv(ViewportResults viewportResults) {
        GridColumnGroups columnGroups = viewportResults.getColumns();
        String[] header1 = new String[columnGroups.getGroups().size()];
        String[] header2 = new String[columnGroups.getColumnCount()];

        int index = 0;
        for (GridColumnGroup gridColumnGroup : columnGroups.getGroups()) {
            header1[index++] = gridColumnGroup.getName();
        }

        List<GridColumn> columns = columnGroups.getColumns();
        index = 0;
        for (GridColumn gridColumn : columns) {
            header2[index++] = gridColumn.getHeader();
        }

        StringWriter stringWriter = new StringWriter();
        @SuppressWarnings("resource")
        CSVWriter csvWriter = new CSVWriter(stringWriter);

        csvWriter.writeNext(header1);
        csvWriter.writeNext(header2);

        List<ResultsCell> viewportCells = viewportResults.getResults();
        Iterable<List<ResultsCell>> results = Iterables.partition(viewportCells, columnGroups.getColumnCount());
        for (List<ResultsCell> row : results) {
            String[] rowArray = new String[row.size()];
            int col = 0;
            for (ResultsCell cell : row) {
                Object cellValue = cell.getValue();
                if (cellValue instanceof RowTarget) {
                    rowArray[col++] = ((RowTarget) cellValue).getName();
                    continue;
                }

                ValueSpecification cellValueSpec = cell.getValueSpecification();
                Object formattedValue = _formatter.format(cellValue, cellValueSpec, cell.getFormat(),
                        cell.getInlineKey());
                if (formattedValue instanceof String) {
                    rowArray[col++] = (String) formattedValue;
                } else {
                    rowArray[col++] = formattedValue.toString();
                }
            }
            csvWriter.writeNext(rowArray);
        }
        return stringWriter.toString();
    }

    // TODO use a Freemarker template - will that perform well enough?
    public String getJson(ViewportResults viewportResults) {
        List<ResultsCell> viewportCells = viewportResults.getResults();
        List<Object> results = Lists.newArrayListWithCapacity(viewportCells.size());
        for (ResultsCell cell : viewportCells) {
            Object cellValue = cell.getValue();
            ValueSpecification cellValueSpec = cell.getValueSpecification();
            Object formattedValue = _formatter.format(cellValue, cellValueSpec, cell.getFormat(),
                    cell.getInlineKey());
            Collection<Object> history = cell.getHistory();
            Class<?> columnType = cell.getType();
            DataType columnFormat = _formatter.getDataType(columnType);
            Map<String, Object> valueMap = Maps.newHashMap();
            AggregatedExecutionLog executionLog = cell.getExecutionLog();
            LogLevel logLevel = maxLogLevel(executionLog);

            valueMap.put(VALUE, formattedValue);
            if (columnFormat == UNKNOWN) {
                // if the the column type isn't known then send the type with the value
                valueMap.put(TYPE, _formatter.getDataTypeForValue(cellValue, cellValueSpec).name());
            }
            if (history != null) {
                List<Object> formattedHistoy = formatHistory(cellValueSpec, cell.getInlineKey(), history);
                if (formattedHistoy != null) {
                    valueMap.put(HISTORY, formattedHistoy);
                }
            }
            if (cell.isError() || isError(formattedValue)) {
                valueMap.put(ERROR, true);
            }
            if (logLevel != null) {
                valueMap.put(LOG_LEVEL, logLevel);
            }
            if (hasLogOutput(executionLog)) {
                valueMap.put(LOG_OUTPUT, formatLogOutput(executionLog));
            }
            results.add(valueMap);
        }
        String duration = _durationFormatter
                .format(new BigDecimal(viewportResults.getCalculationDuration().toMillis()));
        ImmutableMap<String, Object> resultsMap = ImmutableMap.of(VERSION, viewportResults.getVersion(),
                CALCULATION_DURATION, duration, DATA, results);
        return new JSONObject(resultsMap).toString();
    }

    private static boolean isError(Object value) {
        return value instanceof MissingValue;
    }

    private static LogLevel maxLogLevel(AggregatedExecutionLog log) {
        if (log == null) {
            return null;
        }
        EnumSet<LogLevel> logLevels = log.getLogLevels();
        if (logLevels.isEmpty()) {
            return null;
        }
        List<LogLevel> logLevelList = Lists.newArrayList(logLevels);
        Collections.sort(logLevelList);
        return logLevelList.get(logLevelList.size() - 1);
    }

    private static boolean hasLogOutput(AggregatedExecutionLog aggregatedLog) {
        return aggregatedLog != null && aggregatedLog.getLogs() != null && !aggregatedLog.getLogs().isEmpty();
    }

    private static List<Map<String, Object>> formatLogOutput(AggregatedExecutionLog aggregatedLog) {
        List<Map<String, Object>> output = Lists.newArrayList();
        for (ExecutionLogWithContext logWithContext : aggregatedLog.getLogs()) {
            Map<String, Object> logMap = Maps.newHashMap();
            ComputationTargetSpecification target = logWithContext.getTargetSpecification();
            logMap.put(FUNCTION_NAME, logWithContext.getFunctionName());
            logMap.put(TARGET, target.getType() + " " + target.getUniqueId());
            ExecutionLog log = logWithContext.getExecutionLog();
            if (log.hasException()) {
                logMap.put(EXCEPTION_CLASS, log.getExceptionClass());
                logMap.put(EXCEPTION_MESSAGE, log.getExceptionMessage());
                logMap.put(EXCEPTION_STACK_TRACE, log.getExceptionStackTrace());
            }
            List<Map<String, Object>> events = Lists.newArrayList();
            logMap.put(EVENTS, events);
            if (log.getEvents() != null) {
                for (LogEvent logEvent : log.getEvents()) {
                    events.add(ImmutableMap.<String, Object>of(LEVEL, logEvent.getLevel(), MESSAGE,
                            logEvent.getMessage()));
                }
            }
            output.add(logMap);
        }
        return output;
    }

    /**
     * Formats history data as a JSON list.
     *
     * @param cellValueSpec The cell's value specification, can be null
     * @param inlineKey If the cell contains a single value derived from an inlined value, this object is the key
     * to obtain the cell's value from the underlying value.
     *@param history The history values, not null  @return The formatted history
     */
    private List<Object> formatHistory(ValueSpecification cellValueSpec, Object inlineKey,
            Collection<Object> history) {
        List<Object> formattedHistory = Lists.newArrayListWithCapacity(history.size());
        for (Object historyValue : history) {
            Object formattedValue = _formatter.format(historyValue, cellValueSpec, TypeFormatter.Format.HISTORY,
                    inlineKey);
            if (formattedValue != ResultsFormatter.VALUE_UNAVAILABLE) {
                formattedHistory.add(formattedValue);
            }
        }
        // it's possible for an object to have history but not to have any formatted history if it's inlined. some inline
        // keys won't have a value for some inlined results. in that case the cell will have empty history although
        // there is history for the underlying value
        if (formattedHistory.isEmpty()) {
            return null;
        } else {
            return formattedHistory;
        }
    }
}