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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org.apache.log4j.pattern;
20 import java.util.ArrayList;
21 import java.util.List;
25 * NameAbbreviator generates abbreviated logger and class names.
28 public abstract class NameAbbreviator {
30 * Default (no abbreviation) abbreviator.
32 private static final NameAbbreviator DEFAULT = new NOPAbbreviator();
35 * Gets an abbreviator.
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.
43 * @param pattern abbreviation pattern.
44 * @return abbreviator, will not be null.
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();
52 if (trimmed.length() == 0) {
57 if (trimmed.length() > 0) {
58 if (trimmed.charAt(0) == '-') {
62 (i < trimmed.length()) &&
63 (trimmed.charAt(i) >= '0') &&
64 (trimmed.charAt(i) <= '9');
71 // if all blanks and digits
73 if (i == trimmed.length()) {
74 int elements = Integer.parseInt(trimmed);
76 return new MaxElementAbbreviator(elements);
78 return new DropElementAbbreviator(-elements);
82 ArrayList fragments = new ArrayList(5);
87 while ((pos < trimmed.length()) && (pos >= 0)) {
88 int ellipsisPos = pos;
90 if (trimmed.charAt(pos) == '*') {
91 charCount = Integer.MAX_VALUE;
94 if ((trimmed.charAt(pos) >= '0') && (trimmed.charAt(pos) <= '9')) {
95 charCount = trimmed.charAt(pos) - '0';
104 if (ellipsisPos < trimmed.length()) {
105 ellipsis = trimmed.charAt(ellipsisPos);
107 if (ellipsis == '.') {
112 fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis));
113 pos = trimmed.indexOf(".", pos);
122 return new PatternAbbreviator(fragments);
126 // no matching abbreviation, return defaultAbbreviator
132 * Gets default abbreviator.
134 * @return default abbreviator.
136 public static NameAbbreviator getDefaultAbbreviator() {
141 * Abbreviates a name in a StringBuffer.
143 * @param nameStart starting position of name in buf.
144 * @param buf buffer, may not be null.
146 public abstract void abbreviate(final int nameStart, final StringBuffer buf);
149 * Abbreviator that simply appends full name to buffer.
151 private static class NOPAbbreviator extends NameAbbreviator {
155 public NOPAbbreviator() {
161 public void abbreviate(final int nameStart, final StringBuffer buf) {
166 * Abbreviator that drops starting path elements.
168 private static class MaxElementAbbreviator extends NameAbbreviator {
170 * Maximum number of path elements to output.
172 private final int count;
175 * Create new instance.
176 * @param count maximum number of path elements to output.
178 public MaxElementAbbreviator(final int count) {
184 * @param buf buffer to append abbreviation.
185 * @param nameStart start of name to abbreviate.
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;
193 String bufString = buf.toString();
194 for (int i = count; i > 0; i--) {
195 end = bufString.lastIndexOf(".", end - 1);
197 if ((end == -1) || (end < nameStart)) {
202 buf.delete(nameStart, end + 1);
207 * Abbreviator that drops starting path elements.
209 private static class DropElementAbbreviator extends NameAbbreviator {
211 * Maximum number of path elements to output.
213 private final int count;
216 * Create new instance.
217 * @param count maximum number of path elements to output.
219 public DropElementAbbreviator(final int count) {
225 * @param buf buffer to append abbreviation.
226 * @param nameStart start of name to abbreviate.
228 public void abbreviate(final int nameStart, final StringBuffer buf) {
230 for(int pos = buf.indexOf(".", nameStart);
232 pos = buf.indexOf(".", pos + 1)) {
234 buf.delete(nameStart, pos + 1);
243 * Fragment of an pattern abbreviator.
246 private static class PatternAbbreviatorFragment {
248 * Count of initial characters of element to output.
250 private final int charCount;
253 * Character used to represent dropped characters.
254 * '\0' indicates no representation of dropped characters.
256 private final char ellipsis;
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.
264 public PatternAbbreviatorFragment(
265 final int charCount, final char ellipsis) {
266 this.charCount = charCount;
267 this.ellipsis = ellipsis;
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.
276 public int abbreviate(final StringBuffer buf, final int startPos) {
277 int nextDot = buf.toString().indexOf(".", startPos);
280 if ((nextDot - startPos) > charCount) {
281 buf.delete(startPos + charCount, nextDot);
282 nextDot = startPos + charCount;
284 if (ellipsis != '\0') {
285 buf.insert(nextDot, ellipsis);
298 * Pattern abbreviator.
302 private static class PatternAbbreviator extends NameAbbreviator {
304 * Element abbreviation patterns.
306 private final PatternAbbreviatorFragment[] fragments;
309 * Create PatternAbbreviator.
311 * @param fragments element abbreviation patterns.
313 public PatternAbbreviator(List fragments) {
314 if (fragments.size() == 0) {
315 throw new IllegalArgumentException(
316 "fragments must have at least one element");
319 this.fragments = new PatternAbbreviatorFragment[fragments.size()];
320 fragments.toArray(this.fragments);
325 * @param buf buffer that abbreviated name is appended.
326 * @param nameStart start of name.
328 public void abbreviate(final int nameStart, final StringBuffer buf) {
330 // all non-terminal patterns are executed once
334 for (int i = 0; (i < (fragments.length - 1)) && (pos < buf.length());
336 pos = fragments[i].abbreviate(buf, pos);
340 // last pattern in executed repeatedly
342 PatternAbbreviatorFragment terminalFragment =
343 fragments[fragments.length - 1];
345 while ((pos < buf.length()) && (pos >= 0)) {
346 pos = terminalFragment.abbreviate(buf, pos);