/* * Created on Oct 25, 2005 by mschilli */ package alma.acs.commandcenter.util; import java.util.Map; /** * Purpose of this class is to resolve embedded variables * in a source string like "${user.home}/config/${policydir}". * <p> * Invocation of the {@link #toString()} method will resolve the embedded * variables using the <em>Java system properties</em>. * By using the {@link #toString(Map)} method you can specify which * dictionary to use. * * In both cases, the following holds: If a non-existing (or null-value) * variable is encountered, it will either a) be resolved to the empty string, * or b) an exception will be thrown. This depends on the boolean flag {@link #lenient}. * </p> * * <p><strong>Note: </strong> * Relationship between {@link alma.acs.commandcenter.util.PreparedString} * and {@link alma.acs.commandcenter.util.VariableString}: * * {@link alma.acs.commandcenter.util.PreparedString} uses <em>positional</em> * parameters to replace placeholders with the elements of a string array. * {@link alma.acs.commandcenter.util.VariableString} uses <em>named</em> * parameters to replace placeholders with values from a map.</p> */ public class VariableString { protected boolean lenient; protected String source; /** * Constructs an instance with the given source, and a lenient way * of dealing with unresolvable variables. * * @param source string, e.g. "${user.home}/config/${policydir}". */ public VariableString(String source) { this(source, true); } /** * Constructs an instance with the given source. * * @param source string, e.g. "${user.home}/config/${policydir}". * @param lenient whether unresolvable variables will provoke an exception */ public VariableString(String source, boolean lenient) { this.source = source; this.lenient = lenient; } // ================================================ // API // ================================================ /** * Returns the <strong>non-</strong>resolved (source) version of this instance. * @return the <strong>non-</strong>resolved (source) version of this instance. */ public String getSource() { return source; } /** * Returns a <strong>resolved</strong> version of this instance, * resolving is done using the <em>Java system properties</em>. * * @return the <strong>resolved</strong> version of this instance. * @throws UnresolvableException if variables cannot be resolved and we are non-{@link #lenient} */ @Override public String toString() throws UnresolvableException { return toString(System.getProperties()); } /** * Returns a <strong>resolved</strong> version of this instance, * resolving is done using the specified map. * * @return the <strong>resolved</strong> version of this instance. * @throws UnresolvableException if variables cannot be resolved and we are non-{@link #lenient} */ public String toString (Map<?,Object> map) throws UnresolvableException { // wrap the given map with an ad-hoc IResolver final Map<?,Object> m = map; IResolver res = new IResolver() { public String resolve (String name) { Object value = m.get(name); return (value instanceof String)? (String)value : null; } }; return resolveVariables(res, source); } /** * Returns a <strong>resolved</strong> version of this instance, * resolving is done using the specified IResolver. * * @return the <strong>resolved</strong> version of this instance. * @throws UnresolvableException if variables cannot be resolved and we are non-{@link #lenient} */ public String toString (IResolver map) throws UnresolvableException { return resolveVariables(map, source); } // ================================================ // Internal // ================================================ /** * Performs the replacement of embedded variable names, recursively. * If a non-existing (or null-value) variable is encountered, this * will either a) resolve to the empty string, or b) throw an exception, * depending on the boolean flag {@link #lenient}. */ protected String resolveVariables (IResolver res, String value) throws UnresolvableException { if (value == null) return null; int markerStart = value.indexOf("${"); if (markerStart != -1) { int markerEnd = value.indexOf("}", markerStart); if (markerEnd != -1) { // split into 3 parts String preVarName = value.substring(0, markerStart); String embeddedVarName = value.substring(markerStart + 2, markerEnd); String postVarName = value.substring(markerEnd + 1); // resolve middle part Object embeddedVarValue = res.resolve(embeddedVarName); if (embeddedVarValue == null) { // cannot resolve, how should we react? if (lenient) { embeddedVarValue = ""; } else { throw new UnresolvableException(embeddedVarName); } } // concatenate again value = preVarName + embeddedVarValue + postVarName; // continue with next variable return (resolveVariables(res, value)); } } return value; } // // =========== Inner Types =========== // static public interface IResolver { public String resolve(String name); } static public class UnresolvableException extends RuntimeException { protected String variableName; protected UnresolvableException (String varname) { this.variableName = varname; } public String getVariableName() { return variableName; } } }