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

org.n3r.diamond.client.impl.DiamondSubstituter Maven / Gradle / Ivy

The newest version!
package org.n3r.diamond.client.impl;

import org.apache.commons.lang3.StringUtils;
import org.n3r.diamond.client.Miner;

import java.util.HashSet;
import java.util.Properties;
import java.util.Set;


/**
 * Changed from spring PropertyPlaceholderConfigurer.java.
 * Support following formats:
 * 1) ${variable}
 * 2) ${variable:defaultValue}
 * 3) ${variabl${letter}} Recursive invocation
 * 4) mixed above
 *
 * @author Bingoo Huang
 */
public abstract class DiamondSubstituter {
    /**
     * Default Holder prefix: "${" .
     */
    public static final String DEF_HOLDER_PREFIX = "${";
    /**
     * Default Holder suffix: "}".
     */
    public static final String DEF_HOLDER_SUFFIX = "}";
    /**
     * Check system properties if not resolvable in the specified properties.
     * This is the default.
     */
    public static final int SYS_PROPS_MODE_FALLBACK = 1;

    /**
     * Check system properties first, before trying the specified properties.
     * This allows system properties to override any other property source.
     */
    public static final int SYS_PROPS_MODE_OVERRIDE = 2;

    private static final int DEF_HOLDER_PREFIX_LEN = DEF_HOLDER_PREFIX.length();
    private static final int DEF_HOLDER_SUFFIX_LEN = DEF_HOLDER_SUFFIX.length();


    /**
     * Parse the given String value recursively, to be able to resolve
     * nested Holders (when resolved property values in turn contain
     * Holders again).
     *
     * @param strVal           the String value to parse
     * @param ignoreBadHolders during the current resolution attempt (used to detect circular references
     *                         between Holders). Only non-null if we're parsing a nested Holder.
     * @param group            diamond group id
     * @param dataId           diamond data id
     * @param lastProperties   last properties
     * @return value after substituted
     */
    public static String substitute(String strVal, boolean ignoreBadHolders, String group, String dataId, Properties lastProperties) {
        if (strVal == null) return null;

        Set visitedHolders = new HashSet();
        return substitute(strVal, visitedHolders, ignoreBadHolders, group, dataId, lastProperties);
    }

    /**
     * Parse the given String value recursively, to be able to resolve
     * nested Holders (when resolved property values in turn contain
     * Holders again).
     *
     * @param strVal           the String value to parse
     * @param visitedHolders   the Holders that have already been visited
     * @param ignoreBadHolders during the current resolution attempt (used to detect circular references
     *                         between Holders). Only non-null if we're parsing a nested Holder.
     * @param group            diamond group id
     * @param dataId           diamond data id
     * @param lastProperties   last properties
     * @return value after substituted
     */
    public static String substitute(String strVal, Set visitedHolders,
                                    boolean ignoreBadHolders, String group, String dataId, Properties lastProperties) {
        if (strVal == null) return null;

        StringBuffer buf = new StringBuffer(strVal);
        int startIndex = strVal.indexOf(DEF_HOLDER_PREFIX);
        while (startIndex != -1) {
            int endIndex = findHolderEndIndex(buf, startIndex);
            if (endIndex != -1) {
                String holder = buf.substring(startIndex + DEF_HOLDER_PREFIX_LEN, endIndex);
                String defValue = null;
                int defIndex = StringUtils.lastIndexOf(holder, ":");
                if (defIndex >= 0) {
                    defValue = StringUtils.trim(holder.substring(defIndex + 1));
                    holder = StringUtils.trim(holder.substring(0, defIndex));
                }

                if (!visitedHolders.add(holder)) {
                    throw new RuntimeException("Circular PlaceHolder reference '"
                            + holder + "' in property definitions");
                }
                // Recursive invocation, parsing Holders contained in the Holder key.
                holder = substitute(holder, visitedHolders, ignoreBadHolders, group, dataId, lastProperties);
                // Now obtain the value for the fully resolved key...
                String propVal = resolveHolder(strVal, holder, SYS_PROPS_MODE_FALLBACK, defValue, group, dataId, lastProperties);
                if (propVal != null) {
                    // Recursive invocation, parsing Holders contained in the
                    // previously resolved Holder value.
                    propVal = substitute(propVal, visitedHolders, ignoreBadHolders, group, dataId, lastProperties);
                    buf.replace(startIndex, endIndex + DEF_HOLDER_SUFFIX_LEN, propVal);

                    startIndex = buf.indexOf(DEF_HOLDER_PREFIX, startIndex + propVal.length());
                } else if (ignoreBadHolders) {
                    // Proceed with unprocessed value.
                    startIndex = buf.indexOf(DEF_HOLDER_PREFIX, endIndex + DEF_HOLDER_SUFFIX_LEN);
                } else {
                    throw new RuntimeException("Could not resolve Placeholder '" + holder + "'");
                }
                visitedHolders.remove(holder);
            } else {
                startIndex = -1;
            }
        }

        return buf.toString();
    }

