/*
* Copyright 2009 Fred Sauer
*
* 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.allen_sauer.gwt.log.rebind;
import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.BadPropertyValueException;
import com.google.gwt.core.ext.ConfigurationProperty;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.util.collect.HashSet;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.allen_sauer.gwt.log.client.util.LogUtil;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Generator for LogMessageFormatter interface.
*/
public class LogMessageFormatterGenerator extends Generator {
// CHECKSTYLE_JAVADOC_OFF
private static final HashMap<String, String> CONVERSION_MAP = new HashMap<String, String>();
private static final HashMap<String, String> DATE_FORMAT_MAP = new HashMap<String, String>();
private static final String ISO8601 = "ISO8601";
private static final String PROPERTY_LOG_PATTERN = "log_pattern";
private static Set<String> STACKTRACE_SET = new HashSet<String>();
static {
DATE_FORMAT_MAP.put("ABSOLUTE", "HH:mm:ss,SSS");
DATE_FORMAT_MAP.put("DATE", "dd MMM yyyy HH:mm:ss,SSS");
DATE_FORMAT_MAP.put(ISO8601, "yyyy-MM-dd HH:mm:ss,SSS");
}
static {
// category
CONVERSION_MAP.put("c", "\"-\"");
// fully qualified class name of caller
CONVERSION_MAP.put("C", "ste == null ? \"-\" : ste.getClassName()");
STACKTRACE_SET.add("C");
// date of logging event (default ISO8601), e.g. %d{dd MMM yyyy HH:mm:ss,SSS}, %d{ISO8601} or
// %d{ABSOLUTE}
CONVERSION_MAP.put("d", "new Date()");
CONVERSION_MAP.put("F", "ste == null ? \"-\" : ste.getFileName()");
STACKTRACE_SET.add("F");
// location information of caller
CONVERSION_MAP.put("l", "ste == null ? \"-\" : ste.toString()");
STACKTRACE_SET.add("l");
// line number of caller
CONVERSION_MAP.put("L", "ste == null ? \"-\" : ste.getLineNumber()");
STACKTRACE_SET.add("L");
// application supplied message
CONVERSION_MAP.put("m", "message");
// method name of caller
CONVERSION_MAP.put("M", "ste == null ? \"-\" : ste.getMethodName()");
STACKTRACE_SET.add("M");
// platform dependent line separator
CONVERSION_MAP.put("n", "\"\\\\n\"");
// priority of logging event
CONVERSION_MAP.put("p", "logLevelText");
// number of elapsed milliseconds
CONVERSION_MAP.put("r", "Math.round(Duration.currentTimeMillis() - BIG_BANG)");
// name of caller thread
CONVERSION_MAP.put("t", "\"-\"");
// nested diagnostic context
CONVERSION_MAP.put("x", "\"-\"");
// mapped diagnostic context, e.g. %X{someKey}
CONVERSION_MAP.put("X", "\"-\"");
// percent sign
CONVERSION_MAP.put("%", "\"%\"");
}
/**
* Convert a log pattern to source code to be used in generated code.
*
* @param logPattern the log pattern to convert
* @return source code which will format the supplied message at runtime
*/
private static String logPatternToCode(String logPattern) {
StringBuffer buf = new StringBuffer("\"\"");
Pattern pattern = Pattern.compile("(.*?)%(-?)(\\d*)\\.?(\\d*)([cCdFlLmMnprtxX%])(\\{([^\\}]+)\\})?");
Matcher matcher = pattern.matcher(logPattern);
boolean stackTraceToggle = false;
while (matcher.find()) {
buf.append("\n + \"").append(matcher.group(1)).append("\"");
int minFieldWidth = Integer.parseInt(matcher.group(2) + "0" + matcher.group(3));
int maxFieldWidth = Integer.parseInt("0" + matcher.group(4));
String conversionSpecifier = matcher.group(5);
String formatSpecifier = matcher.group(7);
String convertedExpression = CONVERSION_MAP.get(conversionSpecifier);
if (STACKTRACE_SET.contains(conversionSpecifier)) {
stackTraceToggle = true;
}
String group2ToEnd = matcher.group(0).substring(matcher.group(1).length());
if (convertedExpression == null) {
buf.append("\n + \"");
matcher.appendReplacement(buf, group2ToEnd);
buf.append("\" // \"").append(group2ToEnd).append("\"");
} else {
if (minFieldWidth > 0) {
// right justify
convertedExpression = "LogUtil.padLeft(" + convertedExpression + ", " + minFieldWidth
+ ")";
} else if (minFieldWidth < 0) {
// left justify
convertedExpression = "LogUtil.padRight(" + convertedExpression + ", " + -minFieldWidth
+ ")";
}
if (maxFieldWidth > 0) {
convertedExpression = "LogUtil.trim(" + convertedExpression + ", " + maxFieldWidth + ")";
}
// Handle date formating
if (conversionSpecifier.equals("d")) {
if (formatSpecifier == null) {
formatSpecifier = ISO8601;
}
String newFormatSpecifier = DATE_FORMAT_MAP.get(formatSpecifier);
if (newFormatSpecifier != null) {
formatSpecifier = newFormatSpecifier;
}
convertedExpression = "LogUtil.formatDate(" + convertedExpression + ", \""
+ formatSpecifier + "\")";
}
buf.append("\n + (");
matcher.appendReplacement(buf, convertedExpression);
buf.append(") // \"").append(group2ToEnd).append("\"");
}
}
buf.append("\n + \"");
matcher.appendTail(buf);
buf.append("\"");
String ste = "GWT.isScript() ? null : LogUtil.getCallingStackTraceElement(5)";
return (stackTraceToggle ? "StackTraceElement ste = " + ste + ";\n"
: "") + "return "
+ buf.toString() + ";";
}
@Override
public String generate(TreeLogger logger, GeneratorContext context, String typeName)
throws UnableToCompleteException {
TypeOracle typeOracle = context.getTypeOracle();
JClassType userType;
try {
userType = typeOracle.getType(typeName);
} catch (NotFoundException e) {
logger.log(TreeLogger.ERROR, "OOPS", e);
throw new UnableToCompleteException();
}
String packageName = userType.getPackage().getName();
String className = userType.getName();
JClassType remoteService = typeOracle.findType(typeName);
if (remoteService == null) {
logger.log(TreeLogger.ERROR, "Unable to find metadata for type '" + typeName + "'", null);
throw new UnableToCompleteException();
}
if (remoteService.isInterface() == null) {
logger.log(TreeLogger.ERROR, remoteService.getQualifiedSourceName() + " is not an interface",
null);
throw new UnableToCompleteException();
}
ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(
packageName, className + "Impl");
composerFactory.addImplementedInterface(remoteService.getQualifiedSourceName());
composerFactory.addImport(Date.class.getName());
composerFactory.addImport(GWT.class.getName());
composerFactory.addImport(LogUtil.class.getName());
composerFactory.addImport(Duration.class.getName());
PrintWriter pw = context.tryCreate(logger, packageName, className + "Impl");
if (pw != null) {
SourceWriter sw = composerFactory.createSourceWriter(context, pw);
PropertyOracle propertyOracle = context.getPropertyOracle();
String logPattern;
try {
ConfigurationProperty logPatternProperty = propertyOracle.getConfigurationProperty(PROPERTY_LOG_PATTERN);
List<String> values = logPatternProperty.getValues();
logPattern = values.get(0);
} catch (BadPropertyValueException e) {
logger.log(TreeLogger.ERROR, "Unable to find value for '" + PROPERTY_LOG_PATTERN + "'", e);
throw new UnableToCompleteException();
}
sw.println();
sw.println("private double BIG_BANG = Duration.currentTimeMillis();");
sw.println();
sw.println("public String format(String logLevelText, String message) {");
sw.indent();
sw.println("if (message == null) {");
sw.indent();
sw.println("message = \"<null message>\";");
sw.outdent();
sw.println("}");
sw.println(logPatternToCode(logPattern));
sw.outdent();
sw.println("}\n");
sw.commit(logger);
}
return composerFactory.getCreatedClassName();
}
}