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

org.ow2.util.substitution.resolver.RecursiveResolver 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: RecursiveResolver.java 5562 2010-08-12 12:24:45Z sauthieg $
 * --------------------------------------------------------------------------
 */

package org.ow2.util.substitution.resolver;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.ow2.util.substitution.IPropertyResolver;
import org.ow2.util.substitution.ISubstitutionEngine;

/**
 * @author Guillaume Sauthier
 */
public class RecursiveResolver implements IPropertyResolver {

    /**
     * Engine used to delegate recursion.
     */
    private ISubstitutionEngine engine;

    /**
     * Delegating resolver.
     */
    private IPropertyResolver delegate;

    /**
     * Processed Expressions.
     */
    private Stack processedExpressions;

    /**
     * Intermediate resolutions.
     */
    private Map resolutions;

    /**
     * Reports.
     */
    private List reports;

    /**
     * Is this recursive resolver strict ?
     */
    private boolean strict = true;

    public RecursiveResolver(final ISubstitutionEngine engine,
                             final IPropertyResolver delegate) {
        assert engine != null;
        assert delegate != null;

        this.engine = engine;
        this.delegate = delegate;
        this.processedExpressions = new Stack();
        this.resolutions = new Hashtable();
        this.reports = new ArrayList();
    }

    public boolean isStrict() {
        return strict;
    }

    public void setStrict(boolean strict) {
        this.strict = strict;
    }

    public List getReports() {
        return reports;
    }

    public String resolve(final String expression) {

        // Detect if we're going into a loop to prevent infinite recursion
        if (isAlreadyProcessed(expression)) {

            // Create a recursion report
            Report report = createReport(expression);
            this.reports.add(report);

            // Throw exception if we're in strict mode
            if (isStrict()) {
                throw new IllegalArgumentException(report.toString());
            }

            // Return the resolved value with an "infinite" marker
            return "[" + expression + ":infinite-loop]";

        } else {

            // Delegate resolution
            String resolved = delegate.resolve(expression);

            if (resolved == null) {
                // Property could not be resolved
                throw new IllegalArgumentException("Expression [" + expression + "] could not be resolved.");
            }
            
            // Recursive substitution of the resolved value
            return doSubstitute(expression, resolved);
        }
    }

    /**
     * Perform a recursive substitution
     * @param expression original expression
     * @param resolved evaluated value of the expression (may contains variables)
     * @return the substituted value of the resolution result
     */
    private String doSubstitute(final String expression, final String resolved) {

        // Store values
        processedExpressions.push(expression);
        resolutions.put(expression, resolved);
        try {
            // Perform substitution
            return engine.substitute(resolved);
        } finally {

            // Do some clean-up
            processedExpressions.pop();
            resolutions.remove(expression);
        }
    }

    private boolean isAlreadyProcessed(final String expression) {
        return processedExpressions.contains(expression);
    }

    /**
     * Create a report containing information about the recursion error.
     * @param name infinite recursive expression
     * @return a well formed Report
     */
    private Report createReport(final String name) {

        Report report = new Report();
        report.expression = name;

        // Store the resolution stack
        report.resolutionStack = new Stack();
        for (String element : processedExpressions) {
            Resolution res = new Resolution(element, resolutions.get(element));
            report.resolutionStack.push(res);
        }

        return report;
    }

    public static class Report {
        private String expression;
        private Stack resolutionStack;

        public String getRecursiveExpression() {
            return expression;
        }

        public Stack getResolutionStack() {
            return resolutionStack;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();

            sb.append("Stopping infinite recursion loop on '");
            sb.append(expression);
            sb.append("'\n");

            sb.append("Recursion stack\n");
            int i = 0;
            for (Resolution resolution : resolutionStack) {
                sb.append("  ");
                sb.append(i);
                sb.append(". ");
                sb.append(resolution.name);
                sb.append(" -> ");
                sb.append(resolution.value);
                sb.append("\n");
                i++;
            }

            return sb.toString();

        }
    }

    /**
     * Simple name/value pair class representation.
     */
    public static class Resolution {

        public Resolution(final String name, final String value) {
            this.name = name;
            this.value = value;
        }

        public String name;
        public String value;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Resolution that = (Resolution) o;

            if (name != null ? !name.equals(that.name) : that.name != null) return false;
            if (value != null ? !value.equals(that.value) : that.value != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + (value != null ? value.hashCode() : 0);
            return result;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy