001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.log4j.pattern; 019 020import java.util.ArrayList; 021import java.util.List; 022 023 024/** 025 * NameAbbreviator generates abbreviated logger and class names. 026 * 027 */ 028public abstract class NameAbbreviator { 029 /** 030 * Default (no abbreviation) abbreviator. 031 */ 032 private static final NameAbbreviator DEFAULT = new NOPAbbreviator(); 033 034 /** 035 * Gets an abbreviator. 036 * 037 * For example, "%logger{2}" will output only 2 elements of the logger name, 038 * %logger{-2} will drop 2 elements from the logger name, 039 * "%logger{1.}" will output only the first character of the non-final elements in the name, 040 * "%logger{1~.2~} will output the first character of the first element, two characters of 041 * the second and subsequent elements and will use a tilde to indicate abbreviated characters. 042 * 043 * @param pattern abbreviation pattern. 044 * @return abbreviator, will not be null. 045 */ 046 public static NameAbbreviator getAbbreviator(final String pattern) { 047 if (pattern.length() > 0) { 048 // if pattern is just spaces and numbers then 049 // use MaxElementAbbreviator 050 String trimmed = pattern.trim(); 051 052 if (trimmed.length() == 0) { 053 return DEFAULT; 054 } 055 056 int i = 0; 057 if (trimmed.length() > 0) { 058 if (trimmed.charAt(0) == '-') { 059 i++; 060 } 061 for (; 062 (i < trimmed.length()) && 063 (trimmed.charAt(i) >= '0') && 064 (trimmed.charAt(i) <= '9'); 065 i++) { 066 } 067 } 068 069 070 // 071 // if all blanks and digits 072 // 073 if (i == trimmed.length()) { 074 int elements = Integer.parseInt(trimmed); 075 if (elements >= 0) { 076 return new MaxElementAbbreviator(elements); 077 } else { 078 return new DropElementAbbreviator(-elements); 079 } 080 } 081 082 ArrayList fragments = new ArrayList(5); 083 char ellipsis; 084 int charCount; 085 int pos = 0; 086 087 while ((pos < trimmed.length()) && (pos >= 0)) { 088 int ellipsisPos = pos; 089 090 if (trimmed.charAt(pos) == '*') { 091 charCount = Integer.MAX_VALUE; 092 ellipsisPos++; 093 } else { 094 if ((trimmed.charAt(pos) >= '0') && (trimmed.charAt(pos) <= '9')) { 095 charCount = trimmed.charAt(pos) - '0'; 096 ellipsisPos++; 097 } else { 098 charCount = 0; 099 } 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}