org.openmrs.module.reporting.report.renderer.DelimitedTextReportRenderer.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.reporting.report.renderer.DelimitedTextReportRenderer.java

Source

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.module.reporting.report.renderer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Cohort;
import org.openmrs.module.reporting.common.ObjectUtil;
import org.openmrs.module.reporting.dataset.DataSet;
import org.openmrs.module.reporting.dataset.DataSetColumn;
import org.openmrs.module.reporting.dataset.DataSetRow;
import org.openmrs.module.reporting.indicator.IndicatorResult;
import org.openmrs.module.reporting.report.ReportData;
import org.openmrs.module.reporting.report.ReportDesign;
import org.openmrs.module.reporting.report.ReportRequest;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * ReportRenderer that renders to a delimited text file
 */
public class DelimitedTextReportRenderer extends ReportDesignRenderer {

    transient protected final Log log = LogFactory.getLog(getClass());

    /**
      * @param design
     * @return the filename extension for the particular type of delimited file
     */
    public String getFilenameExtension(ReportDesign design) {
        return design.getPropertyValue("filenameExtension", "csv");
    }

    /**
      * @param design
     * @return the delimiter that surrounds each column value, if applicable
     */
    public String getTextDelimiter(ReportDesign design) {
        return design.getPropertyValue("textDelimiter", "\"");
    }

    /**
      * @param design
     * @return the delimiter that separates each column value
     */
    public String getFieldDelimiter(ReportDesign design) {
        return design.getPropertyValue("fieldDelimiter", ",");
    }

    /**
     * Defaults to \r\n, per http://tools.ietf.org/html/rfc4180#page-2
     * @param design
     * @return what to end each line with
     */
    public String getLineEnding(ReportDesign design) {
        return design.getPropertyValue("lineDelimiter", "\r\n");
    }

    /**
     * @param design
     * @return character encoding to use when writing the text output
     */
    public String getCharacterEncoding(ReportDesign design) {
        return design.getPropertyValue("characterEncoding", "UTF-8");
    }

    /**
     * @param design
     * @return date format to use when writing dates to the output.  If null, returns toString value
     */
    public DateFormat getDateFormat(ReportDesign design) {
        DateFormat ret = null;
        String df = design.getPropertyValue("dateFormat", "");
        try {
            if (ObjectUtil.notNull(df)) {
                ret = new SimpleDateFormat(df);
            }
        } catch (Exception e) {
            log.warn("Invalid date format specified for report renderer: " + df);
        }
        return ret;
    }

    /**
     * @param design
     * @return regex defining characters that should be replaced by ? in output
     */
    public Pattern getBlacklistRegex(ReportDesign design) {
        String blacklistRegex = design.getPropertyValue("blacklistRegex", null);
        return blacklistRegex == null ? null : Pattern.compile(blacklistRegex);
    }

    /**
    * @see ReportRenderer#getRenderedContentType(org.openmrs.module.reporting.report.ReportRequest)
     * @param request
    */
    public String getRenderedContentType(ReportRequest request) {
        if (request.getReportDefinition().getParameterizable().getDataSetDefinitions().size() > 1) {
            return "application/zip";
        } else {
            ReportDesign design = getDesign(request.getRenderingMode().getArgument());
            return "text/" + getFilenameExtension(design);
        }
    }

    /**
     * Convenience method used to escape a string of text. Replaces " with "" (per the CSV standard), which is not
      * necessarily ideal behavior for other use cases.
     * 
     * @param   text    The text to escape.
     * @return   The escaped text.
     */
    public String escape(String text) {
        if (text == null) {
            return null;
        } else {
            return text.replaceAll("\\\"", "\"\"");
        }
    }

    /**
     * @see ReportRenderer#getFilename(org.openmrs.module.reporting.report.ReportRequest)
     */
    @Override
    public String getFilename(ReportRequest request) {
        String argument = request.getRenderingMode().getArgument();
        ReportDesign design = getDesign(argument);
        String extension = request.getReportDefinition().getParameterizable().getDataSetDefinitions().size() > 1
                ? "zip"
                : getFilenameExtension(design);
        return getFilenameBaseForName(getFilenameBase(request), new HashSet<String>()) + "." + extension;
    }

