nl.ctmm.trait.proteomics.qcviewer.utils.ReportPDFExporter.java Source code

Java tutorial

Introduction

Here is the source code for nl.ctmm.trait.proteomics.qcviewer.utils.ReportPDFExporter.java

Source

package nl.ctmm.trait.proteomics.qcviewer.utils;

import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import nl.ctmm.trait.proteomics.qcviewer.input.ReportUnit;

import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.title.TextTitle;

import com.itextpdf.text.BadElementException;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chapter;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;

/**
 * This class exports report unit data in PDF format.
 *
 * @author <a href="mailto:pravin.pawar@nbic.nl">Pravin Pawar</a>
 * @author <a href="mailto:freek.de.bruijn@nbic.nl">Freek de Bruijn</a>
 */
public class ReportPDFExporter {
    /**
     * The logger for this class.
     */
    private static final Logger logger = Logger.getLogger(ReportPDFExporter.class.getName());

    /**
     * PDF File type extension.
     */
    private static final String FILE_TYPE_EXTENSION = ".pdf";

    /**
     * Title of the PDF document.
     */
    private static final String PDF_PAGE_TITLE = "QC Pipeline Report for Msrun %s Created on: %s";

    /**
     * Title of the TIC graph section in PDF document.
     */
    private static final String TIC_GRAPH_SECTION_TITLE = "TIC Graph for Msrun %s";

    /**
     * Title of the TIC chart.
     */
    private static final String TIC_GRAPH_TITLE = "%s    MaxIntensity = %s";

    /**
     * Title of the Metrics values section in PDF document.
     */
    private static final String METRICS_VALUES_SECTION_TITLE = "QC Metrics Values for Msrun %s";

    /**
     * Error message to be shown in case exception occurs while exporting reports in PDF format.
     */
    private static final String PDF_EXPORT_EXCEPTION_MESSAGE = "Failed exporting report units "
            + "to PDF format. (Multiple) exceptions occurred.";

    /**
     * Error message to be written to the logger in case exception occurs while preparing metrics values table.
     */
    private static final String PDF_TABLE_EXCEPTION_MESSAGE = "DocumentException occurred while creating "
            + "table for report unit %s.";

    /**
     * Page margin of the PDF document.
     */
    private static final int PDF_PAGE_MARGIN = 30;

    /**
     * Width of the TIC chart image.
     */
    private static final int CHART_IMAGE_WIDTH = 750;

    /**
     * Height of the TIC chart image.
     */
    private static final int CHART_IMAGE_HEIGHT = 150;

    /**
     * Spacing before the metrics values table in the PDF document.
     */
    private static final int TABLE_SPACING = 5;

    /**
     * Spacing for paragraphs in the PDF document.
     */
    private static final int PDF_PARAGRAPH_SPACING = 10;

    /**
     * Width of columns in metrics values table.
     */
    private static final float[] COLUMN_WIDTHS = new float[] { 80, 210, 80, 80, 210, 80 };

    /**
     * Total number of columns in metrics values table.
     */
    private static final int TOTAL_COLUMNS = 6;

    /**
     * The date format for adding creation date/time to the PDF document.
     */
    private static final DateFormat CREATION_DATE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");

    /**
     * Error message to be written to the logger in case exception occurs while preparing TIC Graph.
     */
    private static final String CLONE_EXCEPTION_MESSAGE = "Clone not supported exception occurred while preparing TIC"
            + " graph for %s.";

    /**
     * Default private constructor.
     */
    private ReportPDFExporter() {
    }

    /**
     * Export a report to a pdf file.
     *
     * @param allMetricsMap         map of all QC metrics - keys and description.
     * @param selectedReports       the report units to be exported in PDF format.
     * @param preferredPDFDirectory the directory the pdf document should be exported to.
     * @return Path of the created PDF document if it is successfully created - otherwise return empty string.
     */
    public static String exportReportUnitInPDFFormat(final Map<String, String> allMetricsMap,
            final ArrayList<ReportUnit> selectedReports, final String preferredPDFDirectory) {
        //Obtain current timestamp. 
        final java.util.Date date = new java.util.Date();
        final String timestampString = CREATION_DATE_TIME_FORMAT.format(date);
        //Replace all occurrences of special character ':' from time stamp since ':' is not allowed in filename. 
        final String filenameTimestamp = timestampString.replace(':', '-');
        //Instantiation of document object - landscape format using the rotate() method
        final Document document = new Document(PageSize.A4.rotate(), PDF_PAGE_MARGIN, PDF_PAGE_MARGIN,
                PDF_PAGE_MARGIN, PDF_PAGE_MARGIN);
        final String pdfFileName = preferredPDFDirectory + "\\QCReports-" + filenameTimestamp + FILE_TYPE_EXTENSION;
        try {
            //Creation of PdfWriter object
            //            PdfWriter writer;
            //            writer = PdfWriter.getInstance(document, new FileOutputStream(pdfFileName));
            document.open();
            for (ReportUnit reportUnit : selectedReports) {
                //New page for each report. 
                document.newPage();
                //Creation of chapter object
                final Paragraph pageTitle = new Paragraph(
                        String.format(PDF_PAGE_TITLE, reportUnit.getMsrunName(), timestampString),
                        Constants.PDF_TITLE_FONT);
                pageTitle.setAlignment(Element.ALIGN_CENTER);
                final Chapter chapter1 = new Chapter(pageTitle, 1);
                chapter1.setNumberDepth(0);
                //Creation of TIC graph section object
                final String graphTitle = String.format(TIC_GRAPH_SECTION_TITLE, reportUnit.getMsrunName());
                final Paragraph ticGraphSection = new Paragraph(graphTitle, Constants.PDF_SECTION_FONT);
                ticGraphSection.setSpacingBefore(PDF_PARAGRAPH_SPACING);
                ticGraphSection.add(Chunk.NEWLINE);
                //Insert TIC Graph in ticGraphSection.
                ticGraphSection.add(createTICChartImage(reportUnit));
                chapter1.addSection(ticGraphSection);
                final String metricsTitle = String.format(METRICS_VALUES_SECTION_TITLE, reportUnit.getMsrunName());
                final Paragraph metricsValuesSection = new Paragraph(metricsTitle, Constants.PDF_SECTION_FONT);
                metricsValuesSection.setSpacingBefore(PDF_PARAGRAPH_SPACING);
                //Reference: http://www.java-connect.com/itext/add-table-in-PDF-document-using-java-iText-library.html
                //TODO: Insert metrics values table in metricsValuesSection
                metricsValuesSection.add(createMetricsValuesTable(allMetricsMap, reportUnit));
                chapter1.addSection(metricsValuesSection);
                //Addition of a chapter to the main document
                document.add(chapter1);
            }
            document.close();
            return pdfFileName;
        } catch (final DocumentException e) {
            // TODO Explain when these exception can occur.
            /* FileNotFoundException will be thrown by the FileInputStream, FileOutputStream, and 
             * RandomAccessFile constructors when a file with the specified path name does not exist.
             * It will also be thrown by these constructors if the file does exist but for some reason 
             * is inaccessible, for example when an attempt is made to open a read-only file for writing.
             * DocumentException Signals that an error has occurred in a Document.
             */
            logger.log(Level.SEVERE, PDF_EXPORT_EXCEPTION_MESSAGE, e);
            return "";
        }
    }

    /**
     * Create image of the TIC Chart.
     *
     * @param reportUnit Report unit for which to create TIC chart image.
     * @return TIC Chart image.
     */
    private static Image createTICChartImage(final ReportUnit reportUnit) {
        /*Reference: http://vangjee.wordpress.com/2010/11/03/how-to-use-and-not-use-itext-and-jfreechart/
         * Apache License, Version 2.0
         */
        JFreeChart ticChart = null;
        try {
            ticChart = (JFreeChart) reportUnit.getChartUnit().getTicChart().clone();
        } catch (final CloneNotSupportedException e) {
            logger.log(Level.SEVERE, String.format(CLONE_EXCEPTION_MESSAGE, reportUnit.getMsrunName()), e);
        }
        final String titleString = String.format(TIC_GRAPH_TITLE, reportUnit.getMsrunName(),
                reportUnit.getChartUnit().getMaxTicIntensityString());
        final TextTitle newTitle = new TextTitle(titleString, Constants.PDF_CHART_TITLE_FONT);
        newTitle.setPaint(Color.red);
        if (ticChart != null) {
            ticChart.setTitle(newTitle);
        }
        Image chartImage = null;
        try {
            final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            if (ticChart != null) {
                ChartUtilities.writeChartAsPNG(byteArrayOutputStream, ticChart, CHART_IMAGE_WIDTH,
                        CHART_IMAGE_HEIGHT, new ChartRenderingInfo());
            }
            chartImage = Image.getInstance(byteArrayOutputStream.toByteArray());
            byteArrayOutputStream.close();
        } catch (final BadElementException | IOException e) {
            // TODO Explain when these exception can occur.
            /*
             * IOException occurs in case PDF file can not be created. 
             * e.g. PDF file with same name exist in the PDF directory and it is open. 
             * In this case, the PDF file can not be overwritten. 
             * BadElementException Signals an attempt to create an Element that hasn't got the right form. 
             */
            logger.log(Level.SEVERE, PDF_EXPORT_EXCEPTION_MESSAGE, e);
        }
        if (chartImage != null) {
            chartImage.setAlignment(Element.ALIGN_CENTER);
        }
        return chartImage;
    }

    /**
     * Create the metrics values table for given report unit. This table will be added to the PDF document.
     *
     * @param allMetricsMap map of all QC metrics - keys and description.
     * @param reportUnit    Report unit for which to create the metrics values table.
     * @return PDF table containing metrics values of the report unit.
     */
    private static PdfPTable createMetricsValuesTable(final Map<String, String> allMetricsMap,
            final ReportUnit reportUnit) {
        /*
         * TODO: Column size, font size and spacing of the metrics value table. 
         */
        // Create columns names.
        final String columnNames[] = { Constants.METRICS_ID_COLUMN_NAME, Constants.DESCRIPTION_COLUMN_NAME,
                Constants.VALUE_COLUMN_NAME, Constants.METRICS_ID_COLUMN_NAME, Constants.DESCRIPTION_COLUMN_NAME,
                Constants.VALUE_COLUMN_NAME, };
        //Creation of table object.
        final PdfPTable table = new PdfPTable(columnNames.length);
        try {
            table.setSpacingBefore(TABLE_SPACING);
            //Set the table width. 
            table.setTotalWidth(COLUMN_WIDTHS);
            table.setLockedWidth(true);
            //Add table header. 
            for (int i = 0; i < TOTAL_COLUMNS; ++i) {
                final PdfPCell headerCell = new PdfPCell(new Phrase(columnNames[i], Constants.TABLE_HEADER_FONT));
                headerCell.setBackgroundColor(BaseColor.RED);
                headerCell.setHorizontalAlignment(Element.ALIGN_CENTER);
                table.addCell(headerCell);
            }
            //Read metricsValues corresponding to reportUnit.
            final Map<String, String> metricsValues = reportUnit.getMetricsValues();
            //TODO: Split allMetricsMap in two parts such that sorted rows are properly added in the table.
            //Get all keys  
            final Object[] keyArray = allMetricsMap.keySet().toArray();
            //get all values
            final Object[] valueArray = allMetricsMap.values().toArray();
            //Calculate halfSize
            final int halfSize = keyArray.length / 2;
            for (int i = 0; i < halfSize; ++i) {
                addMetric(keyArray[i].toString(), valueArray[i].toString(), metricsValues, table);
                addMetric(keyArray[i + halfSize].toString(), valueArray[i + halfSize].toString(), metricsValues,
                        table);
            }
        } catch (final DocumentException e) {
            //DocumentException signals that an error has occurred in a Document.
            logger.log(Level.SEVERE, String.format(PDF_TABLE_EXCEPTION_MESSAGE, reportUnit.getMsrunName()), e);
        }
        return table;
    }

    /**
     * Add a metric to the pdf table.
     *
     * @param key           the metric key.
     * @param description   the metric description.
     * @param metricsValues the metric values.
     * @param table         the pdf table.
     */
    private static void addMetric(final String key, final String description,
            final Map<String, String> metricsValues, final PdfPTable table) {
        final String value = (metricsValues != null) ? metricsValues.get(key) : Constants.NOT_AVAILABLE_STRING;
        // Populate content in table cell.
        table.addCell(new Phrase(key, Constants.TABLE_CONTENT_FONT));
        table.addCell(new Phrase(description, Constants.TABLE_CONTENT_FONT));
        final PdfPCell valueCell = new PdfPCell(new Phrase(value, Constants.TABLE_CONTENT_FONT));
        valueCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        table.addCell(valueCell);
    }
}