All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mapfish.print.processor.PdfConfigurationProcessor Maven / Gradle / Ivy

There is a newer version: 3.22.0
Show newest version
package org.mapfish.print.processor;

import org.mapfish.print.config.Configuration;
import org.mapfish.print.config.ConfigurationException;
import org.mapfish.print.config.ConfigurationObject;
import org.mapfish.print.config.PDFConfig;
import org.mapfish.print.output.Values;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
 * 

This processor allows the dynamic configuration of the {@link org.mapfish.print.config.PDFConfig} * object by obtaining data from attributes. For example the title and author could be string attributes * posted from the client, this processor would update the {@link org.mapfish.print.config.PDFConfig} object * with the attribute data allowing per report PDF metadata. *

* Note: The {@link org.mapfish.print.config.PDFConfig} can also be configured in the config.yaml either at * the config or template level. *

*

See also: !updatePdfConfigUpdate attribute

* [[examples=updatePdfMetadata]] */ public final class PdfConfigurationProcessor extends AbstractProcessor { private Map updates; /** * Constructor. */ public PdfConfigurationProcessor() { super(Void.class); } /** * The pdf metadata property -> attribute name map. The keys must be one of the values in {@link * org.mapfish.print.config.PDFConfig} and the values must be the name of the attribute to obtain the the * data from. Example Configuration: *

*

     * processors:
     *   - !updatePdfConfig
     *     updates:
     *       title: "titleAttribute"
     *       subject: "subjectAttribute"
     * 
*

* The type of the attribute must be of the correct type, for example title mus be a string, keywords must * be an array of strings, compress must be a boolean. *

* If the value is within the attribute output object then you can use dot separators for each level. For * example suppose there is a custom attribute: myconfig, if and it has a property title then the * configuration would be: *

     * processors:
     *   - updatePdfConfig
     *     updates: {title: :myconfig.title"}
     * 
*

* For more power a "format" can be defined. The format is a printf style format string which will be * called with a single value that is identified by the value key/path. In this case the short hand key: * value can't be used instead it is as follows: *

     *   - updatePdfConfig
     *     updates:
     *       title: !updatePdfConfigUpdate
     *          valueKey: "myconfig.title"
     *          format: "Print Report %s"
     * 
* * @param updates the attribute map */ public void setUpdates(final Map updates) { Map finalUpdatesMap = new HashMap<>(); for (Map.Entry entry: updates.entrySet()) { String property = entry.getKey(); Update update; if (entry.getValue() instanceof Update) { update = (Update) entry.getValue(); update.property = property; } else if (entry.getValue() instanceof String) { String value = (String) entry.getValue(); update = new Update(); update.property = property; update.setValueKey(value); } else { throw new IllegalArgumentException( "Update property " + property + " has a non-string and non-!updatePdfConfigUpdate " + "value: " + entry.getValue() + "(" + entry.getValue().getClass() + ")"); } finalUpdatesMap.put(property, update); } this.updates = finalUpdatesMap; } @Override protected void extraValidation( final List validationErrors, final Configuration configuration) { if (this.updates == null) { validationErrors.add(new ConfigurationException( "The property 'attributeMap' in the !updatePdfConfig processor is required")); } else { if (this.updates.isEmpty()) { validationErrors.add(new ConfigurationException( "At least one value for 'attributeMap' in !updatePdfConfig should be declared.")); } for (Map.Entry entry: this.updates.entrySet()) { entry.getValue().validate(validationErrors, configuration); } } } @Nullable @Override public In createInputParameter() { return new In(); } @Nullable @Override public Void execute(final In in, final ExecutionContext context) { for (Map.Entry entry: this.updates.entrySet()) { Object value = getAttributeValue(entry.getValue().valueKey, in.values); final PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(PDFConfig.class, entry.getValue().property); final String format = entry.getValue().format; if (format != null) { value = String.format(format, value); } final MethodParameter writeMethodParameter = BeanUtils.getWriteMethodParameter(propertyDescriptor); try { if (propertyDescriptor.getName().equals("keywords")) { value = convertKeywords(value); } writeMethodParameter.getMethod().invoke(in.pdfConfig, value); } catch (Throwable e) { if (writeMethodParameter == null) { throw new RuntimeException( "An error occurred while executing !updatePdfConfig. Unable to set " + "configuration property '" + entry.getKey() + " with value " + value + ". "); } throw new RuntimeException( "An error occurred while executing !updatePdfConfig. Unable to set configuration " + "property '" + entry.getKey() + " with value " + value + ". The expected type is " + writeMethodParameter.getParameterType() + " but the type of the value being set was: " + (value != null ? value.getClass() : "null")); } } return null; } private List convertKeywords(@Nullable final Object keywordsObj) { if (keywordsObj == null) { return Collections.emptyList(); } if (keywordsObj instanceof Iterable) { Iterable obj = (Iterable) keywordsObj; final ArrayList list = new ArrayList<>(); for (Object keyword: obj) { list.add(keyword.toString()); } return list; } if (keywordsObj.getClass().isArray()) { Object[] arr = (Object[]) keywordsObj; final ArrayList list = new ArrayList<>(); for (Object keyword: arr) { list.add(keyword.toString()); } return list; } final String s = keywordsObj.toString(); if (s.contains(",")) { return Arrays.asList(s.split(",")); } return Collections.singletonList(s); } private Object getAttributeValue(final String attributeName, final Values values) { String[] parts = attributeName.split("\\."); Object value = values.getObject(parts[0], Object.class); for (int i = 1; i < parts.length; i++) { assertNonnullValue(attributeName, values, value); String part = parts[i]; final Field field; try { field = value.getClass().getField(part); if (!field.isAccessible()) { field.setAccessible(true); } value = field.get(value); } catch (NoSuchFieldException e) { throw new IllegalArgumentException( "No field " + part + " in object: " + value.getClass() + ". This error is part of the processor " + "!updatePdfConfig for the value: " + attributeName); } catch (IllegalAccessException e) { throw new IllegalArgumentException( "Not permitted to access" + part + " in object: " + value.getClass() + ". This is likely caused by the " + "Java security manager. This error is part of the processor " + "!updatePdfConfig for the value: " + attributeName); } } assertNonnullValue(attributeName, values, value); return value; } private void assertNonnullValue(final String attributeName, final Values values, final Object value) { if (value == null) { throw new IllegalArgumentException( attributeName + " does not identify a value that is currently in the values object. " + "Values object is: \n" + values); } } /** * The input parameters object. */ public static class In { /** * The values object used to retrieve the required attributes. */ public Values values; /** * The pdf configuration object. */ public PDFConfig pdfConfig; } /** *

The object that defines how to update the {@link org.mapfish.print.config.PDFConfig} * (see !updatePdfConfig processor). * [[examples=updatePdfMetadata]] */ public static final class Update implements ConfigurationObject { private String property; private String valueKey; private String format; /** * Default constructor. */ public Update() { // do nothing } Update(final String valueKey, final String format) { this.valueKey = valueKey; this.format = format; } @Override public void validate(final List validationErrors, final Configuration configuration) { if (this.valueKey.isEmpty()) { validationErrors.add(new ConfigurationException( "The value of '" + this.property + "' should not be empty. Error in !updatePdfConfig")); return; } if (this.valueKey.charAt(0) == '.') { validationErrors.add(new ConfigurationException( "The value of '" + this.property + "' should start with a '.', it was " + this.valueKey + ". Error in !updatePdfConfig")); return; } String[] attributeAccessorDefinition = this.valueKey.split("\\."); if (attributeAccessorDefinition.length == 0) { validationErrors.add(new ConfigurationException( this.property + ": " + this.valueKey + " is not a valid mapping in !updatePdfConfig")); return; } final PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(PDFConfig.class, this.property); if (propertyDescriptor == null || BeanUtils.getWriteMethodParameter(propertyDescriptor) == null) { PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(PDFConfig.class); StringBuilder options = new StringBuilder(); for (PropertyDescriptor descriptor: descriptors) { options.append("\n\t* ").append(descriptor.getName()); } validationErrors.add(new ConfigurationException( "There is no pdf config property called '" + this.property + "'. Options include: " + options)); } } /** * The key to use to look up the value in the values object. It can be a path that can reach into * nested objects. *

* Examples 1 a simple lookup key: "key" Example 2 a path. First part (before .) is the lookup key, * the second part is the field name to load: "key.fieldName" * * @param valueKey the path or key for retrieving the value */ public void setValueKey(final String valueKey) { this.valueKey = valueKey; } /** * The replacement format. It is a printf style format. The documentation is in the Formatter class * (just google/bing java.util.Formatter). *

* Example: "Report for %s" * * @param format the update format. There can only be a single value. */ public void setFormat(final String format) { this.format = format; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy