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 }