All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.log4j.pattern.ExtrasPatternParser Maven / Gradle / Ivy

Go to download

The Pax Logging API Library is to allow for the Pax Logging Service to be reloaded without stopping the many dependent bundles. It also contains the OSGi Log Service API and the Knopflerfish Log API.

There is a newer version: 2.2.7
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.log4j.pattern;

import org.apache.log4j.helpers.Loader;
import org.apache.log4j.helpers.LogLog;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

// Contributors:   Nelson Minar <([email protected]>
//                 Igor E. Poteryaev 
//                 Reinhard Deschler 

/**
 * Most of the work of the {@link org.apache.log4j.EnhancedPatternLayout} class
 * is delegated to the ExtrasPatternParser class.
 * 

It is this class that parses conversion patterns and creates * a chained list of {@link PatternConverter PatternConverters}. * * @author James P. Cakalic * @author Ceki Gülcü * @author Anders Kristensen * @author Paul Smith * @author Curt Arnold */ public final class ExtrasPatternParser { /** * Escape character for format specifier. */ private static final char ESCAPE_CHAR = '%'; /** * Literal state. */ private static final int LITERAL_STATE = 0; /** * In converter name state. */ private static final int CONVERTER_STATE = 1; /** * Dot state. */ private static final int DOT_STATE = 3; /** * Min state. */ private static final int MIN_STATE = 4; /** * Max state. */ private static final int MAX_STATE = 5; /** * Standard format specifiers for EnhancedPatternLayout. */ private static final Map PATTERN_LAYOUT_RULES; /** * Standard format specifiers for rolling file appenders. */ private static final Map FILENAME_PATTERN_RULES; static { // We set the global rules in the static initializer of ExtrasPatternParser class Map rules = new HashMap(17); rules.put("c", LoggerPatternConverter.class); rules.put("logger", LoggerPatternConverter.class); rules.put("C", ClassNamePatternConverter.class); rules.put("class", ClassNamePatternConverter.class); rules.put("d", DatePatternConverter.class); rules.put("date", DatePatternConverter.class); rules.put("F", FileLocationPatternConverter.class); rules.put("file", FileLocationPatternConverter.class); rules.put("l", FullLocationPatternConverter.class); rules.put("L", LineLocationPatternConverter.class); rules.put("line", LineLocationPatternConverter.class); rules.put("m", MessagePatternConverter.class); rules.put("message", MessagePatternConverter.class); rules.put("n", LineSeparatorPatternConverter.class); rules.put("M", MethodLocationPatternConverter.class); rules.put("method", MethodLocationPatternConverter.class); rules.put("p", LevelPatternConverter.class); rules.put("level", LevelPatternConverter.class); rules.put("r", RelativeTimePatternConverter.class); rules.put("relative", RelativeTimePatternConverter.class); rules.put("t", ThreadPatternConverter.class); rules.put("thread", ThreadPatternConverter.class); rules.put("x", NDCPatternConverter.class); rules.put("ndc", NDCPatternConverter.class); rules.put("X", PropertiesPatternConverter.class); rules.put("properties", PropertiesPatternConverter.class); rules.put("sn", SequenceNumberPatternConverter.class); rules.put("sequenceNumber", SequenceNumberPatternConverter.class); rules.put("throwable", ThrowableInformationPatternConverter.class); PATTERN_LAYOUT_RULES = new ReadOnlyMap(rules); Map fnameRules = new HashMap(4); fnameRules.put("d", FileDatePatternConverter.class); fnameRules.put("date", FileDatePatternConverter.class); fnameRules.put("i", IntegerPatternConverter.class); fnameRules.put("index", IntegerPatternConverter.class); FILENAME_PATTERN_RULES = new ReadOnlyMap(fnameRules); } /** * Private constructor. */ private ExtrasPatternParser() { } /** * Get standard format specifiers for EnhancedPatternLayout. * * @return read-only map of format converter classes keyed by format specifier strings. */ public static Map getPatternLayoutRules() { return PATTERN_LAYOUT_RULES; } /** * Get standard format specifiers for rolling file appender file specification. * * @return read-only map of format converter classes keyed by format specifier strings. */ public static Map getFileNamePatternRules() { return FILENAME_PATTERN_RULES; } /** * Extract the converter identifier found at position i. *

* After this function returns, the variable i will point to the * first char after the end of the converter identifier. *

* If i points to a char which is not a character acceptable at the * start of a unicode identifier, the value null is returned. * * @param lastChar last processed character. * @param pattern format string. * @param i current index into pattern format. * @param convBuf buffer to receive conversion specifier. * @param currentLiteral literal to be output in case format specifier in unrecognized. * @return position in pattern after converter. */ private static int extractConverter( char lastChar, final String pattern, int i, final StringBuffer convBuf, final StringBuffer currentLiteral) { convBuf.setLength(0); // When this method is called, lastChar points to the first character of the // conversion word. For example: // For "%hello" lastChar = 'h' // For "%-5hello" lastChar = 'h' //System.out.println("lastchar is "+lastChar); if (!Character.isUnicodeIdentifierStart(lastChar)) { return i; } convBuf.append(lastChar); while ( (i < pattern.length()) && Character.isUnicodeIdentifierPart(pattern.charAt(i))) { convBuf.append(pattern.charAt(i)); currentLiteral.append(pattern.charAt(i)); //System.out.println("conv buffer is now ["+convBuf+"]."); i++; } return i; } /** * Extract options. * * @param pattern conversion pattern. * @param i start of options. * @param options array to receive extracted options * @return position in pattern after options. */ private static int extractOptions(String pattern, int i, List options) { while ((i < pattern.length()) && (pattern.charAt(i) == '{')) { int end = pattern.indexOf('}', i); if (end == -1) { break; } String r = pattern.substring(i + 1, end); options.add(r); i = end + 1; } return i; } /** * Parse a format specifier. * * @param pattern pattern to parse. * @param patternConverters list to receive pattern converters. * @param formattingInfos list to receive field specifiers corresponding to pattern converters. * @param converterRegistry map of user-supported pattern converters keyed by format specifier, may be null. * @param rules map of stock pattern converters keyed by format specifier. */ public static void parse( final String pattern, final List patternConverters, final List formattingInfos, final Map converterRegistry, final Map rules) { if (pattern == null) { throw new NullPointerException("pattern"); } StringBuffer currentLiteral = new StringBuffer(32); int patternLength = pattern.length(); int state = LITERAL_STATE; char c; int i = 0; ExtrasFormattingInfo formattingInfo = ExtrasFormattingInfo.getDefault(); while (i < patternLength) { c = pattern.charAt(i++); switch (state) { case LITERAL_STATE: // In literal state, the last char is always a literal. if (i == patternLength) { currentLiteral.append(c); continue; } if (c == ESCAPE_CHAR) { // peek at the next char. switch (pattern.charAt(i)) { case ESCAPE_CHAR: currentLiteral.append(c); i++; // move pointer break; default: if (currentLiteral.length() != 0) { patternConverters.add( new LiteralPatternConverter(currentLiteral.toString())); formattingInfos.add(ExtrasFormattingInfo.getDefault()); } currentLiteral.setLength(0); currentLiteral.append(c); // append % state = CONVERTER_STATE; formattingInfo = ExtrasFormattingInfo.getDefault(); } } else { currentLiteral.append(c); } break; case CONVERTER_STATE: currentLiteral.append(c); switch (c) { case '-': formattingInfo = new ExtrasFormattingInfo( true, formattingInfo.isRightTruncated(), formattingInfo.getMinLength(), formattingInfo.getMaxLength()); break; case '!': formattingInfo = new ExtrasFormattingInfo( formattingInfo.isLeftAligned(), true, formattingInfo.getMinLength(), formattingInfo.getMaxLength()); break; case '.': state = DOT_STATE; break; default: if ((c >= '0') && (c <= '9')) { formattingInfo = new ExtrasFormattingInfo( formattingInfo.isLeftAligned(), formattingInfo.isRightTruncated(), c - '0', formattingInfo.getMaxLength()); state = MIN_STATE; } else { i = finalizeConverter( c, pattern, i, currentLiteral, formattingInfo, converterRegistry, rules, patternConverters, formattingInfos); // Next pattern is assumed to be a literal. state = LITERAL_STATE; formattingInfo = ExtrasFormattingInfo.getDefault(); currentLiteral.setLength(0); } } // switch break; case MIN_STATE: currentLiteral.append(c); if ((c >= '0') && (c <= '9')) { formattingInfo = new ExtrasFormattingInfo( formattingInfo.isLeftAligned(), formattingInfo.isRightTruncated(), (formattingInfo.getMinLength() * 10) + (c - '0'), formattingInfo.getMaxLength()); } else if (c == '.') { state = DOT_STATE; } else { i = finalizeConverter( c, pattern, i, currentLiteral, formattingInfo, converterRegistry, rules, patternConverters, formattingInfos); state = LITERAL_STATE; formattingInfo = ExtrasFormattingInfo.getDefault(); currentLiteral.setLength(0); } break; case DOT_STATE: currentLiteral.append(c); if ((c >= '0') && (c <= '9')) { formattingInfo = new ExtrasFormattingInfo( formattingInfo.isLeftAligned(), formattingInfo.isRightTruncated(), formattingInfo.getMinLength(), c - '0'); state = MAX_STATE; } else { LogLog.error( "Error occured in position " + i + ".\n Was expecting digit, instead got char \"" + c + "\"."); state = LITERAL_STATE; } break; case MAX_STATE: currentLiteral.append(c); if ((c >= '0') && (c <= '9')) { formattingInfo = new ExtrasFormattingInfo( formattingInfo.isLeftAligned(), formattingInfo.isRightTruncated(), formattingInfo.getMinLength(), (formattingInfo.getMaxLength() * 10) + (c - '0')); } else { i = finalizeConverter( c, pattern, i, currentLiteral, formattingInfo, converterRegistry, rules, patternConverters, formattingInfos); state = LITERAL_STATE; formattingInfo = ExtrasFormattingInfo.getDefault(); currentLiteral.setLength(0); } break; } // switch } // while if (currentLiteral.length() != 0) { patternConverters.add( new LiteralPatternConverter(currentLiteral.toString())); formattingInfos.add(ExtrasFormattingInfo.getDefault()); } } /** * Creates a new PatternConverter. * * @param converterId converterId. * @param currentLiteral literal to be used if converter is unrecognized or following converter * if converterId contains extra characters. * @param converterRegistry map of user-supported pattern converters keyed by format specifier, may be null. * @param rules map of stock pattern converters keyed by format specifier. * @param options converter options. * @return converter or null. */ private static PatternConverter createConverter( final String converterId, final StringBuffer currentLiteral, final Map converterRegistry, final Map rules, final List options) { String converterName = converterId; Object converterObj = null; for (int i = converterId.length(); (i > 0) && (converterObj == null); i--) { converterName = converterName.substring(0, i); if (converterRegistry != null) { converterObj = converterRegistry.get(converterName); } if ((converterObj == null) && (rules != null)) { converterObj = rules.get(converterName); } } if (converterObj == null) { LogLog.error("Unrecognized format specifier [" + converterId + "]"); return null; } Class converterClass; if (converterObj instanceof Class) { converterClass = (Class) converterObj; } else { if (converterObj instanceof String) { try { converterClass = Loader.loadClass((String) converterObj); } catch (ClassNotFoundException ex) { LogLog.warn( "Class for conversion pattern %" + converterName + " not found", ex); return null; } } else { LogLog.warn( "Bad map entry for conversion pattern %" + converterName + "."); return null; } } try { Method factory = converterClass.getMethod( "newInstance", new Class[]{ Class.forName("[Ljava.lang.String;") }); String[] optionsArray = new String[options.size()]; optionsArray = (String[]) options.toArray(optionsArray); Object newObj = factory.invoke(null, new Object[]{optionsArray}); if (newObj instanceof PatternConverter) { currentLiteral.delete( 0, currentLiteral.length() - (converterId.length() - converterName.length())); return (PatternConverter) newObj; } else { LogLog.warn( "Class " + converterClass.getName() + " does not extend PatternConverter."); } } catch (Exception ex) { LogLog.error("Error creating converter for " + converterId, ex); try { // // try default constructor PatternConverter pc = (PatternConverter) converterClass.newInstance(); currentLiteral.delete( 0, currentLiteral.length() - (converterId.length() - converterName.length())); return pc; } catch (Exception ex2) { LogLog.error("Error creating converter for " + converterId, ex2); } } return null; } /** * Processes a format specifier sequence. * * @param c initial character of format specifier. * @param pattern conversion pattern * @param i current position in conversion pattern. * @param currentLiteral current literal. * @param formattingInfo current field specifier. * @param converterRegistry map of user-provided pattern converters keyed by format specifier, may be null. * @param rules map of stock pattern converters keyed by format specifier. * @param patternConverters list to receive parsed pattern converter. * @param formattingInfos list to receive corresponding field specifier. * @return position after format specifier sequence. */ private static int finalizeConverter( char c, String pattern, int i, final StringBuffer currentLiteral, final ExtrasFormattingInfo formattingInfo, final Map converterRegistry, final Map rules, final List patternConverters, final List formattingInfos) { StringBuffer convBuf = new StringBuffer(); i = extractConverter(c, pattern, i, convBuf, currentLiteral); String converterId = convBuf.toString(); List options = new ArrayList(); i = extractOptions(pattern, i, options); PatternConverter pc = createConverter( converterId, currentLiteral, converterRegistry, rules, options); if (pc == null) { StringBuffer msg; if (converterId.length() == 0) { msg = new StringBuffer("Empty conversion specifier starting at position "); } else { msg = new StringBuffer("Unrecognized conversion specifier ["); msg.append(converterId); msg.append("] starting at position "); } msg.append(Integer.toString(i)); msg.append(" in conversion pattern."); LogLog.error(msg.toString()); patternConverters.add( new LiteralPatternConverter(currentLiteral.toString())); formattingInfos.add(ExtrasFormattingInfo.getDefault()); } else { patternConverters.add(pc); formattingInfos.add(formattingInfo); if (currentLiteral.length() > 0) { patternConverters.add( new LiteralPatternConverter(currentLiteral.toString())); formattingInfos.add(ExtrasFormattingInfo.getDefault()); } } currentLiteral.setLength(0); return i; } /** * The class wraps another Map but throws exceptions on any attempt to modify the map. */ private static class ReadOnlyMap implements Map { /** * Wrapped map. */ private final Map map; /** * Constructor * * @param src source map. */ public ReadOnlyMap(Map src) { map = src; } /** * {@inheritDoc} */ public void clear() { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public boolean containsKey(Object key) { return map.containsKey(key); } /** * {@inheritDoc} */ public boolean containsValue(Object value) { return map.containsValue(value); } /** * {@inheritDoc} */ public Set entrySet() { return map.entrySet(); } /** * {@inheritDoc} */ public Object get(Object key) { return map.get(key); } /** * {@inheritDoc} */ public boolean isEmpty() { return map.isEmpty(); } /** * {@inheritDoc} */ public Set keySet() { return map.keySet(); } /** * {@inheritDoc} */ public Object put(Object key, Object value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void putAll(Map t) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public Object remove(Object key) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public int size() { return map.size(); } /** * {@inheritDoc} */ public Collection values() { return map.values(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy