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  package org.apache.logging.log4j.core.pattern;
    18
    19  import java.util.ArrayList;
    20  import java.util.List;
    21
    22
    23  /**
    24   * NameAbbreviator generates abbreviated logger and class names.
    25   */
    26  public abstract class NameAbbreviator {
    27      /**
    28       * Default (no abbreviation) abbreviator.
    29       */
    30      private static final NameAbbreviator DEFAULT = new NOPAbbreviator();
    31
    32      /**
    33       * Gets an abbreviator.
    34       * <p>
    35       * For example, "%logger{2}" will output only 2 elements of the logger name, "%logger{1.}" will output only the
    36       * first character of the non-final elements in the name, "%logger(1~.2~} will output the first character of the
    37       * first element, two characters of the second and subsequent elements and will use a tilde to indicate abbreviated
    38       * characters.
    39       * </p>
    40       *
    41       * @param pattern
    42       *        abbreviation pattern.
    43       * @return abbreviator, will not be null.
    44       */
    45      public static NameAbbreviator getAbbreviator(final String pattern) {
    46          if (pattern.length() > 0) {
    47              //  if pattern is just spaces and numbers then
    48              //     use MaxElementAbbreviator
    49              final String trimmed = pattern.trim();
    50
    51              if (trimmed.isEmpty()) {
    52                  return DEFAULT;
    53              }
    54
    55              int i = 0;
    56
    57              while (i < trimmed.length() && trimmed.charAt(i) >= '0'
    58                      && trimmed.charAt(i) <= '9') {
    59                  i++;
    60              }
    61
    62              //
    63              //  if all blanks and digits
    64              //
    65              if (i == trimmed.length()) {
    66                  return new MaxElementAbbreviator(Integer.parseInt(trimmed));
    67              }
    68
    69              final ArrayList<PatternAbbreviatorFragment> fragments = new ArrayList<PatternAbbreviatorFragment>(5);
    70              char ellipsis;
    71              int charCount;
    72              int pos = 0;
    73
    74              while (pos < trimmed.length() && pos >= 0) {
    75                  int ellipsisPos = pos;
    76
    77                  if (trimmed.charAt(pos) == '*') {
    78                      charCount = Integer.MAX_VALUE;
    79                      ellipsisPos++;
    80                  } else {
    81                      if (trimmed.charAt(pos) >= '0' && trimmed.charAt(pos) <= '9') {
    82                          charCount = trimmed.charAt(pos) - '0';
    83                          ellipsisPos++;
    84                      } else {
    85                          charCount = 0;
    86                      }
    87                  }
    88
    89                  ellipsis = '\0';
    90
    91                  if (ellipsisPos < trimmed.length()) {
    92                      ellipsis = trimmed.charAt(ellipsisPos);
    93
    94                      if (ellipsis == '.') {
    95                          ellipsis = '\0';
    96                      }
    97                  }
    98
    99                  fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis));
    100                 pos = trimmed.indexOf('.', pos);
    101
    102                 if (pos == -1) {
    103                     break;
    104                 }
    105
    106                 pos++;
    107             }
    108
    109             return new PatternAbbreviator(fragments);
    110         }
    111
    112         //
    113         //  no matching abbreviation, return defaultAbbreviator
    114         //
    115         return DEFAULT;
    116     }
    117
    118     /**
    119      * Gets default abbreviator.
    120      *
    121      * @return default abbreviator.
    122      */
    123     public static NameAbbreviator getDefaultAbbreviator() {
    124         return DEFAULT;
    125     }
    126
    127     /**
    128      * Abbreviates a name in a String.
    129      *
    130      * @param buf       buffer, may not be null.
    131      * @return The abbreviated String.
    132      */
    133     public abstract String abbreviate(final String buf);
    134
    135     /**
    136      * Abbreviator that simply appends full name to buffer.
    137      */
    138     private static class NOPAbbreviator extends NameAbbreviator {
    139         /**
    140          * Constructor.
    141          */
    142         public NOPAbbreviator() {
    143         }
    144
    145         /**
    146          * {@inheritDoc}
    147          */
    148         @Override
    149         public String abbreviate(final String buf) {
    150             return buf;
    151         }
    152     }
    153
    154     /**
    155      * Abbreviator that drops starting path elements.
    156      */
    157     private static class MaxElementAbbreviator extends NameAbbreviator {
    158         /**
    159          * Maximum number of path elements to output.
    160          */
    161         private final int count;
    162
    163         /**
    164          * Create new instance.
    165          *
    166          * @param count maximum number of path elements to output.
    167          */
    168         public MaxElementAbbreviator(final int count) {
    169             this.count = count < 1 ? 1 : count;
    170         }
    171
    172         /**
    173          * Abbreviate name.
    174          *
    175          * @param buf The String to abbreviate.
    176          * @return the abbreviated String.
    177          */
    178         @Override
    179         public String abbreviate(final String buf) {
    180
    181             // We subtract 1 from 'len' when assigning to 'end' to avoid out of
    182             // bounds exception in return r.substring(end+1, len). This can happen if
    183             // precision is 1 and the category name ends with a dot.
    184             int end = buf.length() - 1;
    185
    186             for (int i = count; i > 0; i--) {
    187                 end = buf.lastIndexOf('.', end - 1);
    188                 if (end == -1) {
    189                     return buf;
    190                 }
    191             }
    192
    193             return buf.substring(end + 1);
    194         }
    195     }
    196
    197     /**
    198      * Fragment of an pattern abbreviator.
    199      */
    200     private static class PatternAbbreviatorFragment {
    201         /**
    202          * Count of initial characters of element to output.
    203          */
    204         private final int charCount;
    205
    206         /**
    207          * Character used to represent dropped characters.
    208          * '\0' indicates no representation of dropped characters.
    209          */
    210         private final char ellipsis;
    211
    212         /**
    213          * Creates a PatternAbbreviatorFragment.
    214          *
    215          * @param charCount number of initial characters to preserve.
    216          * @param ellipsis  character to represent elimination of characters,
    217          *                  '\0' if no ellipsis is desired.
    218          */
    219         public PatternAbbreviatorFragment(
    220             final int charCount, final char ellipsis) {
    221             this.charCount = charCount;
    222             this.ellipsis = ellipsis;
    223         }
    224
    225         /**
    226          * Abbreviate element of name.
    227          *
    228          * @param buf      buffer to receive element.
    229          * @param startPos starting index of name element.
    230          * @return starting index of next element.
    231          */
    232         public int abbreviate(final StringBuilder buf, final int startPos) {
    233             int nextDot = buf.toString().indexOf('.', startPos);
    234
    235             if (nextDot != -1) {
    236                 if (nextDot - startPos > charCount) {
    237                     buf.delete(startPos + charCount, nextDot);
    238                     nextDot = startPos + charCount;
    239
    240                     if (ellipsis != '\0') {
    241                         buf.insert(nextDot, ellipsis);
    242                         nextDot++;
    243                     }
    244                 }
    245
    246                 nextDot++;
    247             }
    248
    249             return nextDot;
    250         }
    251     }
    252
    253     /**
    254      * Pattern abbreviator.
    255      */
    256     private static class PatternAbbreviator extends NameAbbreviator {
    257         /**
    258          * Element abbreviation patterns.
    259          */
    260         private final PatternAbbreviatorFragment[] fragments;
    261
    262         /**
    263          * Create PatternAbbreviator.
    264          *
    265          * @param fragments element abbreviation patterns.
    266          */
    267         public PatternAbbreviator(final List<PatternAbbreviatorFragment> fragments) {
    268             if (fragments.isEmpty()) {
    269                 throw new IllegalArgumentException(
    270                     "fragments must have at least one element");
    271             }
    272
    273             this.fragments = new PatternAbbreviatorFragment[fragments.size()];
    274             fragments.toArray(this.fragments);
    275         }
    276
    277         /**
    278          * Abbreviates name.
    279          *
    280          * @param buf       buffer that abbreviated name is appended.
    281          */
    282         @Override
    283         public String abbreviate(final String buf) {
    284             //
    285             //  all non-terminal patterns are executed once
    286             //
    287             int pos = 0;
    288             final StringBuilder sb = new StringBuilder(buf);
    289
    290             for (int i = 0; i < fragments.length - 1 && pos < buf.length();
    291                  i++) {
    292                 pos = fragments[i].abbreviate(sb, pos);
    293             }
    294
    295             //
    296             //   last pattern in executed repeatedly
    297             //
    298             final PatternAbbreviatorFragment terminalFragment = fragments[fragments.length - 1];
    299
    300             while (pos < buf.length() && pos >= 0) {
    301                 pos = terminalFragment.abbreviate(sb, pos);
    302             }
    303             return sb.toString();
    304         }
    305     }
    306 }