/* * Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute * Copyright [2016-2017] EMBL-European Bioinformatics Institute * * 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 org.ensembl.healthcheck.util; import java.util.LinkedHashMap; import java.util.Map; /** * Simple naive implementation of a templating builder class which attempts * to be a {@link java.text.MessageFormat} with named parameters. Each parameter * is evaluated and replaced with the String representation of the value. e.g. * * <h5>Template</h5> * <code> * <pre> * Hello $world$ * </pre> * </code> * * <h5>Java Code</h5> * <code> * <pre> * TemplateBuilder builder = new TemplateBuilder("Hello $world$"); * builder.addPlaceHolder("world", "there"); * String generatedString = builder.generate(); * System.out.println(generatedString); //// Prints "Hello there" * </pre> * </code> * * <p> * The default implementation always uses $ to delimit placeholder positions * but there is nothing to prevent you from using another value such as # * or ". * * <p> * The code also allows you to setup the template once, bind placeholders * generate the String and then call {@link #clearPlaceholders()} to * start binding a new set of parameters. * * <p> * If this implementation does not work then perhaps you need something with * a bit more welly like Velocity or StringTemplate. Both of these solutions * allow you to define macros & more advanced filtering/manipulation techniques * as part of your template rather than having to do the manipulation in Java * code. * * @author ayates * @author $Author$ * @version $Revision$ */ public class TemplateBuilder { private static final String DEFAULT_DELIM = "$"; private final String delim; private final String template; private final Map<String, Object> placeHolders; /** * Used when you want to generate a template quickly but do not * want to go through the hassle of creating one of these objects * and setting the holders. You can use it as: * * <code> * String message = TemplateBuilder.template("hello $greeting$ $name$", * "greeting", "there", "name", "Andy"); * </code> * * @param template The template you wish to use. $ flank the placeholder * @param placeHolders The placeholder array. Look at {@link #addPlaceHolders(Object[])} * for more info on the format * @return The final generated String */ public static String template(String template, Object... placeHolders) { TemplateBuilder builder = new TemplateBuilder(template); builder.addPlaceHolders(placeHolders); return builder.generate(); } public TemplateBuilder(final String template) { this(DEFAULT_DELIM, template); } public TemplateBuilder(final String template, final Map<String,Object> placeHolders) { this(DEFAULT_DELIM, template, placeHolders); } public TemplateBuilder(final String delim, final String template) { this(delim, template, new LinkedHashMap<String, Object>()); } public TemplateBuilder(final String delim, final String template, final Map<String,Object> placeHolders) { this.delim = delim; this.template = template; this.placeHolders = placeHolders; } public void addPlaceHolder(String key, Object value) { placeHolders.put(key, value); } public void addPlaceHolders(Map<String,Object> placeHolders) { this.placeHolders.putAll(placeHolders); } /** * Removes all the currently set placeholders. Useful if you want to * reuse the same template object */ public void clearPlaceholders() { this.placeHolders.clear(); } /** * Works in the same way the Perl array to hash code works where every * odd element is a key and every even element is the value. It's a very * un-Java way of encoding this information but it does allow us to populate * a template quickly. */ public void addPlaceHolders(Object... params) { if((params.length % 2) != 0) { throw new UtilUncheckedException("Given params array was not of " + "even length. Length was "+params.length); } for(int i=0; i<params.length; i = i+2) { this.addPlaceHolder(String.valueOf(params[i]), params[i+1]); } } /** * Performs the generating procedure by looping through all the given * key value pairs and performing String replace operations. */ public String generate() { String output = template; for(Map.Entry<String,Object> entry: placeHolders.entrySet()) { String targetEntry = generatePlaceholder(entry.getKey()); output = output.replace(targetEntry, String.valueOf(entry.getValue())); } return output; } /** * Potentially useful shortcut for using the builder. This delegates to * {@link #generate()} so you can push this into your debug statements * like: * * <p> * <code> * TemplateBuilder tb = new TemplateBuilder("Found $val$ values"); * tb.addPlaceHolders("val", 127131); * getLog().info(tb); * </code> * * <p> * However you can use the {@link #template(String, Object[])} method for * even more convenient logging code. */ public String toString() { return generate(); } /** * Surrounds the placeholder delim to the given String */ private String generatePlaceholder(String placeholder) { return delim+placeholder+delim; } }