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