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

com.databasesandlife.util.DomVariableExpander Maven / Gradle / Ivy

The newest version!
package com.databasesandlife.util;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import javax.annotation.Nonnull;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Takes a XML element (containing text and sub-nodes), and expands variables like ${xyz}. 
 *    

* Usage: *

 * try {
 *     Map<String, String> variables = new Map<>() {{ put("foo", "value"); }};
 *
 *     // for example <element attr="${foo}">${foo}</element>
 *     Element elementWithVariables = ...
 *
 *     Document elementExpanded = DomVariableExpander.expand(
 *       variableStyle, variables, elementWithVariables);
 * }
 * catch (VariableNotFoundException e) { .. }
 * 
*

* For the syntax of variables, see the {@link VariableSyntax} enum. *

* Variables in the Map passed to the expand method should not have the dollar prefix. *

* Variable names may contain a-z, A-Z, 0-9, hypen and underscore and are case sensitive. *

* This class is XML namespace aware. * * @author This source is copyright Adrian Smith and licensed under the LGPL 3. * @see Project on GitHub */ public class DomVariableExpander extends IdentityForwardingSaxHandler { @FunctionalInterface public interface VariableGetter { /** Gets the variable with the given name */ @Nonnull String get(@Nonnull String name); } public static class VariableNotFoundException extends RuntimeException { public VariableNotFoundException(String var) { super(var); } } public enum VariableSyntax { /** Accepts variables like ${foo} but NOT $foo. Variables are any chars except close bracket "}" */ dollarThenBraces { protected final Pattern variablePattern = Pattern.compile("\\$\\{(.+?)}"); @Override public CharSequence expand(@Nonnull VariableGetter variables, CharSequence template) throws VariableNotFoundException { var matcher = variablePattern.matcher(template); var result = new StringBuffer(); while (matcher.find()) { var variable = matcher.group(1); // ${xyz} var expansion = variables.get(variable); if (expansion == null) throw new VariableNotFoundException( "Variable '${" + variable + "}' is used in XML template, but is missing from map of variables"); matcher.appendReplacement(result, Matcher.quoteReplacement(expansion)); } matcher.appendTail(result); return result; } }, /** Accepts variables like ${foo} or $foo. Variables are letters, numbers, underscore, hyphen. */ dollarOrDollarThenBraces { protected final Pattern variablePattern = Pattern.compile("\\$(([\\w-]+)|\\{([\\w-]+)})"); @Override public CharSequence expand(@Nonnull VariableGetter variables, CharSequence template) throws VariableNotFoundException { var matcher = variablePattern.matcher(template); var result = new StringBuffer(); while (matcher.find()) { var variable = matcher.group(2); // $xyz if (variable == null) variable = matcher.group(3); // ${xyz} var expansion = variables.get(variable); if (expansion == null) throw new VariableNotFoundException( "Variable '$" + variable + "' is used in XML template, but is missing from map of variables"); matcher.appendReplacement(result, Matcher.quoteReplacement(expansion)); } matcher.appendTail(result); return result; } }; public abstract CharSequence expand(@Nonnull VariableGetter variables, CharSequence template) throws VariableNotFoundException; } protected final VariableSyntax syntax; protected final @Nonnull VariableGetter variables; public DomVariableExpander(VariableSyntax syntax, @Nonnull VariableGetter variables, TransformerHandler outputHandler) { super(outputHandler); this.syntax = syntax; this.variables = variables; } @Override public void startElement(String uri, String localName, String el, Attributes templateAttributes) throws SAXException { var expandedAttributes = new AttributesImpl(templateAttributes); for (var a = 0; a < templateAttributes.getLength(); a++) { var valueTemplate = templateAttributes.getValue(a); var replacement = syntax.expand(variables, valueTemplate); expandedAttributes.setValue(a, replacement.toString()); } super.startElement(uri, localName, el, expandedAttributes); } @Override public void characters(char[] ch, int start, int length) throws SAXException { var templateCharacters = new String(ch, start, length); var expandedCharacters = syntax.expand(variables, templateCharacters); super.characters(expandedCharacters.toString().toCharArray(), 0, expandedCharacters.length()); } public static Document expand(Node prototypeElement, Function expander) throws TransformerException { try { var systemProperties = System.getProperties(); systemProperties.remove("javax.xml.transform.TransformerFactory"); // The resulting DOM var result = new DOMResult(); // SAX identity transformer to populate the resulting DOM var writerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); var toResult = writerFactory.newTransformerHandler(); // Identity transform toResult.setResult(result); // Our transformer which expands variables into the above identity transformer var intoExpander = new SAXResult(expander.apply(toResult)); // Perform the chain of transformations, and populate "result" var source = new DOMSource(prototypeElement); var transformer = TransformerFactory.newInstance().newTransformer(); transformer.transform(source, intoExpander); return (Document) result.getNode(); } catch (TransformerConfigurationException e) { throw new RuntimeException(e); } } public static Document expand(VariableSyntax syntax, @Nonnull VariableGetter variables, Node prototypeElement) throws VariableNotFoundException { try { return expand(prototypeElement, toResult -> new DomVariableExpander(syntax, variables, toResult)); } catch (TransformerConfigurationException e) { throw new RuntimeException(e); } catch (TransformerException e) { if (e.getCause() instanceof VariableNotFoundException) throw (VariableNotFoundException) e.getCause(); throw new RuntimeException(e); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy