clearfacts.cds.JsonDataSource.java Source code

Java tutorial

Introduction

Here is the source code for clearfacts.cds.JsonDataSource.java

Source

/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2011 Jaspersoft Corporation. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
 */
//package net.sf.jasperreports.engine.data;
package clearfacts.cds;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.data.JRAbstractTextDataSource;
import net.sf.jasperreports.engine.JRRewindableDataSource;
import net.sf.jasperreports.engine.JasperReportsContext;
//import net.sf.jasperreports.engine.util.JsonUtil;
import net.sf.jasperreports.repo.RepositoryUtil;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;

/**
 * JSON data source implementation
 * 
 * @author Narcis Marcu (narcism@users.sourceforge.net)
 * @version $Id$
 */
public class JsonDataSource extends JRAbstractTextDataSource implements JRRewindableDataSource {

    // the JSON select expression that gives the nodes to iterate
    private String selectExpression;

    private Iterator<JsonNode> jsonNodesIterator;

    // the current node
    private JsonNode currentJsonNode;

    private final String PROPERTY_SEPARATOR = ".";

    private final String ARRAY_LEFT = "[";

    private final String ARRAY_RIGHT = "]";

    private final String ATTRIBUTE_LEFT = "(";

    private final String ATTRIBUTE_RIGHT = ")";

    // the JSON tree as it is obtained from the JSON source
    private JsonNode jsonTree;

    private ObjectMapper mapper;

    private InputStream jsonStream;

    private boolean toClose;

    public JsonDataSource(InputStream stream) throws JRException {
        this(stream, null);
    }

    public JsonDataSource(InputStream jsonStream, String selectExpression) throws JRException {
        try {
            this.jsonStream = jsonStream;
            this.mapper = new ObjectMapper();

            mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
            mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
            mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);

            this.jsonTree = mapper.readTree(jsonStream);
            this.selectExpression = selectExpression;

            moveFirst();
        } catch (JsonProcessingException e) {
            throw new JRException(e);
        } catch (IOException e) {
            throw new JRException(e);
        }
    }

    public JsonDataSource(File file) throws FileNotFoundException, JRException {
        this(file, null);
    }

    public JsonDataSource(File file, String selectExpression) throws FileNotFoundException, JRException {
        this(new FileInputStream(file), selectExpression);

        toClose = true;
    }

    /**
     * Creates a data source instance that reads JSON data from a given location
     * @param jasperReportsContext the JasperReportsContext
     * @param location a String representing JSON data source
     * @param selectExpression a String representing the select expression
     */
    public JsonDataSource(JasperReportsContext jasperReportsContext, String location, String selectExpression)
            throws JRException {
        this(RepositoryUtil.getInstance(jasperReportsContext).getInputStreamFromLocation(location),
                selectExpression);

        toClose = true;
    }

    /**
     * @see #JsonDataSource(JasperReportsContext, String, String)
     */
    public JsonDataSource(String location, String selectExpression) throws JRException {
        this(DefaultJasperReportsContext.getInstance(), location, selectExpression);
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.sf.jasperreports.engine.JRRewindableDataSource#moveFirst()
     */
    public void moveFirst() throws JRException {
        if (jsonTree == null || jsonTree.isMissingNode()) {
            throw new JRException("No JSON data to operate on!");
        }

        currentJsonNode = null;
        JsonNode result = getJsonData(jsonTree, selectExpression);
        if (result != null && result.isObject()) {
            final List<JsonNode> list = new ArrayList<JsonNode>();
            list.add(result);
            jsonNodesIterator = new Iterator<JsonNode>() {
                private int count = -1;

                public void remove() {
                    list.remove(count);
                }

                public JsonNode next() {
                    count++;
                    return list.get(count);
                }

                public boolean hasNext() {
                    return count < list.size() - 1;
                }
            };
        } else if (result != null && result.isArray()) {
            jsonNodesIterator = result.elements();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.sf.jasperreports.engine.JRDataSource#next()
     */
    public boolean next() {
        if (jsonNodesIterator == null || !jsonNodesIterator.hasNext()) {
            return false;
        }
        currentJsonNode = jsonNodesIterator.next();
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.sf.jasperreports.engine.JRDataSource#getFieldValue(net.sf.jasperreports.engine.JRField)
     */
    public Object getFieldValue(JRField jrField) throws JRException {
        if (currentJsonNode == null) {
            return null;
        }
        String expression = jrField.getDescription();
        if (expression == null || expression.length() == 0) {
            return null;
        }
        Object value = null;

        Class<?> valueClass = jrField.getValueClass();
        JsonNode selectedObject = getJsonData(currentJsonNode, expression);
        if (Object.class != valueClass) {
            if (selectedObject != null) {
                try {
                    if (valueClass.equals(String.class)) {
                        value = selectedObject.asText();

                    } else if (valueClass.equals(Boolean.class)) {
                        value = selectedObject.booleanValue();

                    } else if (Number.class.isAssignableFrom(valueClass)) {
                        value = convertStringValue(selectedObject.asText(), valueClass);

                    } else if (Date.class.isAssignableFrom(valueClass)) {
                        value = convertStringValue(selectedObject.asText(), valueClass);

                    } else {
                        throw new JRException("Field '" + jrField.getName() + "' is of class '"
                                + valueClass.getName() + "' and cannot be converted");
                    }
                } catch (Exception e) {
                    throw new JRException("Unable to get value for field '" + jrField.getName() + "' of class '"
                            + valueClass.getName() + "'", e);
                }
            }
        } else {
            value = selectedObject;
        }

        return value;
    }

    /**
     * Extracts the JSON nodes based on the query expression
     * 
     * @param rootNode
     * @param jsonExpression
     * @throws JRException
     */
    protected JsonNode getJsonData(JsonNode rootNode, String jsonExpression) throws JRException {
        if (jsonExpression == null || jsonExpression.length() == 0) {
            return rootNode;
        }
        JsonNode tempNode = rootNode;
        StringTokenizer tokenizer = new StringTokenizer(jsonExpression, PROPERTY_SEPARATOR);

        while (tokenizer.hasMoreTokens()) {
            String currentToken = tokenizer.nextToken();
            int currentTokenLength = currentToken.length();
            int indexOfLeftSquareBracket = currentToken.indexOf(ARRAY_LEFT);

            // got Left Square Bracket - LSB
            if (indexOfLeftSquareBracket != -1) {
                // a Right Square Bracket must be the last character in the current token
                if (currentToken.lastIndexOf(ARRAY_RIGHT) != (currentTokenLength - 1)) {
                    throw new JRException("Invalid expression: " + jsonExpression + "; current token "
                            + currentToken + " not ended properly");
                }

                // LSB not first character
                if (indexOfLeftSquareBracket > 0) {
                    // extract nodes at property
                    String property = currentToken.substring(0, indexOfLeftSquareBracket);
                    tempNode = goDownPathWithAttribute(tempNode, property);

                    String arrayOperators = currentToken.substring(indexOfLeftSquareBracket);
                    StringTokenizer arrayOpsTokenizer = new StringTokenizer(arrayOperators, ARRAY_RIGHT);
                    while (arrayOpsTokenizer.hasMoreTokens()) {
                        if (!tempNode.isMissingNode() && tempNode.isArray()) {
                            String currentArrayOperator = arrayOpsTokenizer.nextToken();
                            tempNode = tempNode.path(Integer.parseInt(currentArrayOperator.substring(1)));
                        }
                    }
                } else { // LSB first character
                    String arrayOperators = currentToken.substring(indexOfLeftSquareBracket);
                    StringTokenizer arrayOpsTokenizer = new StringTokenizer(arrayOperators, ARRAY_RIGHT);
                    while (arrayOpsTokenizer.hasMoreTokens()) {
                        if (!tempNode.isMissingNode() && tempNode.isArray()) {
                            String currentArrayOperator = arrayOpsTokenizer.nextToken();
                            tempNode = tempNode.path(Integer.parseInt(currentArrayOperator.substring(1)));
                        }
                    }
                }
            } else {
                tempNode = goDownPathWithAttribute(tempNode, currentToken);
            }
        }

        return tempNode;
    }

    /**
     * Extracts the JSON nodes that match the attribute expression
     * 
     * @param rootNode
     * @param pathWithAttributeExpression : e.g. Orders(CustomerId == HILAA)
     * @throws JRException
     */
    protected JsonNode goDownPathWithAttribute(JsonNode rootNode, String pathWithAttributeExpression)
            throws JRException {
        // check if path has attribute selector
        int indexOfLeftRoundBracket = pathWithAttributeExpression.indexOf(ATTRIBUTE_LEFT);
        if (indexOfLeftRoundBracket != -1) {

            // a Right Round Bracket must be the last character in the current pathWithAttribute
            if (pathWithAttributeExpression
                    .indexOf(ATTRIBUTE_RIGHT) != (pathWithAttributeExpression.length() - 1)) {
                throw new JRException("Invalid attribute selection expression: " + pathWithAttributeExpression);
            }

            if (rootNode != null && !rootNode.isMissingNode()) {

                String path = pathWithAttributeExpression.substring(0, indexOfLeftRoundBracket);

                // an expression in a form like: attribute==value
                String attributeExpression = pathWithAttributeExpression.substring(indexOfLeftRoundBracket + 1,
                        pathWithAttributeExpression.length() - 1);

                JsonNode result = null;
                if (rootNode.isObject()) {
                    // select only those nodes for which the attribute expression applies
                    if (!rootNode.path(path).isMissingNode()) {
                        if (rootNode.path(path).isObject()) {
                            if (isValidExpression(rootNode.path(path), attributeExpression)) {
                                result = rootNode.path(path);
                            }
                        } else if (rootNode.path(path).isArray()) {
                            result = mapper.createArrayNode();
                            for (JsonNode node : rootNode.path(path)) {
                                if (isValidExpression(node, attributeExpression)) {
                                    ((ArrayNode) result).add(node);
                                }
                            }
                        }
                    }
                } else if (rootNode.isArray()) {
                    result = mapper.createArrayNode();
                    for (JsonNode node : rootNode) {
                        JsonNode deeperNode = node.path(path);
                        if (!deeperNode.isMissingNode() && isValidExpression(deeperNode, attributeExpression)) {
                            ((ArrayNode) result).add(deeperNode);
                        }
                    }
                }
                return result;
            }

        } else { // path has no attribute selectors
            return goDownPath(rootNode, pathWithAttributeExpression);
        }
        return rootNode;
    }

    /**
     * Extracts the JSON nodes under the simple path
     * 
     * @param rootNode
     * @param simplePath - a simple field name, with no selection by attribute
     */
    protected JsonNode goDownPath(JsonNode rootNode, String simplePath) {
        if (rootNode != null && !rootNode.isMissingNode()) {
            JsonNode result = null;
            if (rootNode.isObject()) {
                result = rootNode.path(simplePath);
            } else if (rootNode.isArray()) {
                result = mapper.createArrayNode();
                for (JsonNode node : rootNode) {
                    JsonNode deeperNode = node.path(simplePath);
                    if (!deeperNode.isMissingNode()) {
                        ((ArrayNode) result).add(deeperNode);
                    }
                }
            }
            return result;
        }
        return rootNode;
    }

    /**
     * Validates an attribute expression on a JsonNode
     * 
     * @param operand
     * @param attributeExpression
     * @throws JRException
     */
    protected boolean isValidExpression(JsonNode operand, String attributeExpression) throws JRException {
        return JsonUtil.evaluateJsonExpression(operand, attributeExpression);
    }

    public static void main(String[] args) throws Exception {
    }

    public void close() {
        if (toClose) {
            try {
                jsonStream.close();
            } catch (Exception e) {
                //nothing to do
            }
        }
    }

}