JAL-3026 major update 1/2
[jalview.git] / srcjar_unused / org / apache / log4j / pattern / PatternParser.java
diff --git a/srcjar_unused/org/apache/log4j/pattern/PatternParser.java b/srcjar_unused/org/apache/log4j/pattern/PatternParser.java
new file mode 100644 (file)
index 0000000..901b0fe
--- /dev/null
@@ -0,0 +1,701 @@
+/*
+ * 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 <(nelson@monkey.org>
+//                 Igor E. Poteryaev <jah@mail.ru>
+//                 Reinhard Deschler <reinhard.deschler@web.de>
+
+/**
+ * Most of the work of the {@link org.apache.log4j.EnhancedPatternLayout} class
+ * is delegated to the PatternParser class.
+ * <p>It is this class that parses conversion patterns and creates
+ * a chained list of {@link PatternConverter PatternConverters}.
+ *
+ * @author James P. Cakalic
+ * @author Ceki G&uuml;lc&uuml;
+ * @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.isRightTruncated(),
+              formattingInfo.getMinLength(),
+              formattingInfo.getMaxLength());
+          break;
+
+        case '!':
+          formattingInfo =
+            new FormattingInfo(
+              formattingInfo.isLeftAligned(), 
+              true,
+              formattingInfo.getMinLength(),
+              formattingInfo.getMaxLength());
+          break;
+
+
+        case '.':
+          state = DOT_STATE;
+
+          break;
+
+        default:
+
+          if ((c >= '0') && (c <= '9')) {
+            formattingInfo =
+              new FormattingInfo(
+                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 = FormattingInfo.getDefault();
+            currentLiteral.setLength(0);
+          }
+        } // switch
+
+        break;
+
+      case MIN_STATE:
+        currentLiteral.append(c);
+
+        if ((c >= '0') && (c <= '9')) {
+          formattingInfo =
+            new FormattingInfo(
+              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 = FormattingInfo.getDefault();
+          currentLiteral.setLength(0);
+        }
+
+        break;
+
+      case DOT_STATE:
+        currentLiteral.append(c);
+
+        if ((c >= '0') && (c <= '9')) {
+          formattingInfo =
+            new FormattingInfo(
+              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 FormattingInfo(
+              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 = 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();
+    }
+  }
+}