    /**
     * @param definitionName name to base the returned filename on (e.g. a ReportDefinition name or a data set key)
     * @param alreadyUsed names that have already been used, and should be avoided; the returned value will be added to this set
     * @return definitionName, with characters unsuitable for a filename removed, and a suffix added if necessary for uniqueness
     */
    private String getFilenameBaseForName(String definitionName, Set<String> alreadyUsed) {
        String clean = definitionName.replaceAll("[\\\\/:\"*?<>|#%&{}<>$!'@+`|=]", "");
        if (alreadyUsed.contains(clean)) {
            int i = 2;
            while (alreadyUsed.contains(clean + "_" + i)) {
                ++i;
            }
            clean = clean + "_" + i;
        }
        alreadyUsed.add(clean);
        return clean;
    }

    /**
     * @see ReportRenderer#render(ReportData, String, OutputStream)
     */
    public void render(ReportData results, String argument, OutputStream out)
            throws IOException, RenderingException {
        DataSet dataset = results.getDataSets().values().iterator().next();

        ReportDesign design = getDesign(argument);
        String textDelimiter = getTextDelimiter(design);
        String fieldDelimiter = getFieldDelimiter(design);
        String lineEnding = getLineEnding(design);
        String characterEncoding = getCharacterEncoding(design);
        Pattern blacklistRegex = getBlacklistRegex(design);
        DateFormat dateFormat = getDateFormat(design);

        if (results.getDataSets().size() > 1) {
            ZipOutputStream zip = new ZipOutputStream(out);
            Set<String> usedFilenames = new HashSet<String>();
            for (Map.Entry<String, DataSet> e : results.getDataSets().entrySet()) {
                String fn = getFilenameBaseForName(e.getKey(), usedFilenames) + "."
                        + getFilenameExtension(getDesign(argument));
                zip.putNextEntry(new ZipEntry(fn));
                writeDataSet(e.getValue(), zip, textDelimiter, fieldDelimiter, lineEnding, characterEncoding,
                        blacklistRegex, dateFormat);
                zip.closeEntry();
            }
            zip.finish();
        } else {
            writeDataSet(dataset, out, textDelimiter, fieldDelimiter, lineEnding, characterEncoding, blacklistRegex,
                    dateFormat);
        }
    }

    /**
     * Only visible for testing
     *
     * @param dataset
     * @param out
     * @param textDelimiter
     * @param fieldDelimiter
     * @param lineEnding
     * @param characterEncoding
     * @param blacklist
     * @throws IOException
     */
    void writeDataSet(DataSet dataset, OutputStream out, String textDelimiter, String fieldDelimiter,
            String lineEnding, String characterEncoding, Pattern blacklist, DateFormat dateFormat)
            throws IOException {
        Writer w = new OutputStreamWriter(out, characterEncoding);
        List<DataSetColumn> columns = dataset.getMetaData().getColumns();

        // header row
        for (Iterator<DataSetColumn> i = columns.iterator(); i.hasNext();) {
            DataSetColumn column = i.next();
            w.write(textDelimiter + filterBlacklist(escape(column.getName()), blacklist) + textDelimiter);
            if (i.hasNext()) {
                w.write(fieldDelimiter);
            }
        }
        w.write(lineEnding);

        // data rows
        for (DataSetRow row : dataset) {
            for (Iterator<DataSetColumn> i = columns.iterator(); i.hasNext();) {
                DataSetColumn column = i.next();
                Object colValue = row.getColumnValue(column);
                w.write(textDelimiter);
                if (colValue != null) {
                    String toPrint;
                    if (colValue instanceof Cohort) {
                        toPrint = escape(Integer.toString(((Cohort) colValue).size()));
                    } else if (colValue instanceof IndicatorResult) {
                        toPrint = ((IndicatorResult) colValue).getValue().toString();
                    } else if (dateFormat != null && colValue instanceof Date) {
                        toPrint = dateFormat.format((Date) colValue);
                    } else {
                        // this check is because a logic EmptyResult .toString() -> null
                        String temp = escape(colValue.toString());
                        if (temp != null) {
                            toPrint = temp;
                        } else {
                            toPrint = "";
                        }
                    }
                    w.write(filterBlacklist(toPrint, blacklist));
                }
                w.write(textDelimiter);
                if (i.hasNext()) {
                    w.write(fieldDelimiter);
                }
            }
            w.write(lineEnding);
        }

        w.flush();
    }

    /**
     * @param input
     * @param blacklist
     * @return replaces any matches of blacklist in input with ?
     */
    private String filterBlacklist(String input, Pattern blacklist) {
        if (blacklist == null) {
            return input;
        }
        return blacklist.matcher(input).replaceAll("?");
    }

}