org.glowroot.advicegen.MessageTemplate Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2013-2015 the original author or authors.
*
* 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 org.glowroot.advicegen;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.glowroot.shaded.google.common.base.MoreObjects;
import org.glowroot.shaded.google.common.collect.ImmutableList;
import org.glowroot.shaded.google.common.collect.Lists;
import org.glowroot.shaded.slf4j.Logger;
import org.glowroot.shaded.slf4j.LoggerFactory;
import org.glowroot.markers.UsedByGeneratedBytecode;
import static org.glowroot.shaded.google.common.base.Preconditions.checkNotNull;
@UsedByGeneratedBytecode
public class MessageTemplate {
private static final Logger logger = LoggerFactory.getLogger(MessageTemplate.class);
private static final Pattern pattern = Pattern.compile("\\{\\{([^}]*)}}");
private final ImmutableList allParts;
private final ImmutableList thisPathParts;
private final ImmutableList argPathParts;
private final ImmutableList returnPathParts;
@UsedByGeneratedBytecode
public static MessageTemplate create(String template, Class> declaringClass,
Class> returnType, Class>[] parameterTypes) {
List allParts = Lists.newArrayList();
List thisPathParts = Lists.newArrayList();
List argPathParts = Lists.newArrayList();
List returnPathParts = Lists.newArrayList();
Matcher matcher = pattern.matcher(template);
int curr = 0;
while (matcher.find()) {
if (matcher.start() > curr) {
allParts.add(new ConstantPart(template.substring(curr, matcher.start())));
}
String group = matcher.group(1);
checkNotNull(group);
String path = group.trim();
int index = path.indexOf('.');
String base;
String remaining;
if (index == -1) {
base = path;
remaining = "";
} else {
base = path.substring(0, index);
remaining = path.substring(index + 1);
}
if (base.equals("this")) {
ValuePathPart part =
new ValuePathPart(PartType.THIS_PATH, declaringClass, remaining);
allParts.add(part);
thisPathParts.add(part);
} else if (base.matches("[0-9]+")) {
int argNumber = Integer.parseInt(base);
if (argNumber < parameterTypes.length) {
ArgPathPart part =
new ArgPathPart(parameterTypes[argNumber], remaining, argNumber);
allParts.add(part);
argPathParts.add(part);
} else {
allParts.add(new ConstantPart(
""));
}
} else if (base.equals("_")) {
ValuePathPart part = new ValuePathPart(PartType.RETURN_PATH, returnType, remaining);
allParts.add(part);
returnPathParts.add(part);
} else if (base.equals("methodName")) {
allParts.add(new Part(PartType.METHOD_NAME));
} else {
logger.warn("invalid template substitution: {}", path);
allParts.add(new ConstantPart("{{" + path + "}}"));
}
curr = matcher.end();
}
if (curr < template.length()) {
allParts.add(new ConstantPart(template.substring(curr)));
}
return new MessageTemplate(allParts, thisPathParts, argPathParts, returnPathParts);
}
private MessageTemplate(List allParts, List thisPathParts,
List argPathParts, List returnPathParts) {
this.allParts = ImmutableList.copyOf(allParts);
this.thisPathParts = ImmutableList.copyOf(thisPathParts);
this.argPathParts = ImmutableList.copyOf(argPathParts);
this.returnPathParts = ImmutableList.copyOf(returnPathParts);
}
ImmutableList getAllParts() {
return allParts;
}
ImmutableList getThisPathParts() {
return thisPathParts;
}
ImmutableList getArgPathParts() {
return argPathParts;
}
ImmutableList getReturnPathParts() {
return returnPathParts;
}
enum PartType {
CONSTANT, THIS_PATH, ARG_PATH, RETURN_PATH, METHOD_NAME;
}
static class Part {
private final PartType type;
private Part(PartType type) {
this.type = type;
}
PartType getType() {
return type;
}
}
static class ConstantPart extends Part {
private final String constant;
private ConstantPart(String constant) {
super(PartType.CONSTANT);
this.constant = constant;
}
String getConstant() {
return constant;
}
}
static class ValuePathPart extends Part {
private final PathEvaluator pathEvaluator;
private ValuePathPart(PartType partType, Class> valueClass, String propertyPath) {
super(partType);
this.pathEvaluator = new PathEvaluator(valueClass, propertyPath);
}
String evaluatePart(@Nullable Object base) {
if (base == null) {
// this is same as String.valueOf((Object) null);
return "null";
}
try {
return valueOf(pathEvaluator.evaluateOnBase(base));
} catch (InvocationTargetException e) {
logger.debug(e.getMessage(), e);
// InvocationTargetException has the problem of obscuring the original message
// to try to use cause
Throwable t = MoreObjects.firstNonNull(e.getCause(), e);
// using toString() instead of getMessage() in order to capture exception class name
return "";
} catch (Exception e) {
logger.warn(e.getMessage(), e);
// using toString() instead of getMessage() in order to capture exception class name
return "";
}
}
private String valueOf(@Nullable Object value) {
if (value == null || !value.getClass().isArray()) {
// shortcut the common case
return String.valueOf(value);
}
StringBuilder sb = new StringBuilder();
valueOfArray(value, sb);
return sb.toString();
}
private static void valueOfArray(Object array, StringBuilder sb) {
sb.append('[');
int len = Array.getLength(array);
for (int i = 0; i < len; i++) {
if (i != 0) {
sb.append(", ");
}
valueOf(Array.get(array, i), sb);
}
sb.append(']');
}
private static void valueOf(Object object, StringBuilder sb) {
if (object.getClass().isArray()) {
valueOfArray(object, sb);
} else {
sb.append(String.valueOf(object));
}
}
}
static class ArgPathPart extends ValuePathPart {
private final int argNumber;
private ArgPathPart(Class> argClass, String propertyPath, int argNumber) {
super(PartType.ARG_PATH, argClass, propertyPath);
this.argNumber = argNumber;
}
int getArgNumber() {
return argNumber;
}
}
}