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

com.github.bingoohuang.utils.lang.Substituters Maven / Gradle / Ivy

package com.github.bingoohuang.utils.lang;

import ognl.Ognl;
import ognl.OgnlException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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


/**
 * 从PropertyPlaceholderConfigurer.java代码中摘出的字符串变量解析的辅助类。
 * 字符串中变量引用形式:
 * 1) ${variable}
 * 2) ${variable:defaultValue}
 * 3) ${variabl${letter}} Recursive invocation
 * 4) mixed above
 *
 * @author Bingoo Huang
 */
@SuppressWarnings("unchecked")
public abstract class Substituters {
    /**
     * 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();
    private static Logger log = LoggerFactory.getLogger(Substituters.class);

    /**
     * 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
     * @return 进行值填充后的字符串
     */
    public static String parse(String strVal) {
        Set visitedHolders = new HashSet();
        return parse(strVal, null, visitedHolders, false);
    }

    /**
     * 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 props  the Properties to resolve Holders against
     * @return 进行值填充后的字符串
     */
    public static String parse(String strVal, Object props) {
        Set visitedHolders = new HashSet();
        return parse(strVal, props, visitedHolders, false);
    }

    public static String parse(String strVal, Object sub, Object props) {
        Set visitedHolders = new HashSet();
        return parse(strVal, sub, props, visitedHolders, false);
    }

    private static String parse(String strVal, Object sub, Object props, Set visitedHolders, boolean ignoreBadHolders) {
        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 keyValue.
                holder = parse(holder, props, visitedHolders, ignoreBadHolders);
                // Now obtain the value for the fully resolved keyValue...
                String propVal = resolveHolder(holder, sub, props, SYS_PROPS_MODE_FALLBACK, defValue);
                if (propVal != null) {
                    // Recursive invocation, parsing Holders contained in the
                    // previously resolved Holder value.
                    propVal = parse(propVal, props, visitedHolders, ignoreBadHolders);
                    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 String resolveHolder(String holder, Object sub, Object props, int sysPropsMode, String defaultValue) {
        String propVal = null;
        if (sysPropsMode == SYS_PROPS_MODE_OVERRIDE) {
            propVal = resolveSystemProperty(holder);
        }
        if (propVal == null) {
            propVal = resolveHolder(holder, props, defaultValue);
        }
        if (propVal == null && sysPropsMode == SYS_PROPS_MODE_FALLBACK) {
            propVal = resolveSystemProperty(holder);
        }
        if (propVal == null) {
            propVal = resolveHolder(holder, sub, defaultValue);
        }
        return propVal;
    }

    /**
     * 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 是否忽略没有的占位符号
     * @return 进行值填充后的字符串
     */
    public static String parse(String strVal, boolean ignoreBadHolders) {
        Set visitedHolders = new HashSet();
        return parse(strVal, null, visitedHolders, ignoreBadHolders);
    }

    /**
     * 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 props            the Properties to resolve Holders against
     * @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.
     * @return 进行值填充后的字符串
     */
    public static String parse(String strVal, Object props, Set visitedHolders,
                               boolean ignoreBadHolders) {

        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 keyValue.
                holder = parse(holder, props, visitedHolders, ignoreBadHolders);
                // Now obtain the value for the fully resolved keyValue...
                String propVal = resolveHolder(holder, props, SYS_PROPS_MODE_FALLBACK, defValue);
                if (propVal != null) {
                    // Recursive invocation, parsing Holders contained in the
                    // previously resolved Holder value.
                    propVal = parse(propVal, props, visitedHolders, ignoreBadHolders);
                    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 holder the Holder to resolve * @param props the merged properties of this configurer * @param sysPropsMode the system properties mode, * according to the constants in this class * @return the resolved value, of null if none * @see System#getProperty */ private static String resolveHolder(String holder, Object props, int sysPropsMode, String defaultValue) { String propVal = null; if (sysPropsMode == SYS_PROPS_MODE_OVERRIDE) { propVal = resolveSystemProperty(holder); } if (propVal == null) { propVal = resolveHolder(holder, props, defaultValue); } 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 keyValue. *

Subclasses can override this for customized Holder-to-keyValue 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 holder the Holder to resolve * @param props the merged properties of this configurer * @param defaultValue 默认值 * @return the resolved value, of null if none */ protected static String resolveHolder(String holder, Object props, String defaultValue) { if (props != null) { Object value = ognlGetValue(holder, props); // Object value = props.get(holder); if (value != null) { return "" + value; } else if (defaultValue != null) { return defaultValue; } } return defaultValue; } private static Object ognlGetValue(String holder, Object props) { try { return Ognl.getValue(holder, props); } catch (OgnlException e) { log.debug("", e); } return null; } /** * Resolve the given keyValue 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 keyValue * @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