org.obiba.onyx.print.impl.DefaultPdfTemplateEngine.java Source code

Java tutorial

Introduction

Here is the source code for org.obiba.onyx.print.impl.DefaultPdfTemplateEngine.java

Source

/*******************************************************************************
 * Copyright 2008(c) The OBiBa Consortium. All rights reserved.
 * 
 * This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package org.obiba.onyx.print.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.obiba.magma.Datasource;
import org.obiba.magma.MagmaEngine;
import org.obiba.magma.NoSuchVariableException;
import org.obiba.magma.Value;
import org.obiba.magma.ValueSequence;
import org.obiba.magma.ValueSet;
import org.obiba.magma.ValueTable;
import org.obiba.magma.VariableEntity;
import org.obiba.magma.VariableValueSource;
import org.obiba.magma.support.MagmaEngineVariableResolver;
import org.obiba.magma.support.VariableEntityBean;
import org.obiba.magma.type.DateTimeType;
import org.obiba.onyx.core.domain.participant.Participant;
import org.obiba.onyx.core.io.support.LocalizedResourceLoader;
import org.obiba.onyx.core.service.ActiveInterviewService;
import org.obiba.onyx.print.PdfTemplateEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;

import com.lowagie.text.pdf.AcroFields;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;

public class DefaultPdfTemplateEngine implements PdfTemplateEngine {

    private static final Logger log = LoggerFactory.getLogger(PdfTemplateReport.class);

    private static final String PARTICIPANT_ENTITY_TYPE = "Participant";

    private static final Pattern onyxPattern = Pattern.compile("^Onyx\\.");

    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    private ActiveInterviewService activeInterviewService;

    public void setActiveInterviewService(ActiveInterviewService activeInterviewService) {
        this.activeInterviewService = activeInterviewService;
    }

    public InputStream applyTemplate(Locale locale, Map<String, String> fieldToVariableMap,
            LocalizedResourceLoader reportTemplateLoader, ActiveInterviewService activeInterviewService) {

        // Get report template
        Resource resource = reportTemplateLoader.getLocalizedResource(locale);

        // Read report template
        PdfReader pdfReader;
        try {
            pdfReader = new PdfReader(resource.getInputStream());
        } catch (Exception ex) {
            throw new RuntimeException("Report to participant template cannot be read", ex);
        }

        ByteArrayOutputStream output = new ByteArrayOutputStream();
        PdfStamper stamper = null;

        // Set the values in the report data fields
        try {
            stamper = new PdfStamper(pdfReader, output);
            stamper.setFormFlattening(true);

            AcroFields form = stamper.getAcroFields();
            Participant participant = activeInterviewService.getParticipant();

            setVariableDataFields(participant, form, fieldToVariableMap);
            setAdditionalDataFields(form);

        } catch (Exception ex) {
            throw new RuntimeException("An error occured while preparing the report to participant", ex);
        } finally {
            try {
                stamper.close();
            } catch (Exception e) {
                log.warn("Could not close PdfStamper", e);
            }
            try {
                output.close();
            } catch (IOException e) {
                log.warn("Could not close OutputStream", e);
            }
            pdfReader.close();
        }

        return new ByteArrayInputStream(output.toByteArray());
    }

    private void setAdditionalDataFields(AcroFields form) {
        try {
            form.setField("DateInterview\\.date", dateFormat.format(new Date()));
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private void setVariableDataFields(Participant participant, AcroFields form,
            Map<String, String> fieldToVariableMap) {

        HashMap<String, String> fieldList = form.getFields();

        try {
            // Iterate on each field of pdf template
            for (Entry<String, String> field : fieldList.entrySet()) {
                String[] keys = splitData(field.getKey());

                // TEMPORARY FIX so that "NA" is displayed in a field if the data is not available.
                // This should be replaced by a default value for the field in the PDF template,
                // however this didn't seem to work when I tested it (default values were not getting printed)
                // We need to find a way to fix that (might be a bug in Acrobat forms).
                form.setField(field.getKey(), "N/A");

                // Iterate on each key for one field of pdf template (for example when a variable depends on several
                // instruments)
                for (String variableKey : keys) {
                    String variablePath = fieldToVariableMap.get(variableKey);

                    if (variablePath != null) {
                        String[] pathElements = extractPathElements(variablePath);

                        ValueTable tableForVariable = resolveTable(pathElements[0], pathElements[1]);
                        ValueSet valueSetInTable = getParticipantValueSet(tableForVariable);
                        String variableName = pathElements[2];

                        String valueString = getStringValue(tableForVariable, valueSetInTable, field.getKey(),
                                variableName);
                        if (valueString != null && valueString.length() != 0) {
                            form.setField(field.getKey(), valueString);
                            break;
                        }
                    }
                }
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private String getStringValue(ValueTable valueTable, ValueSet valueSet, String fieldName, String variablePath) {
        try {
            VariableValueSource variableValueSource = valueTable
                    .getVariableValueSource(stripOnyxPrefix(variablePath));
            Value value = variableValueSource.getValue(valueSet);
            return getValueAsString(variableValueSource.getVariable(), value);
        } catch (NoSuchVariableException e) {
            log.error("Invalid PDF template definition. Field '{}' is linked to inexistent variable '{}'.",
                    fieldName, variablePath);
            throw e;

        }
    }

    private ValueSet getParticipantValueSet(ValueTable valueTable) {
        VariableEntity entity = new VariableEntityBean("Participant",
                activeInterviewService.getParticipant().getBarcode());
        return valueTable.getValueSet(entity);
    }

    private String stripOnyxPrefix(String variablePath) {
        Assert.notNull(variablePath, "variablePath must not be null.");
        Matcher matcher = onyxPattern.matcher(variablePath);
        return matcher.replaceAll("");
    }

    private String getValueAsString(org.obiba.magma.Variable variable, Value value) {
        if (value == null || value.isNull())
            return "";

        String valueString = "";
        if (value.isSequence()) {
            ValueSequence valueSequence = value.asSequence();
            for (Value v : valueSequence.getValues()) {
                valueString += " " + getValueAsString(variable, v);
            }
        }
        if (value.getValueType() == DateTimeType.get()) {
            valueString = dateFormat.format(value.getValue());
        } else {
            valueString = value.toString();

            if (variable != null && variable.getUnit() != null)
                valueString += " " + variable.getUnit();
        }
        return valueString;
    }

    private String[] splitData(String string) {

        // By default, a field name in a PDF form starts with "form[0].page[0].", concatenated with the custom name that we
        // gave to this field
        String pattern = "([a-zA-Z0-9]+\\[+[0-9]+\\]+\\.){2}";

        // Delete "form[0].page[0]." string to keep only the custom name given to the variable
        String variable = string.replaceFirst(pattern, "");
        variable = variable.replaceAll("\\[[0-9]\\]", "");

        // If multiple instrument types are used for the same field, they are separated by "-"
        String[] list = variable.split("-");
        return list;
    }

    public void setDateFormat(String dateFormat) {
        this.dateFormat = new SimpleDateFormat(dateFormat);
    }

    private ValueTable resolveTable(String datasourceName, String tableName) {
        if (tableName != null) {
            for (Datasource datasource : MagmaEngine.get().getDatasources()) {
                if (datasourceName == null || datasource.getName().equals(datasourceName)) {
                    for (ValueTable table : datasource.getValueTables()) {
                        if (table.isForEntityType(PARTICIPANT_ENTITY_TYPE) && table.getName().equals(tableName)) {
                            return table;
                        }
                    }
                }
            }
            throw new IllegalStateException("Could not resolve ValueTable (name: " + tableName + ")");
        }
        throw new IllegalStateException("Could not resolve ValueTable (tableName is null)");
    }

    private String[] extractPathElements(String variablePath) {
        String[] pathElements = new String[3];

        MagmaEngineVariableResolver resolver = MagmaEngineVariableResolver.valueOf(variablePath);

        pathElements[0] = resolver.getDatasourceName();
        pathElements[1] = resolver.getTableName();
        pathElements[2] = resolver.getVariableName();

        return pathElements;
    }
}