org.apache.cocoon.util.log.ExtensiblePatternFormatter.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.cocoon.util.log.ExtensiblePatternFormatter.java

Source

/*
 * Copyright 1999-2004 The Apache Software Foundation.
 * 
 * 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.apache.cocoon.util.log;

import java.io.StringWriter;
import java.util.Stack;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.log.LogEvent;
import org.apache.log.Priority;
import org.apache.log.format.Formatter;
import org.apache.log.util.DefaultErrorHandler;

/**
 * A refactoring of <code>org.apache.log.format.PatternFormatter</code> that
 * can be extended.
 * This formater formats the LogEntries according to a input pattern string.
 *
 * The format of each pattern element can be %[+|-]#.#{field:subformat}
 *
 * The +|- indicates left or right justify.
 * The #.# indicates the minimum and maximum size of output.
 * 'field' indicates which field is to be output and must be one of
 *  properties of LogEvent
 * 'subformat' indicates a particular subformat and is currently unused.
 *
 * @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
 * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
 * @version CVS $Id: ExtensiblePatternFormatter.java 30932 2004-07-29 17:35:38Z vgritsenko $
 */
public class ExtensiblePatternFormatter implements Formatter {
    protected final static int TYPE_TEXT = 1;
    protected final static int TYPE_CATEGORY = 2;
    protected final static int TYPE_MESSAGE = 4;
    protected final static int TYPE_TIME = 5;
    protected final static int TYPE_RELATIVE_TIME = 6;
    protected final static int TYPE_THROWABLE = 7;
    protected final static int TYPE_PRIORITY = 8;

    /**
     * The maximum value used for TYPEs. Subclasses can define their own TYPEs
     * starting at <code>MAX_TYPE + 1</code>.
     */
    protected final static int MAX_TYPE = 8;

    protected final static String TYPE_CATEGORY_STR = "category";
    protected final static String TYPE_MESSAGE_STR = "message";
    protected final static String TYPE_TIME_STR = "time";
    protected final static String TYPE_RELATIVE_TIME_STR = "rtime";
    protected final static String TYPE_THROWABLE_STR = "throwable";
    protected final static String TYPE_PRIORITY_STR = "priority";

    protected static class PatternRun {
        public String m_data;
        public boolean m_rightJustify;
        public int m_minSize;
        public int m_maxSize;
        public int m_type;
        public String m_format;
    }

    protected PatternRun m_formatSpecification[];

    /**
     * Extract and build a pattern from input string.
     *
     * @param stack the stack on which to place patterns
     * @param pattern the input string
     * @param index the start of pattern run
     * @return the number of characters in pattern run
     */
    protected int addPatternRun(final Stack stack, final char pattern[], int index) {
        final PatternRun run = new PatternRun();
        final int start = index++;

        // first check for a +|- sign
        if ('+' == pattern[index]) {
            index++;
        } else if ('-' == pattern[index]) {
            run.m_rightJustify = true;
            index++;
        }

        if (Character.isDigit(pattern[index])) {
            int total = 0;
            while (Character.isDigit(pattern[index])) {
                total = total * 10 + (pattern[index] - '0');
                index++;
            }
            run.m_minSize = total;
        }

        //check for . sign indicating a maximum is to follow
        if (index < pattern.length && '.' == pattern[index]) {
            index++;
            if (Character.isDigit(pattern[index])) {
                int total = 0;
                while (Character.isDigit(pattern[index])) {
                    total = total * 10 + (pattern[index] - '0');
                    index++;
                }
                run.m_maxSize = total;
            }
        }

        if (index >= pattern.length || '{' != pattern[index]) {
            throw new IllegalArgumentException("Badly formed pattern at character " + index);
        }

        int typeStart = index;

        while (index < pattern.length && pattern[index] != ':' && pattern[index] != '}') {
            index++;
        }

        int typeEnd = index - 1;

        final String type = new String(pattern, typeStart + 1, typeEnd - typeStart);

        run.m_type = getTypeIdFor(type);

        if (index < pattern.length && pattern[index] == ':') {
            index++;
            while (index < pattern.length && pattern[index] != '}') {
                index++;
            }
            final int length = index - typeEnd - 2;

            if (0 != length) {
                run.m_format = new String(pattern, typeEnd + 2, length);
            }
        }

        if (index >= pattern.length || '}' != pattern[index]) {
            throw new IllegalArgumentException("Unterminated type in pattern at character " + index);
        }
        index++;
        stack.push(run);
        return index - start;
    }

