001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.classic.pattern;
015
016import ch.qos.logback.classic.ClassicConstants;
017import ch.qos.logback.core.CoreConstants;
018
019public class TargetLengthBasedClassNameAbbreviator2 implements Abbreviator {
020
021    final int targetLength;
022
023    public TargetLengthBasedClassNameAbbreviator2(int targetLength) {
024        this.targetLength = targetLength;
025    }
026
027    public String abbreviate(String fqClassName) {
028        StringBuilder buf = new StringBuilder(targetLength);
029        if (fqClassName == null) {
030            throw new IllegalArgumentException("Class name may not be null");
031        }
032
033        int inLen = fqClassName.length();
034        if (inLen < targetLength) {
035            return fqClassName;
036        }
037
038        int[] dotIndexesArray = new int[ClassicConstants.MAX_DOTS];
039        // a.b.c contains 2 dots but 2+1 parts.
040        // see also http://jira.qos.ch/browse/LOGBACK-437
041        int[] lengthArray = new int[ClassicConstants.MAX_DOTS + 1];
042
043        int dotCount = computeDotIndexes(fqClassName, dotIndexesArray);
044
045        // System.out.println();
046        // System.out.println("Dot count for [" + className + "] is " + dotCount);
047        // if there are no dots than abbreviation is not possible
048        if (dotCount == 0) {
049            return fqClassName;
050        }
051        // printArray("dotArray: ", dotIndexesArray);
052        computeLengthArray(fqClassName, dotIndexesArray, lengthArray, dotCount);
053        // printArray("lengthArray: ", lengthArray);
054        for (int i = 0; i <= dotCount; i++) {
055            if (i == 0) {
056                buf.append(fqClassName.substring(0, lengthArray[i] - 1));
057            } else {
058                buf.append(fqClassName.substring(dotIndexesArray[i - 1], dotIndexesArray[i - 1] + lengthArray[i]));
059            }
060            // System.out.println("i=" + i + ", buf=" + buf);
061        }
062
063        return buf.toString();
064    }
065
066    /**
067     * Populate dotArray with the positions of the DOT character in className.
068     * Leftmost dot is placed at index 0 of dotArray.
069     * 
070     * @param className
071     * @param dotArray
072     * @return the number of dots found
073     */
074    static int computeDotIndexes(final String className, int[] dotArray) {
075        int dotCount = 0;
076        int k = 0;
077        while (true) {
078            // ignore the $ separator in our computations. This is both convenient
079            // and sensible.
080            k = className.indexOf(CoreConstants.DOT, k);
081            if (k != -1 && dotCount < ClassicConstants.MAX_DOTS) {
082                dotArray[dotCount] = k;
083                dotCount++;
084                k++; // move past the last found DOT
085            } else {
086                break;
087            }
088        }
089        return dotCount;
090    }
091
092    void computeLengthArray(final String className, int[] dotArray, int[] lengthArray, int dotCount) {
093        int toTrim = className.length() - targetLength;
094        // System.out.println("dotCount=" + dotCount);
095
096        int len;
097        for (int i = 0; i < dotCount; i++) {
098            // System.out.println("i=" + i + ", toTrim = " + toTrim);
099
100            // if i==0, previousDotPosition = -1, otherwise dotArray[i - 1]
101            int previousDotPosition = (i == 0) ? -1 : dotArray[i - 1];
102            // System.out.println("i="+i+ " previousDotPosition="+previousDotPosition);
103
104            // number of characters within the segment, i.e/ within the previous dot
105            // position and the current dot position
106            int charactersInSegment = dotArray[i] - previousDotPosition - 1;
107            // System.out.println("i=" + i + ", charactersInSegment = " +
108            // charactersInSegment);
109
110            if (toTrim > 0) {
111                len = (charactersInSegment < 1) ? charactersInSegment : 1;
112            } else {
113                len = charactersInSegment;
114            }
115            // System.out.println("i=" + i + ", len = " + len);
116
117            toTrim -= (charactersInSegment - len);
118            lengthArray[i] = len + 1;
119        }
120
121        int lastDotIndex = dotCount - 1;
122        lengthArray[dotCount] = className.length() - dotArray[lastDotIndex];
123    }
124
125    static void printArray(String msg, int[] ia) {
126        System.out.print(msg);
127        for (int i = 0; i < ia.length; i++) {
128            if (i == 0) {
129                System.out.print(ia[i]);
130            } else {
131                System.out.print(", " + ia[i]);
132            }
133        }
134        System.out.println();
135    }
136}