class StringUtil {

    public static String substituteWithProperties(String string0, String startStr, String endStr, Properties properties) throws SubstitutionException {
        // a null source string will always return the same -- a null result
        if (string0 == null) {
            return null;
        }

        // create a builder for the resulting string
        StringBuilder result = new StringBuilder(string0.length());

        // attempt to find the first occurrence of the starting string
        int end = -1;
        int pos = string0.indexOf(startStr);
        
        // keep looping while we keep finding more occurrences
        while (pos >= 0) {
            // is there string data before the position that we should append to the result?
            if (pos > end+1) {
                result.append(string0.substring(end+endStr.length(), pos));
            }

            // search for endStr starting from the end of the startStr
            end = string0.indexOf(endStr, pos+startStr.length());

            // was the end found?
            if (end < 0) {
                throw new SubstitutionException("End of substitution pattern '" + endStr + "' not found [@position=" + pos + "]");
            }

            // extract the part in the middle of the start and end strings
            String key = string0.substring(pos+startStr.length(), end);
            // NOTE: don't trim the key, whitespace technically matters...

            // was there anything left?
            if (key == null || key.equals("")) {
                throw new SubstitutionException("Property key was empty in string with an occurrence of '" + startStr + endStr + "' [@position=" + pos + "]");
            }

            // attempt to get this property
            String value = properties.getProperty(key);
            // was the property found
            if (value == null) {
                throw new SubstitutionException("A property value for '" + startStr + key + endStr + "' was not found (property missing?)");
            }

            // append this value to our result
            result.append(value);

            // find next occurrence after last end
            pos = string0.indexOf(startStr, end+endStr.length());
        }

        // is there any string data we missed in the loop above?
        if (end+endStr.length() < string0.length()) {
            // append the remaining part of the string
            result.append(string0.substring(end+endStr.length()));
        }

        return result.toString();
    }

}