    private static int findHolderEndIndex(CharSequence buf, int startIndex) {
        int index = startIndex + DEF_HOLDER_PREFIX_LEN;
        int withinNestedHolder = 0;
        while (index < buf.length()) {
            if (substringMatch(buf, index, DEF_HOLDER_SUFFIX)) {
                if (withinNestedHolder > 0) {
                    withinNestedHolder--;
                    index = index + DEF_HOLDER_SUFFIX_LEN;
                } else {
                    return index;
                }
            } else if (substringMatch(buf, index, DEF_HOLDER_PREFIX)) {
                withinNestedHolder++;
                index = index + DEF_HOLDER_PREFIX_LEN;
            } else {
                index++;
            }
        }
        return -1;
    }

    /**
     * Test whether the given string matches the given substring at the given index.
     *
     * @param str       the original string (or StringBuffer)
     * @param index     the index in the original string to start matching against
     * @param substring the substring to match at the given index
     * @return true/false
     */
    public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
        for (int j = 0; j < substring.length(); j++) {
            int i = index + j;
            if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
                return false;
            }
        }
        return true;
    }


    /**
     * Resolve the given Holder using the given properties, performing
     * a system properties check according to the given mode.
     * 

Default implementation delegates to resolveHolder * (Holder, props) before/after the system properties check. *

Subclasses can override this for custom resolution strategies, * including customized points for the system properties check. * * @param strVal string value * @param holder the Holder to resolve * @param sysPropsMode the system properties mode, * according to the constants in this class * @param defaultValue default value * @param group diamond group id * @param dataId diamond data id * @param lastProperties last properties * @return the resolved value, of null if none * @see System#getProperty */ private static String resolveHolder(String strVal, String holder, int sysPropsMode, String defaultValue, String group, String dataId, Properties lastProperties) { String propVal = null; if (sysPropsMode == SYS_PROPS_MODE_OVERRIDE) { propVal = resolveSystemProperty(holder); } if (propVal == null) { propVal = resolveHolder(strVal, holder, defaultValue, group, dataId, lastProperties); } if (propVal == null && sysPropsMode == SYS_PROPS_MODE_FALLBACK) { propVal = resolveSystemProperty(holder); } return propVal; } /** * Resolve the given Holder using the given properties. * The default implementation simply checks for a corresponding property key. *

Subclasses can override this for customized Holder-to-key mappings * or custom resolution strategies, possibly just using the given properties * as fallback. *

Note that system properties will still be checked before respectively * after this method is invoked, according to the system properties mode. * * @param strVal string value * @param holder the Holder to resolve * @param defaultValue default value * @param curGroup diamond group id * @param curDataId diamond data id * @param lastProperties last properties * @return the resolved value, of null if none */ protected static String resolveHolder(String strVal, String holder, String defaultValue, String curGroup, String curDataId, Properties lastProperties) { int separated = holder.indexOf('^'); if (separated < 0 && holder.startsWith("this.")) { String referKey = holder.substring("this.".length()); return recursiveSubstitute(strVal, defaultValue, curGroup, curDataId, lastProperties, referKey); } String group = Constants.DEFAULT_GROUP; String dataId = holder; if (separated > 0 && separated < holder.length() - 1) { group = holder.substring(0, separated); dataId = holder.substring(separated + 1); } String value; int propKeyPos = dataId.indexOf('^'); if (propKeyPos > 0) { String subDataId = dataId.substring(0, propKeyPos); String propKey = dataId.substring(propKeyPos + 1); if (isSameGroupAndDataId(curGroup, curDataId, group, subDataId)) { value = recursiveSubstitute(strVal, defaultValue, curGroup, curDataId, lastProperties, propKey); } else { value = new Miner().getMiner(group, subDataId).getString(propKey); } } else { if (isSameGroupAndDataId(curGroup, curDataId, group, dataId)) { // reference to itself throw new RuntimeException(curGroup + "^" + curDataId + "can not refer itself"); } value = new Miner().getStone(group, dataId); } return value != null ? value : defaultValue; } private static String recursiveSubstitute(String strVal, String defaultValue, String curGroup, String curDataId, Properties lastProperties, String referKey) { Properties properties = lastProperties != null ? lastProperties : DiamondUtils.parseStoneToProperties(strVal); String property = properties.getProperty(referKey, defaultValue); String substitute = substitute(property, true, curGroup, curDataId, properties); properties.setProperty(referKey, substitute); return substitute; } private static boolean isSameGroupAndDataId(String curGroup, String curDataId, String group, String subDataId) { return StringUtils.equals(group, curGroup) && StringUtils.equals(subDataId, curDataId); } /** * Resolve the given key as JVM system property, and optionally also as * system environment variable if no matching system property has been found. * * @param key the Holder to resolve as system property key * @return the system property value, or null if not found * @see System#getProperty(String) * @see System#getenv(String) */ private static String resolveSystemProperty(String key) { try { String value = System.getProperty(key); if (value == null) { value = System.getenv(key); } return value; } catch (Exception ex) { return null; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy