--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.pattern;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * NameAbbreviator generates abbreviated logger and class names.
+ *
+ */
+public abstract class NameAbbreviator {
+ /**
+ * Default (no abbreviation) abbreviator.
+ */
+ private static final NameAbbreviator DEFAULT = new NOPAbbreviator();
+
+ /**
+ * Gets an abbreviator.
+ *
+ * For example, "%logger{2}" will output only 2 elements of the logger name,
+ * %logger{-2} will drop 2 elements from the logger name,
+ * "%logger{1.}" will output only the first character of the non-final elements in the name,
+ * "%logger{1~.2~} will output the first character of the first element, two characters of
+ * the second and subsequent elements and will use a tilde to indicate abbreviated characters.
+ *
+ * @param pattern abbreviation pattern.
+ * @return abbreviator, will not be null.
+ */
+ public static NameAbbreviator getAbbreviator(final String pattern) {
+ if (pattern.length() > 0) {
+ // if pattern is just spaces and numbers then
+ // use MaxElementAbbreviator
+ String trimmed = pattern.trim();
+
+ if (trimmed.length() == 0) {
+ return DEFAULT;
+ }
+
+ int i = 0;
+ if (trimmed.length() > 0) {
+ if (trimmed.charAt(0) == '-') {
+ i++;
+ }
+ for (;
+ (i < trimmed.length()) &&
+ (trimmed.charAt(i) >= '0') &&
+ (trimmed.charAt(i) <= '9');
+ i++) {
+ }
+ }
+
+
+ //
+ // if all blanks and digits
+ //
+ if (i == trimmed.length()) {
+ int elements = Integer.parseInt(trimmed);
+ if (elements >= 0) {
+ return new MaxElementAbbreviator(elements);
+ } else {
+ return new DropElementAbbreviator(-elements);
+ }
+ }
+
+ ArrayList fragments = new ArrayList(5);
+ char ellipsis;
+ int charCount;
+ int pos = 0;
+
+ while ((pos < trimmed.length()) && (pos >= 0)) {
+ int ellipsisPos = pos;
+
+ if (trimmed.charAt(pos) == '*') {
+ charCount = Integer.MAX_VALUE;
+ ellipsisPos++;
+ } else {
+ if ((trimmed.charAt(pos) >= '0') && (trimmed.charAt(pos) <= '9')) {
+ charCount = trimmed.charAt(pos) - '0';
+ ellipsisPos++;
+ } else {
+ charCount = 0;
+ }
+ }
+
+ ellipsis = '\0';
+
+ if (ellipsisPos < trimmed.length()) {
+ ellipsis = trimmed.charAt(ellipsisPos);
+
+ if (ellipsis == '.') {
+ ellipsis = '\0';
+ }
+ }
+
+ fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis));
+ pos = trimmed.indexOf(".", pos);
+
+ if (pos == -1) {
+ break;
+ }
+
+ pos++;
+ }
+
+ return new PatternAbbreviator(fragments);
+ }
+
+ //
+ // no matching abbreviation, return defaultAbbreviator
+ //
+ return DEFAULT;
+ }
+
+ /**
+ * Gets default abbreviator.
+ *
+ * @return default abbreviator.
+ */
+ public static NameAbbreviator getDefaultAbbreviator() {
+ return DEFAULT;
+ }
+
+ /**
+ * Abbreviates a name in a StringBuffer.
+ *
+ * @param nameStart starting position of name in buf.
+ * @param buf buffer, may not be null.
+ */
+ public abstract void abbreviate(final int nameStart, final StringBuffer buf);
+
+ /**
+ * Abbreviator that simply appends full name to buffer.
+ */
+ private static class NOPAbbreviator extends NameAbbreviator {
+ /**
+ * Constructor.
+ */
+ public NOPAbbreviator() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ }
+ }
+
+ /**
+ * Abbreviator that drops starting path elements.
+ */
+ private static class MaxElementAbbreviator extends NameAbbreviator {
+ /**
+ * Maximum number of path elements to output.
+ */
+ private final int count;
+
+ /**
+ * Create new instance.
+ * @param count maximum number of path elements to output.
+ */
+ public MaxElementAbbreviator(final int count) {
+ this.count = count;
+ }
+
+ /**
+ * Abbreviate name.
+ * @param buf buffer to append abbreviation.
+ * @param nameStart start of name to abbreviate.
+ */
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = buf.length() - 1;
+
+ String bufString = buf.toString();
+ for (int i = count; i > 0; i--) {
+ end = bufString.lastIndexOf(".", end - 1);
+
+ if ((end == -1) || (end < nameStart)) {
+ return;
+ }
+ }
+
+ buf.delete(nameStart, end + 1);
+ }
+ }
+
+ /**
+ * Abbreviator that drops starting path elements.
+ */
+ private static class DropElementAbbreviator extends NameAbbreviator {
+ /**
+ * Maximum number of path elements to output.
+ */
+ private final int count;
+
+ /**
+ * Create new instance.
+ * @param count maximum number of path elements to output.
+ */
+ public DropElementAbbreviator(final int count) {
+ this.count = count;
+ }
+
+ /**
+ * Abbreviate name.
+ * @param buf buffer to append abbreviation.
+ * @param nameStart start of name to abbreviate.
+ */
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ int i = count;
+ for(int pos = buf.indexOf(".", nameStart);
+ pos != -1;
+ pos = buf.indexOf(".", pos + 1)) {
+ if(--i == 0) {
+ buf.delete(nameStart, pos + 1);
+ break;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Fragment of an pattern abbreviator.
+ *
+ */
+ private static class PatternAbbreviatorFragment {
+ /**
+ * Count of initial characters of element to output.
+ */
+ private final int charCount;
+
+ /**
+ * Character used to represent dropped characters.
+ * '\0' indicates no representation of dropped characters.
+ */
+ private final char ellipsis;
+
+ /**
+ * Creates a PatternAbbreviatorFragment.
+ * @param charCount number of initial characters to preserve.
+ * @param ellipsis character to represent elimination of characters,
+ * '\0' if no ellipsis is desired.
+ */
+ public PatternAbbreviatorFragment(
+ final int charCount, final char ellipsis) {
+ this.charCount = charCount;
+ this.ellipsis = ellipsis;
+ }
+
+ /**
+ * Abbreviate element of name.
+ * @param buf buffer to receive element.
+ * @param startPos starting index of name element.
+ * @return starting index of next element.
+ */
+ public int abbreviate(final StringBuffer buf, final int startPos) {
+ int nextDot = buf.toString().indexOf(".", startPos);
+
+ if (nextDot != -1) {
+ if ((nextDot - startPos) > charCount) {
+ buf.delete(startPos + charCount, nextDot);
+ nextDot = startPos + charCount;
+
+ if (ellipsis != '\0') {
+ buf.insert(nextDot, ellipsis);
+ nextDot++;
+ }
+ }
+
+ nextDot++;
+ }
+
+ return nextDot;
+ }
+ }
+
+ /**
+ * Pattern abbreviator.
+ *
+ *
+ */
+ private static class PatternAbbreviator extends NameAbbreviator {
+ /**
+ * Element abbreviation patterns.
+ */
+ private final PatternAbbreviatorFragment[] fragments;
+
+ /**
+ * Create PatternAbbreviator.
+ *
+ * @param fragments element abbreviation patterns.
+ */
+ public PatternAbbreviator(List fragments) {
+ if (fragments.size() == 0) {
+ throw new IllegalArgumentException(
+ "fragments must have at least one element");
+ }
+
+ this.fragments = new PatternAbbreviatorFragment[fragments.size()];
+ fragments.toArray(this.fragments);
+ }
+
+ /**
+ * Abbreviate name.
+ * @param buf buffer that abbreviated name is appended.
+ * @param nameStart start of name.
+ */
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ //
+ // all non-terminal patterns are executed once
+ //
+ int pos = nameStart;
+
+ for (int i = 0; (i < (fragments.length - 1)) && (pos < buf.length());
+ i++) {
+ pos = fragments[i].abbreviate(buf, pos);
+ }
+
+ //
+ // last pattern in executed repeatedly
+ //
+ PatternAbbreviatorFragment terminalFragment =
+ fragments[fragments.length - 1];
+
+ while ((pos < buf.length()) && (pos >= 0)) {
+ pos = terminalFragment.abbreviate(buf, pos);
+ }
+ }
+ }
+}