    /**
     * Extract and build a text run  from input string.
     * It does special handling of '\n' and '\t' replaceing
     * them with newline and tab.
     *
     * @param stack the stack on which to place runs
     * @param pattern the input string
     * @param index the start of the text run
     * @return the number of characters in run
     */
    protected int addTextRun(final Stack stack, final char pattern[], int index) {
        final PatternRun run = new PatternRun();
        final int start = index;
        boolean escapeMode = false;

        if ('%' == pattern[index]) {
            index++;
        }
        final StringBuffer sb = new StringBuffer();
        while (index < pattern.length && pattern[index] != '%') {
            if (escapeMode) {
                if ('n' == pattern[index]) {
                    sb.append(SystemUtils.LINE_SEPARATOR);
                } else if ('t' == pattern[index]) {
                    sb.append('\t');
                } else {
                    sb.append(pattern[index]);
                }
                escapeMode = false;
            } else if ('\\' == pattern[index]) {
                escapeMode = true;
            } else {
                sb.append(pattern[index]);
            }
            index++;
        }
        run.m_data = sb.toString();
        run.m_type = TYPE_TEXT;
        stack.push(run);
        return index - start;
    }

    /**
     * Utility to append a string to buffer given certain constraints.
     *
     * @param sb the StringBuffer
     * @param minSize the minimum size of output (0 to ignore)
     * @param maxSize the maximum size of output (0 to ignore)
     * @param rightJustify true if the string is to be right justified in it's box.
     * @param output the input string
     */
    protected void append(final StringBuffer sb, final int minSize, final int maxSize, final boolean rightJustify,
            final String output) {
        if (output.length() < minSize) {
            if (rightJustify) {
                sb.append(StringUtils.leftPad(output, minSize));
            } else {
                sb.append(StringUtils.rightPad(output, minSize));
            }
        } else if (maxSize > 0) {
            if (rightJustify) {
                sb.append(StringUtils.right(output, maxSize));
            } else {
                sb.append(StringUtils.left(output, maxSize));
            }
        } else {
            sb.append(output);
        }
    }

    /**
     * Format the event according to the pattern.
     *
     * @param event the event
     * @return the formatted output
     */
    public String format(final LogEvent event) {
        final StringBuffer sb = new StringBuffer();

        for (int i = 0; i < m_formatSpecification.length; i++) {
            final PatternRun run = m_formatSpecification[i];
            //treat text differently as it doesn't need min/max padding
            if (run.m_type == TYPE_TEXT) {
                sb.append(run.m_data);
            } else {
                final String data = formatPatternRun(event, run);
                if (null != data) {
                    append(sb, run.m_minSize, run.m_maxSize, run.m_rightJustify, data);
                }
            }
        }
        return sb.toString();
    }

    /**
     * Formats a single pattern run (can be extended in subclasses).
     *
     * @param  run the pattern run to format.
     * @return the formatted result.
     */
    protected String formatPatternRun(final LogEvent event, final PatternRun run) {
        String str = null;

        switch (run.m_type) {
        case TYPE_RELATIVE_TIME:
            str = getTime(event.getRelativeTime(), run.m_format);
            break;
        case TYPE_TIME:
            str = getTime(event.getTime(), run.m_format);
            break;
        case TYPE_THROWABLE:
            str = getStackTrace(event.getThrowable(), run.m_format);
            break;
        case TYPE_MESSAGE:
            str = getMessage(event.getMessage(), run.m_format);
            break;
        case TYPE_CATEGORY:
            str = getCategory(event.getCategory(), run.m_format);
            break;
        case TYPE_PRIORITY:
            str = getPriority(event.getPriority(), run.m_format);
            break;
        default:
            new DefaultErrorHandler().error("Unknown Pattern specification." + run.m_type, null, null);
        }
        return str;
    }

    /**
     * Utility method to format category.
     *
     * @param category the category string
     * @param format ancilliary format parameter - allowed to be null
     * @return the formatted string
     */
    protected String getCategory(final String category, final String format) {
        return category;
    }

    /**
     * Get formatted priority string.
     */
    protected String getPriority(final Priority priority, final String format) {
        return priority.getName();
    }

    /**
     * Correct a context string by replacing '.''s with a '_'.
     *
     * @param context the un-fixed context
     * @return the fixed context
     */
    protected final String fix(final String context) {
        return context.replace('.', '_');
    }

    /**
     * Utility method to format message.
     *
     * @param message the message string
     * @param format ancilliary format parameter - allowed to be null
     * @return the formatted string
     */
    protected String getMessage(final String message, final String format) {
        return message;
    }

    /**
     * Utility method to format stack trace.
     *
     * @param throwable the throwable instance
     * @param format ancilliary format parameter - allowed to be null
     * @return the formatted string
     */
    protected String getStackTrace(final Throwable throwable, final String format) {
        if (null != throwable) {
            final StringWriter sw = new StringWriter();
            throwable.printStackTrace(new java.io.PrintWriter(sw));
            return sw.toString();
        }
        return "";
    }

    /**
     * Utility method to format time.
     *
     * @param time the time
     * @param format ancilliary format parameter - allowed to be null
     * @return the formatted string
     */
    protected String getTime(final long time, final String format) {
        return Long.toString(time);
    }

    /**
     * Retrieve the type-id for a particular string.
     *
     * @param type the string
     * @return the type-id
     */
    protected int getTypeIdFor(final String type) {
        if (type.equalsIgnoreCase(TYPE_CATEGORY_STR)) {
            return TYPE_CATEGORY;
        } else if (type.equalsIgnoreCase(TYPE_MESSAGE_STR)) {
            return TYPE_MESSAGE;
        } else if (type.equalsIgnoreCase(TYPE_PRIORITY_STR)) {
            return TYPE_PRIORITY;
        } else if (type.equalsIgnoreCase(TYPE_TIME_STR)) {
            return TYPE_TIME;
        } else if (type.equalsIgnoreCase(TYPE_RELATIVE_TIME_STR)) {
            return TYPE_RELATIVE_TIME;
        } else if (type.equalsIgnoreCase(TYPE_THROWABLE_STR)) {
            return TYPE_THROWABLE;
        } else {
            throw new IllegalArgumentException("Unknown Type in pattern - " + type);
        }
    }

    /**
     * Parse the input pattern and build internal data structures.
     *
     * @param patternString the pattern
     */
    protected void parse(final String patternString) {
        final Stack stack = new Stack();
        final int size = patternString.length();
        final char pattern[] = new char[size];
        int index = 0;

        patternString.getChars(0, size, pattern, 0);
        while (index < size) {
            if (pattern[index] == '%' && !(index != size - 1 && pattern[index + 1] == '%')) {
                index += addPatternRun(stack, pattern, index);
            } else {
                index += addTextRun(stack, pattern, index);
            }
        }
        final int elementCount = stack.size();
        m_formatSpecification = new PatternRun[elementCount];

        for (int i = 0; i < elementCount; i++) {
            m_formatSpecification[i] = (PatternRun) stack.elementAt(i);
        }
    }

    /**
     * Set the string description that the format is extracted from.
     *
     * @param format the string format
     */
    public void setFormat(final String format) {
        parse(format);
    }
}