class Settings implements Serializable {

    void substituteVariables(Properties p) {
        Preconditions.checkNotNull(p);
        for (Map.Entry<Object, Object> entry : p.entrySet()) {
            String key = entry.getKey().toString();
            String value = entry.getValue().toString();
            StringBuilder b = new StringBuilder();
            int pos, prev = 0, end = 0;
            if (value.matches(".*(\\$\\{(\\s*\\w.+\\s*)\\}).*")) {
                // search for the next instance of $ from the 'prev' position
                while ((pos = value.indexOf('$', prev)) >= 0) {
                    if (value.charAt(pos + 1) != '{') {
                        prev = pos + 1;
                    } else {
                        if (pos > prev) {
                            prev = value.charAt(prev) == '}' ? prev + 1 : prev;
                            b.append(value.substring(prev, pos));
                        }
                        end = value.indexOf('}', pos);
                        if (end == -1) {
                            throw new IllegalStateException(
                                    "can't substitute value in " + value
                                            + " (there is no ending '}')"
                            );
                        }
                        String variable = value.substring(pos + 2, end);
                        String substitutedValue = p.getProperty(variable);
                        if (!Strings.isNullOrEmpty(substitutedValue)) {
                            log.debug(String.format(
                                    "resolved variable %s to value %s from same settings",
                                    variable, substitutedValue));
                        } else {
                            substitutedValue = properties.getProperty(variable);
                            if (!Strings.isNullOrEmpty(substitutedValue)) {
                                log.debug(String.format(
                                        "resolved variable %s to value %s from previously loaded settings",
                                        variable, substitutedValue));
                            } else {
                                substitutedValue = System.getProperty(variable);
                                if (!Strings.isNullOrEmpty(substitutedValue)) {
                                    log.debug(String.format(
                                            "resolved variable %s to value %s from system properties",
                                            variable, substitutedValue));
                                } else {
                                    String msg = String.format(
                                            "unable to substitute variable %s for property %s (value = %s)",
                                            variable, key, value);
                                    log.warn(msg);
                                    substitutedValue = "${" + variable + "}"; // just keep it as-is
                                }
                            }
                        }
                        b.append(substitutedValue);
                        prev = end;
                    }
                }
            }
            if (b.length() != 0) {
                if (value.length() > end + 1) {
                    b.append(value.substring(end + 1));
                }
                p.setProperty(key, b.toString());
            }
        }
    }

      Settings();

}