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

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

There is a newer version: 6.1.2
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 PatternParser 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 PatternParser { /** * 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 PatternParser 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 PatternParser() { } /** * 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; FormattingInfo formattingInfo = FormattingInfo.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(FormattingInfo.getDefault()); } currentLiteral.setLength(0); currentLiteral.append(c); // append % state = CONVERTER_STATE; formattingInfo = FormattingInfo.getDefault(); } } else { currentLiteral.append(c); } break; case CONVERTER_STATE: currentLiteral.append(c); switch (c) { case '-': formattingInfo = new FormattingInfo( true, formattingInfo.getMinLength(), formattingInfo.getMaxLength()); break; case '.': state = DOT_STATE; break; default: if ((c >= '0') && (c <= '9')) { formattingInfo = new FormattingInfo( formattingInfo.isLeftAligned(), 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 = FormattingInfo.getDefault(); currentLiteral.setLength(0); } } // switch break; case MIN_STATE: currentLiteral.append(c); if ((c >= '0') && (c <= '9')) { formattingInfo = new FormattingInfo( formattingInfo.isLeftAligned(), (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 = FormattingInfo.getDefault(); currentLiteral.setLength(0); } break; case DOT_STATE: currentLiteral.append(c); if ((c >= '0') && (c <= '9')) { formattingInfo = new FormattingInfo( formattingInfo.isLeftAligned(), 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 FormattingInfo( formattingInfo.isLeftAligned(), formattingInfo.getMinLength(), (formattingInfo.getMaxLength() * 10) + (c - '0')); } else { i = finalizeConverter( c, pattern, i, currentLiteral, formattingInfo, converterRegistry, rules, patternConverters, formattingInfos); state = LITERAL_STATE; formattingInfo = FormattingInfo.getDefault(); currentLiteral.setLength(0); } break; } // switch } // while if (currentLiteral.length() != 0) { patternConverters.add( new LiteralPatternConverter(currentLiteral.toString())); formattingInfos.add(FormattingInfo.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 = null; 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 FormattingInfo 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 == null) || (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(FormattingInfo.getDefault()); } else { patternConverters.add(pc); formattingInfos.add(formattingInfo); if (currentLiteral.length() > 0) { patternConverters.add( new LiteralPatternConverter(currentLiteral.toString())); formattingInfos.add(FormattingInfo.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 - 2024 Weber Informatics LLC | Privacy Policy