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