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