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

org.ow2.util.substitution.engine.DefaultSubstitutionEngine Maven / Gradle / Ivy

There is a newer version: 1.0.37
Show newest version
/**
 * 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;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy