View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.log4j.pattern;
    19
    20  import java.util.ArrayList;
    21  import java.util.List;
    22
    23
    24  /**
    25   * NameAbbreviator generates abbreviated logger and class names.
    26   *
    27   */
    28  public abstract class NameAbbreviator {
    29    /**
    30     * Default (no abbreviation) abbreviator.
    31     */
    32    private static final NameAbbreviator DEFAULT = new NOPAbbreviator();
    33
    34    /**
    35     * Gets an abbreviator.
    36     *
    37     * For example, "%logger{2}" will output only 2 elements of the logger name,
    38     * %logger{-2} will drop 2 elements from the logger name,
    39     * "%logger{1.}" will output only the first character of the non-final elements in the name,
    40     * "%logger{1~.2~} will output the first character of the first element, two characters of
    41     * the second and subsequent elements and will use a tilde to indicate abbreviated characters.
    42     *
    43     * @param pattern abbreviation pattern.
    44     * @return abbreviator, will not be null.
    45     */
    46    public static NameAbbreviator getAbbreviator(final String pattern) {
    47      if (pattern.length() > 0) {
    48        //  if pattern is just spaces and numbers then
    49        //     use MaxElementAbbreviator
    50        String trimmed = pattern.trim();
    51
    52        if (trimmed.length() == 0) {
    53          return DEFAULT;
    54        }
    55
    56        int i = 0;
    57        if (trimmed.length() > 0) {
    58            if (trimmed.charAt(0) == '-') {
    59                i++;
    60            }
    61            for (;
    62                  (i < trimmed.length()) &&
    63                    (trimmed.charAt(i) >= '0') &&
    64                    (trimmed.charAt(i) <= '9');
    65                 i++) {
    66            }
    67        }
    68
    69
    70        //
    71        //  if all blanks and digits
    72        //
    73        if (i == trimmed.length()) {
    74          int elements = Integer.parseInt(trimmed);
    75          if (elements >= 0) {
    76              return new MaxElementAbbreviator(elements);
    77          } else {
    78              return new DropElementAbbreviator(-elements);
    79          }
    80        }
    81
    82        ArrayList fragments = new ArrayList(5);
    83        char ellipsis;
    84        int charCount;
    85        int pos = 0;
    86
    87        while ((pos < trimmed.length()) && (pos >= 0)) {
    88          int ellipsisPos = pos;
    89
    90          if (trimmed.charAt(pos) == '*') {
    91            charCount = Integer.MAX_VALUE;
    92            ellipsisPos++;
    93          } else {
    94            if ((trimmed.charAt(pos) >= '0') && (trimmed.charAt(pos) <= '9')) {
    95              charCount = trimmed.charAt(pos) - '0';
    96              ellipsisPos++;
    97            } else {
    98              charCount = 0;
    99            }
    100         }
    101
    102         ellipsis = '\0';
    103
    104         if (ellipsisPos < trimmed.length()) {
    105           ellipsis = trimmed.charAt(ellipsisPos);
    106
    107           if (ellipsis == '.') {
    108             ellipsis = '\0';
    109           }
    110         }
    111
    112         fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis));
    113         pos = trimmed.indexOf(".", pos);
    114
    115         if (pos == -1) {
    116           break;
    117         }
    118
    119         pos++;
    120       }
    121
    122       return new PatternAbbreviator(fragments);
    123     }
    124
    125     //
    126     //  no matching abbreviation, return defaultAbbreviator
    127     //
    128     return DEFAULT;
    129   }
    130
    131   /**
    132    * Gets default abbreviator.
    133    *
    134    * @return default abbreviator.
    135    */
    136   public static NameAbbreviator getDefaultAbbreviator() {
    137     return DEFAULT;
    138   }
    139
    140   /**
    141    * Abbreviates a name in a StringBuffer.
    142    *
    143    * @param nameStart starting position of name in buf.
    144    * @param buf buffer, may not be null.
    145    */
    146   public abstract void abbreviate(final int nameStart, final StringBuffer buf);
    147
    148   /**
    149    * Abbreviator that simply appends full name to buffer.
    150    */
    151   private static class NOPAbbreviator extends NameAbbreviator {
    152     /**
    153      * Constructor.
    154      */
    155     public NOPAbbreviator() {
    156     }
    157
    158     /**
    159      * {@inheritDoc}
    160      */
    161     public void abbreviate(final int nameStart, final StringBuffer buf) {
    162     }
    163   }
    164
    165   /**
    166    * Abbreviator that drops starting path elements.
    167    */
    168   private static class MaxElementAbbreviator extends NameAbbreviator {
    169     /**
    170      * Maximum number of path elements to output.
    171      */
    172     private final int count;
    173
    174     /**
    175      * Create new instance.
    176      * @param count maximum number of path elements to output.
    177      */
    178     public MaxElementAbbreviator(final int count) {
    179       this.count = count;
    180     }
    181
    182     /**
    183      * Abbreviate name.
    184      * @param buf buffer to append abbreviation.
    185      * @param nameStart start of name to abbreviate.
    186      */
    187     public void abbreviate(final int nameStart, final StringBuffer buf) {
    188       // We substract 1 from 'len' when assigning to 'end' to avoid out of
    189       // bounds exception in return r.substring(end+1, len). This can happen if
    190       // precision is 1 and the category name ends with a dot.
    191       int end = buf.length() - 1;
    192
    193       String bufString = buf.toString();
    194       for (int i = count; i > 0; i--) {
    195         end = bufString.lastIndexOf(".", end - 1);
    196
    197         if ((end == -1) || (end < nameStart)) {
    198           return;
    199         }
    200       }
    201
    202       buf.delete(nameStart, end + 1);
    203     }
    204   }
    205
    206   /**
    207    * Abbreviator that drops starting path elements.
    208    */
    209   private static class DropElementAbbreviator extends NameAbbreviator {
    210     /**
    211      * Maximum number of path elements to output.
    212      */
    213     private final int count;
    214
    215     /**
    216      * Create new instance.
    217      * @param count maximum number of path elements to output.
    218      */
    219     public DropElementAbbreviator(final int count) {
    220       this.count = count;
    221     }
    222
    223     /**
    224      * Abbreviate name.
    225      * @param buf buffer to append abbreviation.
    226      * @param nameStart start of name to abbreviate.
    227      */
    228     public void abbreviate(final int nameStart, final StringBuffer buf) {
    229       int i = count;
    230       for(int pos = buf.indexOf(".", nameStart);
    231         pos != -1;
    232         pos = buf.indexOf(".", pos + 1)) {
    233           if(--i == 0) {
    234               buf.delete(nameStart, pos + 1);
    235               break;
    236           }
    237       }
    238     }
    239   }
    240
    241
    242   /**
    243    * Fragment of an pattern abbreviator.
    244    *
    245    */
    246   private static class PatternAbbreviatorFragment {
    247     /**
    248      * Count of initial characters of element to output.
    249      */
    250     private final int charCount;
    251
    252     /**
    253      *  Character used to represent dropped characters.
    254      * '\0' indicates no representation of dropped characters.
    255      */
    256     private final char ellipsis;
    257
    258     /**
    259      * Creates a PatternAbbreviatorFragment.
    260      * @param charCount number of initial characters to preserve.
    261      * @param ellipsis character to represent elimination of characters,
    262      *    '\0' if no ellipsis is desired.
    263      */
    264     public PatternAbbreviatorFragment(
    265       final int charCount, final char ellipsis) {
    266       this.charCount = charCount;
    267       this.ellipsis = ellipsis;
    268     }
    269
    270     /**
    271      * Abbreviate element of name.
    272      * @param buf buffer to receive element.
    273      * @param startPos starting index of name element.
    274      * @return starting index of next element.
    275      */
    276     public int abbreviate(final StringBuffer buf, final int startPos) {
    277       int nextDot = buf.toString().indexOf(".", startPos);
    278
    279       if (nextDot != -1) {
    280         if ((nextDot - startPos) > charCount) {
    281           buf.delete(startPos + charCount, nextDot);
    282           nextDot = startPos + charCount;
    283
    284           if (ellipsis != '\0') {
    285             buf.insert(nextDot, ellipsis);
    286             nextDot++;
    287           }
    288         }
    289
    290         nextDot++;
    291       }
    292
    293       return nextDot;
    294     }
    295   }
    296
    297   /**
    298    * Pattern abbreviator.
    299    *
    300    *
    301    */
    302   private static class PatternAbbreviator extends NameAbbreviator {
    303     /**
    304      * Element abbreviation patterns.
    305      */
    306     private final PatternAbbreviatorFragment[] fragments;
    307
    308     /**
    309      * Create PatternAbbreviator.
    310      *
    311      * @param fragments element abbreviation patterns.
    312      */
    313     public PatternAbbreviator(List fragments) {
    314       if (fragments.size() == 0) {
    315         throw new IllegalArgumentException(
    316           "fragments must have at least one element");
    317       }
    318
    319       this.fragments = new PatternAbbreviatorFragment[fragments.size()];
    320       fragments.toArray(this.fragments);
    321     }
    322
    323     /**
    324      * Abbreviate name.
    325      * @param buf buffer that abbreviated name is appended.
    326      * @param nameStart start of name.
    327      */
    328     public void abbreviate(final int nameStart, final StringBuffer buf) {
    329       //
    330       //  all non-terminal patterns are executed once
    331       //
    332       int pos = nameStart;
    333
    334       for (int i = 0; (i < (fragments.length - 1)) && (pos < buf.length());
    335           i++) {
    336         pos = fragments[i].abbreviate(buf, pos);
    337       }
    338
    339       //
    340       //   last pattern in executed repeatedly
    341       //
    342       PatternAbbreviatorFragment terminalFragment =
    343         fragments[fragments.length - 1];
    344
    345       while ((pos < buf.length()) && (pos >= 0)) {
    346         pos = terminalFragment.abbreviate(buf, pos);
    347       }
    348     }
    349   }
    350 }