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 */ 017package org.apache.logging.log4j.core.pattern; 018 019import java.util.ArrayList; 020import java.util.List; 021 022 023/** 024 * NameAbbreviator generates abbreviated logger and class names. 025 */ 026public abstract class NameAbbreviator { 027 /** 028 * Default (no abbreviation) abbreviator. 029 */ 030 private static final NameAbbreviator DEFAULT = new NOPAbbreviator(); 031 032 /** 033 * Gets an abbreviator. 034 * <p> 035 * For example, "%logger{2}" will output only 2 elements of the logger name, "%logger{1.}" will output only the 036 * first character of the non-final elements in the name, "%logger(1~.2~} will output the first character of the 037 * first element, two characters of the second and subsequent elements and will use a tilde to indicate abbreviated 038 * characters. 039 * </p> 040 * 041 * @param pattern 042 * abbreviation pattern. 043 * @return abbreviator, will not be null. 044 */ 045 public static NameAbbreviator getAbbreviator(final String pattern) { 046 if (pattern.length() > 0) { 047 // if pattern is just spaces and numbers then 048 // use MaxElementAbbreviator 049 final String trimmed = pattern.trim(); 050 051 if (trimmed.isEmpty()) { 052 return DEFAULT; 053 } 054 055 int i = 0; 056 057 while (i < trimmed.length() && trimmed.charAt(i) >= '0' 058 && trimmed.charAt(i) <= '9') { 059 i++; 060 } 061 062 // 063 // if all blanks and digits 064 // 065 if (i == trimmed.length()) { 066 return new MaxElementAbbreviator(Integer.parseInt(trimmed)); 067 } 068 069 final ArrayList<PatternAbbreviatorFragment> fragments = new ArrayList<PatternAbbreviatorFragment>(5); 070 char ellipsis; 071 int charCount; 072 int pos = 0; 073 074 while (pos < trimmed.length() && pos >= 0) { 075 int ellipsisPos = pos; 076 077 if (trimmed.charAt(pos) == '*') { 078 charCount = Integer.MAX_VALUE; 079 ellipsisPos++; 080 } else { 081 if (trimmed.charAt(pos) >= '0' && trimmed.charAt(pos) <= '9') { 082 charCount = trimmed.charAt(pos) - '0'; 083 ellipsisPos++; 084 } else { 085 charCount = 0; 086 } 087 } 088 089 ellipsis = '\0'; 090 091 if (ellipsisPos < trimmed.length()) { 092 ellipsis = trimmed.charAt(ellipsisPos); 093 094 if (ellipsis == '.') { 095 ellipsis = '\0'; 096 } 097 } 098 099 fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis)); 100 pos = trimmed.indexOf('.', pos); 101 102 if (pos == -1) { 103 break; 104 } 105 106 pos++; 107 } 108 109 return new PatternAbbreviator(fragments); 110 } 111 112 // 113 // no matching abbreviation, return defaultAbbreviator 114 // 115 return DEFAULT; 116 } 117 118 /** 119 * Gets default abbreviator. 120 * 121 * @return default abbreviator. 122 */ 123 public static NameAbbreviator getDefaultAbbreviator() { 124 return DEFAULT; 125 } 126 127 /** 128 * Abbreviates a name in a String. 129 * 130 * @param buf buffer, may not be null. 131 * @return The abbreviated String. 132 */ 133 public abstract String abbreviate(final String buf); 134 135 /** 136 * Abbreviator that simply appends full name to buffer. 137 */ 138 private static class NOPAbbreviator extends NameAbbreviator { 139 /** 140 * Constructor. 141 */ 142 public NOPAbbreviator() { 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override 149 public String abbreviate(final String buf) { 150 return 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 * 166 * @param count maximum number of path elements to output. 167 */ 168 public MaxElementAbbreviator(final int count) { 169 this.count = count < 1 ? 1 : count; 170 } 171 172 /** 173 * Abbreviate name. 174 * 175 * @param buf The String to abbreviate. 176 * @return the abbreviated String. 177 */ 178 @Override 179 public String abbreviate(final String buf) { 180 181 // We subtract 1 from 'len' when assigning to 'end' to avoid out of 182 // bounds exception in return r.substring(end+1, len). This can happen if 183 // precision is 1 and the category name ends with a dot. 184 int end = buf.length() - 1; 185 186 for (int i = count; i > 0; i--) { 187 end = buf.lastIndexOf('.', end - 1); 188 if (end == -1) { 189 return buf; 190 } 191 } 192 193 return buf.substring(end + 1); 194 } 195 } 196 197 /** 198 * Fragment of an pattern abbreviator. 199 */ 200 private static class PatternAbbreviatorFragment { 201 /** 202 * Count of initial characters of element to output. 203 */ 204 private final int charCount; 205 206 /** 207 * Character used to represent dropped characters. 208 * '\0' indicates no representation of dropped characters. 209 */ 210 private final char ellipsis; 211 212 /** 213 * Creates a PatternAbbreviatorFragment. 214 * 215 * @param charCount number of initial characters to preserve. 216 * @param ellipsis character to represent elimination of characters, 217 * '\0' if no ellipsis is desired. 218 */ 219 public PatternAbbreviatorFragment( 220 final int charCount, final char ellipsis) { 221 this.charCount = charCount; 222 this.ellipsis = ellipsis; 223 } 224 225 /** 226 * Abbreviate element of name. 227 * 228 * @param buf buffer to receive element. 229 * @param startPos starting index of name element. 230 * @return starting index of next element. 231 */ 232 public int abbreviate(final StringBuilder buf, final int startPos) { 233 int nextDot = buf.toString().indexOf('.', startPos); 234 235 if (nextDot != -1) { 236 if (nextDot - startPos > charCount) { 237 buf.delete(startPos + charCount, nextDot); 238 nextDot = startPos + charCount; 239 240 if (ellipsis != '\0') { 241 buf.insert(nextDot, ellipsis); 242 nextDot++; 243 } 244 } 245 246 nextDot++; 247 } 248 249 return nextDot; 250 } 251 } 252 253 /** 254 * Pattern abbreviator. 255 */ 256 private static class PatternAbbreviator extends NameAbbreviator { 257 /** 258 * Element abbreviation patterns. 259 */ 260 private final PatternAbbreviatorFragment[] fragments; 261 262 /** 263 * Create PatternAbbreviator. 264 * 265 * @param fragments element abbreviation patterns. 266 */ 267 public PatternAbbreviator(final List<PatternAbbreviatorFragment> fragments) { 268 if (fragments.isEmpty()) { 269 throw new IllegalArgumentException( 270 "fragments must have at least one element"); 271 } 272 273 this.fragments = new PatternAbbreviatorFragment[fragments.size()]; 274 fragments.toArray(this.fragments); 275 } 276 277 /** 278 * Abbreviates name. 279 * 280 * @param buf buffer that abbreviated name is appended. 281 */ 282 @Override 283 public String abbreviate(final String buf) { 284 // 285 // all non-terminal patterns are executed once 286 // 287 int pos = 0; 288 final StringBuilder sb = new StringBuilder(buf); 289 290 for (int i = 0; i < fragments.length - 1 && pos < buf.length(); 291 i++) { 292 pos = fragments[i].abbreviate(sb, pos); 293 } 294 295 // 296 // last pattern in executed repeatedly 297 // 298 final PatternAbbreviatorFragment terminalFragment = fragments[fragments.length - 1]; 299 300 while (pos < buf.length() && pos >= 0) { 301 pos = terminalFragment.abbreviate(sb, pos); 302 } 303 return sb.toString(); 304 } 305 } 306}