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 }