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

com.seanox.pdf.Template Maven / Gradle / Ivy

There is a newer version: 4.4.0
Show newest version
/**
 * LIZENZBEDINGUNGEN - Seanox Software Solutions ist ein Open-Source-Projekt, im
 * Folgenden Seanox Software Solutions oder kurz Seanox genannt.
 * Diese Software unterliegt der Version 2 der Apache License.
 *
 * PDF Service
 * Copyright (C) 2020 Seanox Software Solutions
 *  
 * Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.seanox.pdf;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;

/** 
 * Abstract class for implementing of template implementations.
 * 
 * About the templates
 * As engine {@link Generator} is used, here you can find more details.
 * The most important in short form:
 *  
 * #[palceholder]
 * Simple placeholder, global or in a section.
 *  
 * #[palceholder-exists]
 * Pendant to any placeholder, if it exists.
 *  
 * #[section[[...]]]
 * Section/Bock can contain more substructures.
 * Sections/blocks are only rendered if a corresponding map entry exists.
 *  
 * ![static-text]
 * Placeholder for static text from the ResourceBundle.
 * As language {@link Service.Meta#locale} is used.
 *  
 * #[locale]
 * Placeholder provided by {@link Service} with the current language.
 * Available in all sections (header, content/data, footer).
 *  
 * #[page]
 * Placeholder provided by {@link Service} with the current page number.
 * Available in sections: header, footer
 *  
 * #[pages]
 * Placeholder provided by {@link Service} with the total page number.
 * Available in sections: header, footer
*
* Template 3.1x.0x 20200313
* Copyright (C) 2020 Seanox Software Solutions
* Alle Rechte vorbehalten. * * @author Seanox Software Solutions * @version 3.1x.0x 20200313 */ public abstract class Template extends Service.Template { /** Naturally sort comparator */ private static class NaturalComparator implements Comparator { /** * Normalizes the numeric fragments that they can be sorted. * @param string string to be escaped * @return the normalized string */ private static String normalize(String string) { String buffer = ""; string = StringUtils.trimToEmpty(string); for (String fragment : string.split("(?:(?<=\\d)(?!\\d))|(?:(? map, String key, String value) throws PreviewDataParserException { final String PATTERN_EXPRESSION = "^(?i)([a-z](?:[\\w\\-]*\\w)*)((?:\\[\\s*\\d+\\s*\\])*)(\\.([a-z](?:[\\w\\-]*\\w)*)((?:\\[\\s*\\d+\\s*\\])*))*$"; if (!key.matches(PATTERN_EXPRESSION)) throw new PreviewDataParserException("Invalid key: " + key); if (key.contains(".")) { String path = key.replaceAll("\\.[^\\.]+$", ""); for (String entry : path.split("\\.")) { final String PATTERN_LIST_EXPRESSION = "^(.*)\\s*\\[\\s*(\\d+)\\s*\\]$"; if (entry.matches(PATTERN_LIST_EXPRESSION)) { int index = Integer.valueOf(entry.replaceAll(PATTERN_LIST_EXPRESSION, "$2")).intValue(); entry = entry.replaceAll(PATTERN_LIST_EXPRESSION, "$1").trim(); if (!map.containsKey(entry) || !(map.get(entry) instanceof List)) map.put(entry, new ArrayList<>()); List list = (List)map.get(entry); if (list.size() < index) throw new PreviewDataParserException("Invalid key index: " + key); if (list.size() > index) { if (!(list.get(index) instanceof Map)) list.set(index, new HashMap<>()); } else list.add(index, new HashMap<>()); map = (Map)list.get(index); } else { if (!map.containsKey(entry) || !(map.get(entry) instanceof Map)) map.put(entry, new HashMap<>()); map = (Map)map.get(entry); } } key = key.replaceAll("^.*\\.", ""); } map.put(key, value); } /** * Returns the preview data for the template as properties. * The properties file are in the same package and use the same name.
* e.g. ArticleTemplateImpl -> ArticleTemplateImpl.properties * @return the preview for one data record as preview * @throws Exception * In case of unexpected errors. */ protected Properties getPreviewProperties() throws Exception { String resource = this.getSourcePath(); resource = resource.replaceAll("[^\\\\/\\.]+$", "") + "properties"; Properties properties = new Properties(); properties.load(this.getResourceStream(resource)); return properties; } /** * Creates preview data based on a properties file corresponding to impl. * The properties file are in the same package and use the same name.
* e.g. ArticleTemplateImpl -> ArticleTemplateImpl.properties * @return the data for one data record as preview * @throws Exception * In case of unexpected errors. */ @SuppressWarnings({"rawtypes", "unchecked"}) @Override protected Map getPreviewData() throws Exception { Properties properties = this.getPreviewProperties(); Map map = new HashMap<>(); Set keySet = new TreeSet<>(new NaturalComparator()); keySet.addAll(properties.keySet()); for (Object key : keySet) { String source = ((String)key); String target = source.replaceAll("(^\\.+)|(\\.+$)", ""); Template.collectPreviewData(map, target, properties.getProperty(source)); } return map; } /** * Escapes all characters greater than ASCII 0x7F as HTML code. * The value {@code null} is used as a space. * @param text text to escape * @return the possibly escaped text */ static String escapeHtml(String text) { if (text == null) return ""; StringBuilder build = new StringBuilder(); for (char digit : text.toCharArray()) { if (digit > 0x7F) build.append("&#").append((int)digit).append(";"); else if (digit == '&') build.append("&"); else build.append(digit); } text = build.toString(); text = text.replaceAll("(\r\n)|(\n\r)|[\r\n]", "
"); return text; } /** * Escapes the text values of/in a Object. * @param object object to escape * @return the escaped object */ @SuppressWarnings({"rawtypes", "unchecked"}) private static Object escapeHtml(Object object) { if (object == null) return ""; if (object instanceof Collection) return Template.escapeHtml((Collection)object); if (object instanceof Map) return Template.escapeHtml((Map)object); return Template.escapeHtml(String.valueOf(object)); } /** * Escapes the text values in a Collection. * @param collection collection with text values to escape * @return the Collection with escaped text values */ private static Collection> escapeHtml(Collection> collection) { if (collection == null) collection = new ArrayList<>(); return collection.stream().map( entry -> Template.escapeHtml(entry) ).collect(Collectors.toList()); } /** * Escapes the text values in a Map. * @param map map with text values to escape * @return the Map with escaped text values */ private static Map escapeHtml(Map map) { if (map == null) map = new HashMap<>(); return map.entrySet().stream().collect(Collectors.toMap( entry -> entry.getKey(), entry -> Template.escapeHtml(entry.getValue()) )); } /** * Extends the contained maps by an exists-key for each key. * This hack is necessary because CSS :empty has no effect in OpenHtmlToPdf * and empty elements cannot be smoothed out by CSS. Therefore the inverted * exists solution. * @param collection * @return collection with additional exists keys */ private static Collection> indicateEmpty(Collection> collection) { List> result = new ArrayList<>(); if (collection == null) collection = new ArrayList<>(); collection.forEach(entry -> { if (!entry.isEmpty()) result.add(Template.indicateEmpty(entry)); }); return result; } /** * Extends the maps by an exists-key for each key. * This hack is necessary because CSS :empty has no effect in OpenHtmlToPdf * and empty elements cannot be smoothed out by CSS. Therefore the inverted * exists solution. * @param collection * @return collection with additional exists keys */ @SuppressWarnings({"rawtypes", "unchecked"}) private static Map indicateEmpty(Map map) { Map result = new HashMap<>(); if (map == null) map = new HashMap<>(); map.entrySet().forEach(entry -> { if (entry.getValue() != null) { if (entry.getValue() instanceof Collection) { Collection value = (Collection)entry.getValue(); if (!value.isEmpty()) result.put(entry.getKey() + "-exists", "exists"); result.put(entry.getKey(), Template.indicateEmpty(value)); } else if (entry.getValue() instanceof Map) { Map value = (Map)entry.getValue(); if (!value.isEmpty()) result.put(entry.getKey() + "-exists", "exists"); result.put(entry.getKey(), Template.indicateEmpty(value)); } else { String value = String.valueOf(entry.getValue()); if (!value.trim().isEmpty()) result.put(entry.getKey() + "-exists", "exists"); result.put(entry.getKey(), value); } } }); return result; } @Override protected String generate(String markup, Service.Meta.Type type, Service.Meta meta) { Map statics = meta.getStatics(); if (statics == null) statics = new HashMap<>(); Pattern pattern = Pattern.compile("\\!\\[\\s*(.*?)\\s*\\]"); Matcher matcher = pattern.matcher(markup); while (matcher.find()) markup = markup.replace(matcher.group(0), Template.escapeHtml(statics.get(matcher.group(1)))); Generator generator = Generator.parse(markup.getBytes()); if (Service.Meta.Type.HEADER.equals(type)) { Map header = Template.escapeHtml(meta.getHeader()); header = Template.indicateEmpty(header); generator.set(header); } else if (Service.Meta.Type.DATA.equals(type)) { Map data = Template.escapeHtml(meta.getData()); data = Template.indicateEmpty(data); generator.set(data); } else if (Service.Meta.Type.FOOTER.equals(type)) { Map footer = Template.escapeHtml(meta.getFooter()); footer = Template.indicateEmpty(footer); generator.set(footer); } generator.set(new HashMap() { private static final long serialVersionUID = 1L; { if (meta.getLocale() != null) put("locale", meta.getLocale().getLanguage()); }}); return new String(generator.extract()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy