| // Copyright (C) 2009 The Android Open Source Project | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package com.google.gerrit.common.data; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.Collections; | |
| import java.util.HashMap; | |
| import java.util.List; | |
| import java.util.Locale; | |
| import java.util.Map; | |
| /** Performs replacements on strings such as <code>Hello ${user}</code>. */ | |
| public class ParameterizedString { | |
| /** Obtain a string which has no parameters and always produces the value. */ | |
| public static ParameterizedString asis(String constant) { | |
| return new ParameterizedString(new Constant(constant)); | |
| } | |
| private final String pattern; | |
| private final String rawPattern; | |
| private final List<Format> patternOps; | |
| private final List<Parameter> parameters; | |
| protected ParameterizedString() { | |
| this(new Constant("")); | |
| } | |
| private ParameterizedString(Constant c) { | |
| pattern = c.text; | |
| rawPattern = c.text; | |
| patternOps = Collections.singletonList(c); | |
| parameters = Collections.emptyList(); | |
| } | |
| public ParameterizedString(String pattern) { | |
| final StringBuilder raw = new StringBuilder(); | |
| final List<Parameter> prs = new ArrayList<>(4); | |
| final List<Format> ops = new ArrayList<>(4); | |
| int i = 0; | |
| while (i < pattern.length()) { | |
| final int b = pattern.indexOf("${", i); | |
| if (b < 0) { | |
| break; | |
| } | |
| final int e = pattern.indexOf("}", b + 2); | |
| if (e < 0) { | |
| break; | |
| } | |
| raw.append(pattern, i, b); | |
| ops.add(new Constant(pattern.substring(i, b))); | |
| // "${parameter[.functions...]}" -> "parameter[.functions...]" | |
| final Parameter p = new Parameter(pattern.substring(b + 2, e)); | |
| raw.append("{").append(prs.size()).append("}"); | |
| prs.add(p); | |
| ops.add(p); | |
| i = e + 1; | |
| } | |
| if (i < pattern.length()) { | |
| raw.append(pattern.substring(i)); | |
| ops.add(new Constant(pattern.substring(i))); | |
| } | |
| this.pattern = pattern; | |
| this.rawPattern = raw.toString(); | |
| this.patternOps = Collections.unmodifiableList(ops); | |
| this.parameters = Collections.unmodifiableList(prs); | |
| } | |
| /** Get the original pattern given to the constructor. */ | |
| public String getPattern() { | |
| return pattern; | |
| } | |
| /** Get the pattern with variables replaced with {0}, {1}, ... */ | |
| public String getRawPattern() { | |
| return rawPattern; | |
| } | |
| /** Get the list of parameter names, ordered by appearance in the pattern. */ | |
| public List<String> getParameterNames() { | |
| final ArrayList<String> r = new ArrayList<>(parameters.size()); | |
| for (Parameter p : parameters) { | |
| r.add(p.name); | |
| } | |
| return Collections.unmodifiableList(r); | |
| } | |
| /** Convert a map of parameters into a value array for binding. */ | |
| public String[] bind(Map<String, String> params) { | |
| final String[] r = new String[parameters.size()]; | |
| for (int i = 0; i < r.length; i++) { | |
| final StringBuilder b = new StringBuilder(); | |
| parameters.get(i).format(b, params); | |
| r[i] = b.toString(); | |
| } | |
| return r; | |
| } | |
| /** Format this string by performing the variable replacements. */ | |
| public String replace(Map<String, String> params) { | |
| final StringBuilder r = new StringBuilder(); | |
| for (Format f : patternOps) { | |
| f.format(r, params); | |
| } | |
| return r.toString(); | |
| } | |
| public Builder replace(String name, String value) { | |
| return new Builder().replace(name, value); | |
| } | |
| @Override | |
| public String toString() { | |
| return getPattern(); | |
| } | |
| public final class Builder { | |
| private final Map<String, String> params = new HashMap<>(); | |
| public Builder replace(String name, String value) { | |
| params.put(name, value); | |
| return this; | |
| } | |
| @Override | |
| public String toString() { | |
| return ParameterizedString.this.replace(params); | |
| } | |
| } | |
| private abstract static class Format { | |
| abstract void format(StringBuilder b, Map<String, String> p); | |
| } | |
| private static class Constant extends Format { | |
| private final String text; | |
| Constant(String text) { | |
| this.text = text; | |
| } | |
| @Override | |
| void format(StringBuilder b, Map<String, String> p) { | |
| b.append(text); | |
| } | |
| } | |
| private static class Parameter extends Format { | |
| private final String name; | |
| private final List<Function> functions; | |
| Parameter(String parameter) { | |
| // "parameter[.functions...]" -> (parameter, functions...) | |
| final List<String> names = Arrays.asList(parameter.split("\\.")); | |
| final List<Function> functs = new ArrayList<>(names.size()); | |
| if (names.isEmpty()) { | |
| name = ""; | |
| } else { | |
| name = names.get(0); | |
| for (String fname : names.subList(1, names.size())) { | |
| final Function function = FUNCTIONS.get(fname); | |
| if (function != null) { | |
| functs.add(function); | |
| } | |
| } | |
| } | |
| functions = Collections.unmodifiableList(functs); | |
| } | |
| @Override | |
| void format(StringBuilder b, Map<String, String> p) { | |
| String v = p.get(name); | |
| if (v == null) { | |
| v = ""; | |
| } | |
| for (Function function : functions) { | |
| v = function.apply(v); | |
| } | |
| b.append(v); | |
| } | |
| } | |
| private abstract static class Function { | |
| abstract String apply(String a); | |
| } | |
| private static final Map<String, Function> FUNCTIONS = initFunctions(); | |
| private static Map<String, Function> initFunctions() { | |
| HashMap<String, Function> m = new HashMap<>(); | |
| m.put( | |
| "toLowerCase", | |
| new Function() { | |
| @Override | |
| String apply(String a) { | |
| return a.toLowerCase(Locale.US); | |
| } | |
| }); | |
| m.put( | |
| "toUpperCase", | |
| new Function() { | |
| @Override | |
| String apply(String a) { | |
| return a.toUpperCase(Locale.US); | |
| } | |
| }); | |
| m.put( | |
| "localPart", | |
| new Function() { | |
| @Override | |
| String apply(String a) { | |
| int at = a.indexOf('@'); | |
| return at < 0 ? a : a.substring(0, at); | |
| } | |
| }); | |
| return Collections.unmodifiableMap(m); | |
| } | |
| } |