com.allen_sauer.gwt.log.rebind.LogMessageFormatterGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.allen_sauer.gwt.log.rebind.LogMessageFormatterGenerator.java

Source

/*
 * 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.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

import com.allen_sauer.gwt.log.client.LogUtil;

import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Generator for {@link com.allen_sauer.gwt.log.client.LogMessageFormatter}.
 */
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 {
        // %c - category
        CONVERSION_MAP.put("c", "category");

        // %C - fully qualified class name of caller
        CONVERSION_MAP.put("C", "ste == null ? \"-\" : ste.getClassName()");
        STACKTRACE_SET.add("C");

        // %d - 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()");

        // %F - filename
        CONVERSION_MAP.put("F", "ste == null ? \"-\" : ste.getFileName()");
        STACKTRACE_SET.add("F");

        // %l - location information of caller
        CONVERSION_MAP.put("l", "ste == null ? \"-\" : ste.toString()");
        STACKTRACE_SET.add("l");

        // %L - line number of caller
        CONVERSION_MAP.put("L", "ste == null ? \"-\" : ste.getLineNumber()");
        STACKTRACE_SET.add("L");

        // %m - application supplied message
        CONVERSION_MAP.put("m", "message");

        // %M - method name of caller
        CONVERSION_MAP.put("M", "ste == null ? \"-\" : ste.getMethodName()");
        STACKTRACE_SET.add("M");

        // %n - platform dependent line separator
        CONVERSION_MAP.put("n", "\"\\\\n\"");

        // %p - priority of logging event
        CONVERSION_MAP.put("p", "logLevelText");

        // %r - number of elapsed milliseconds
        CONVERSION_MAP.put("r", "Math.round(Duration.currentTimeMillis() - BIG_BANG)");

        // %t - name of caller thread
        CONVERSION_MAP.put("t", "\"-\"");

        // %x - nested diagnostic context
        CONVERSION_MAP.put("x", "\"-\"");

        // %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("\"\"");
        // Regex breakdown
        // 1. (.*?) - Non pattern characters
        // -. % - Escape character ("%")
        // 2. (-?) - Optional leading dash ("-")
        // 3. (\\d*) - Zero or more digits
        // -. \\.? - Optional period (".")
        // 4. (\\d*) - Zero or more digits
        // 5. ([cCdFlLmMnprtxX%]) - Conversion character
        // 6. (\\{ . . . \\})? - "{" + . . . + "}"
        // 7. ([^\\}]+) - Format specifier: one or more characters, but not "}"
        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 (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
                            + "\")";
                } else if (conversionSpecifier.equals("c") || conversionSpecifier.equals("C")) {
                    if (formatSpecifier != null) {
                        int precision = Integer.parseInt("0" + formatSpecifier);
                        convertedExpression = "LogUtil.formatCategory(" + convertedExpression + ", " + precision
                                + ")";
                    }
                }

                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 + ")";
                }

                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(throwable)";
        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 category, String message, Throwable throwable) {");
            sw.indent();
            sw.println("if (category == null) {");
            sw.indent();
            sw.println("category = \"<null category>\";");
            sw.outdent();
            sw.println("}");
            sw.println("if (message == null) {");
            sw.indent();
            sw.println("message = \"<null message>\";");
            sw.outdent();
            sw.println("}");
            sw.println(logPatternToCode(logPattern));
            sw.outdent();
            sw.println("}");

            sw.commit(logger);
        }
        return composerFactory.getCreatedClassName();
    }
}