blob: 68676cf9b5233c62ec84dd5b6bcc1eaec970b7c3 [file] [log] [blame]
// 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.Collections;
import java.util.HashMap;
import java.util.List;
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(final 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(final Constant c) {
pattern = c.text;
rawPattern = c.text;
patternOps = Collections.<Format> singletonList(c);
parameters = Collections.emptyList();
}
public ParameterizedString(final String pattern) {
final StringBuilder raw = new StringBuilder();
final List<Parameter> prs = new ArrayList<Parameter>(4);
final List<Format> ops = new ArrayList<Format>(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.substring(i, b));
ops.add(new Constant(pattern.substring(i, b)));
String expr = pattern.substring(b + 2, e);
String parameterName = "";
List<Function> functions = new ArrayList<Function>();
if (!expr.contains(".")) {
parameterName = expr;
} else {
int firstDot = expr.indexOf('.');
parameterName = expr.substring(0, firstDot);
String actionsStr = expr.substring(firstDot + 1);
String[] actions = actionsStr.split("\\.");
for (String action : actions) {
Function function = FUNCTIONS.get(action);
if (function == null) {
function = NOOP;
}
functions.add(function);
}
}
final Parameter p =
new Parameter(parameterName, Collections.unmodifiableList(functions));
raw.append("{" + prs.size() + "}");
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<String>(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(final 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(final Map<String, String> params) {
final StringBuilder r = new StringBuilder();
for (final Format f : patternOps) {
f.format(r, params);
}
return r.toString();
}
public Builder replace(final String name, final 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<String, String>();
public Builder replace(final String name, final String value) {
params.put(name, value);
return this;
}
@Override
public String toString() {
return ParameterizedString.this.replace(params);
}
}
private static abstract class Format {
abstract void format(StringBuilder b, Map<String, String> p);
}
private static class Constant extends Format {
private final String text;
Constant(final 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(final String name, final List<Function> functions) {
this.name = name;
this.functions = functions;
}
@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 static abstract class Function {
abstract String apply(String a);
}
private static final Map<String, Function> FUNCTIONS = initFunctions();
private static final Function NOOP = new Function() {
@Override
String apply(String a) {
return a;
}
};
private static Map<String, Function> initFunctions() {
final HashMap<String, Function> m = new HashMap<String, Function>();
m.put("toLowerCase", new Function() {
@Override
String apply(String a) {
return a.toLowerCase();
}
});
m.put("toUpperCase", new Function() {
@Override
String apply(String a) {
return a.toUpperCase();
}
});
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);
}
}