org.ow2.util.substitution.engine.DefaultSubstitutionEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of util-substitution Show documentation
Show all versions of util-substitution Show documentation
Utility classes for properties substitution.
/**
* EasyBeans
* Copyright (C) 2010 Bull S.A.S.
* Contact: [email protected]
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* --------------------------------------------------------------------------
* $Id: DefaultSubstitutionEngine.java 5561 2010-08-12 11:55:54Z sauthieg $
* --------------------------------------------------------------------------
*/
package org.ow2.util.substitution.engine;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.ow2.util.substitution.IPropertyResolver;
import org.ow2.util.substitution.ISubstitutionEngine;
import org.ow2.util.substitution.engine.element.CompositeElement;
import org.ow2.util.substitution.engine.element.FixedValueElement;
import org.ow2.util.substitution.engine.element.VariableElement;
import org.ow2.util.substitution.engine.util.StringUtils;
/**
* This substitution engine supports customization of the property marker.
* By default, it uses ${}as property marker.
*
* @author Loris Bouzonnet
* @author Guillaume Sauthier
*/
public class DefaultSubstitutionEngine implements ISubstitutionEngine {
/**
* JDK Logger in use.
*/
private static final Logger logger = Logger.getLogger(DefaultSubstitutionEngine.class.getName());
public static final char DEFAULT_MARKER = '$';
public static final char DEFAULT_OPENING = '{';
public static final char DEFAULT_ENDING = '}';
//
private char markerChar = DEFAULT_MARKER;
private char openingChar = DEFAULT_OPENING;
private char endingChar = DEFAULT_ENDING;
/**
* Property resolver to be used for property substitution.
*/
private IPropertyResolver resolver;
public void setMarkerChar(char markerChar) {
this.markerChar = markerChar;
}
public void setOpeningChar(char openingChar) {
this.openingChar = openingChar;
}
public void setEndingChar(char endingChar) {
this.endingChar = endingChar;
}
public void setResolver(IPropertyResolver resolver) {
this.resolver = resolver;
}
/**
* Subsitute variables found in this String with their values.
* @param variableValue chain to be evaluated
* @return the given String with variable substituted with values
* @throws IllegalArgumentException when the String cannot be parsed or
* when variables cannot be evaluated
*/
public String substitute(final String variableValue) {
// Parse the input String into a ResolvableElement structure
IResolvableElement element = buildResolvableElement(variableValue);
// Log the element parsed structure
logger.fine(element.toString());
// Resolve the properties
return element.getValue(resolver);
}
/**
* Build an IResolvableElement from the given input String.
* @param input String to be parsed
* @return a resolvable element
*/
private IResolvableElement buildResolvableElement(final String input) {
// Build the composite and launch the initial parsing
CompositeElement element = new CompositeElement();
buildResolvableElement(element, input);
return element;
}
/**
* Build an IResolvableElement from the given input String.
* @param composite composite resolvable element to be filled up
* @param input String to be parsed
*/
private void buildResolvableElement(final CompositeElement composite,
final String input) {
// TODO Should be cached somewhere
String opening = String.valueOf(markerChar).concat(String.valueOf(openingChar));
String ending = String.valueOf(endingChar);
// Init parsing with remaining bits
String remaining = input;
// While there is something to parse
while (!StringUtils.isEmpty(remaining)) {
// Look for the opening marker
int openingMarkerIndex = remaining.indexOf(opening);
String prefix;
VariableElement variable = new VariableElement();
if (openingMarkerIndex == -1) {
// Only a prefix: there is no opening marker
prefix = remaining;
variable = null;
} else if (openingMarkerIndex == 0) {
// Only a sub property: opening marker in first position
prefix = "";
} else {
// Prefix & sub property: general case
prefix = remaining.substring(0, openingMarkerIndex);
}
// Check for one extra closing bracket (if there was an opening marker)
if ((openingMarkerIndex != -1) && prefix.contains(ending)) {
throw new IllegalArgumentException(remaining + " is not a valid expression: '" + opening + "' missing");
}
if (!StringUtils.isEmpty(prefix)) {
// If there is a prefix, add a FixedValueElement as child
composite.getElements().add(new FixedValueElement(prefix));
}
// If there is a variable
if (variable != null) {
int startIndex = openingMarkerIndex + opening.length();
int closingMarkerIndex = startIndex;
int subPropertyCounter = 1;
// Search for one corresponding closing bracket
while (subPropertyCounter != 0 && closingMarkerIndex < remaining.length()) {
if (remaining.charAt(closingMarkerIndex) == endingChar) {
subPropertyCounter--;
} else if (remaining.charAt(closingMarkerIndex) == openingChar) {
subPropertyCounter++;
}
closingMarkerIndex++;
}
// Check for one erroneous extra opening bracket
// We've processed the whole String but there are opened properties that are not closed
if (subPropertyCounter != 0 && closingMarkerIndex == remaining.length()) {
throw new IllegalArgumentException(remaining + " is not a valid expression: " + ending + " missing");
}
// Retrieve the property name (the value in the brackets)
String name = remaining.substring(startIndex, closingMarkerIndex - 1);
// Check for empty property
if (StringUtils.isEmpty(name)) {
// TODO maybe we could be lax and simply remove that invalid value from the chain ?
throw new IllegalArgumentException("The chain " + opening + ending +" is forbidden.");
}
// Build the Variable element (recursion)
buildResolvableElement(variable, name);
composite.getElements().add(variable);
// Continue the processing with the remaining bits
remaining = remaining.substring(closingMarkerIndex);
} else {
// End the processing
remaining = null;
}
}
}
}