All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sshtools.slf4jtty.MessageFormatter Maven / Gradle / Ivy

Go to download

An SLF4J log provider that directly implements the SLF4J API and provides logging output suitable for console output (i.e. not to a file). The width of the output is restricted to the terminal width, with the possibility to split the width into multiple sections, each which may be a fixed width, or 'auto' which divides all remaining space amongst other automative sections. Any log attribute fields may then be placed in any of the sections with a particular alignment. Text will not overflow out of its section. Sections may be colour or styled with other text attributes, including hyperlinks for supported termins. Emoticons and other graphical sequences may be used in place of log levels. Configuration is achieved via 'ini' type files, which are reload automatically when changed.

The newest version!
package com.sshtools.slf4jtty;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.NormalizedParameters;
import org.slf4j.helpers.Reporter;

// contributors: lizongbo: proposed special treatment of array parameter values
// Joern Huxhorn: pointed out double[] omission, suggested deep array copy
/**
 * Formats messages according to very simple substitution rules. Substitutions
 * can be made 1, 2 or more arguments.
 *
 * 

* For example, * *

 * MessageFormatter.format("Hi {}.", "there")
 * 
* * will return the string "Hi there.". *

* The {} pair is called the formatting anchor. It serves to designate * the location where arguments need to be substituted within the message * pattern. *

* In case your message contains the '{' or the '}' character, you do not have * to do anything special unless the '}' character immediately follows '{'. For * example, * *

 * MessageFormatter.format("Set {1,2,3} is not equal to {}.", "1,2");
 * 
* * will return the string "Set {1,2,3} is not equal to 1,2.". * *

* If for whatever reason you need to place the string "{}" in the message * without its formatting anchor meaning, then you need to escape the * '{' character with '\', that is the backslash character. Only the '{' * character should be escaped. There is no need to escape the '}' character. * For example, * *

 * MessageFormatter.format("Set \\{} is not equal to {}.", "1,2");
 * 
* * will return the string "Set {} is not equal to 1,2.". * *

* The escaping behavior just described can be overridden by escaping the escape * character '\'. Calling * *

 * MessageFormatter.format("File name is C:\\\\{}.", "file.zip");
 * 
* * will return the string "File name is C:\file.zip". * *

* The formatting conventions are different from those of {@link MessageFormat} * which ships with the Java platform. This is justified by the fact that * SLF4J's implementation is 10 times faster than that of {@link MessageFormat}. * This local performance difference is both measurable and significant in the * larger context of the complete logging processing chain. * *

* See also {@link #format(String, Object)}, * {@link #format(String, Object, Object)} and * {@link #arrayFormat(String, Object[])} methods for more details. * *

* The version of MessageFormatter is an exact copy of that in slf4j-api, but * with added ability to style parameters. It would be nice if the API provided * a way to do this one day. *

* TtyLogger and associated classes are based on SimpleLogger implementation to varying degrees. * * @author Ceki Gülcü * @author Joern Huxhorn * @author Brett Smith */ final public class MessageFormatter { static final char DELIM_START = '{'; static final char DELIM_STOP = '}'; static final String DELIM_STR = "{}"; private static final char ESCAPE_CHAR = '\\'; /** * Performs single argument substitution for the 'messagePattern' passed as * parameter. *

* For example, * *

     * MessageFormatter.format("Hi {}.", "there");
     * 
* * will return the string "Hi there.". *

* * @param parameterStyle style * @param messagePattern * The message pattern which will be parsed and formatted * @param arg * The argument to be substituted in place of the formatting anchor * @return The formatted message */ final public static FormattingTuple format(String paramterStyle, String messagePattern, Object arg) { return arrayFormat(paramterStyle, messagePattern, new Object[] { arg }); } /** * * Performs a two argument substitution for the 'messagePattern' passed as * parameter. *

* For example, * *

     * MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob");
     * 
* * will return the string "Hi Alice. My name is Bob.". * * @param parameterStyle style * @param messagePattern * The message pattern which will be parsed and formatted * @param arg1 * The argument to be substituted in place of the first formatting * anchor * @param arg2 * The argument to be substituted in place of the second formatting * anchor * @return The formatted message */ final public static FormattingTuple format(final String parameterStyle, final String messagePattern, Object arg1, Object arg2) { return arrayFormat(parameterStyle, messagePattern, new Object[] { arg1, arg2 }); } final public static FormattingTuple arrayFormat(final String parameterStyle, final String messagePattern, final Object[] argArray) { Throwable throwableCandidate = MessageFormatter.getThrowableCandidate(argArray); Object[] args = argArray; if (throwableCandidate != null) { args = MessageFormatter.trimmedCopy(argArray); } return arrayFormat(parameterStyle, messagePattern, args, throwableCandidate); } /** * Assumes that argArray only contains arguments with no throwable as last element. * * @param parameterStyle style * @param messagePattern * @param argArray */ final public static String basicArrayFormat(final String parameterStyle, final String messagePattern, final Object[] argArray) { FormattingTuple ft = arrayFormat(parameterStyle, messagePattern, argArray, null); return ft.getMessage(); } public static String basicArrayFormat(final String parameterStyle, NormalizedParameters np) { return basicArrayFormat(parameterStyle, np.getMessage(), np.getArguments()); } final public static FormattingTuple arrayFormat(final String parameterStyle, final String messagePattern, final Object[] argArray, Throwable throwable) { if (messagePattern == null) { return new FormattingTuple(null, argArray, throwable); } if (argArray == null) { return new FormattingTuple(messagePattern); } int i = 0; int j; // use string builder for better multicore performance StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); int L; for (L = 0; L < argArray.length; L++) { j = messagePattern.indexOf(DELIM_STR, i); if (j == -1) { // no more variables if (i == 0) { // this is a simple string return new FormattingTuple(messagePattern, argArray, throwable); } else { // add the tail string which contains no variables and return // the result. sbuf.append(messagePattern, i, messagePattern.length()); return new FormattingTuple(sbuf.toString(), argArray, throwable); } } else { if (isEscapedDelimeter(messagePattern, j)) { if (!isDoubleEscaped(messagePattern, j)) { L--; // DELIM_START was escaped, thus should not be incremented sbuf.append(messagePattern, i, j - 1); sbuf.append(DELIM_START); i = j + 1; } else { // The escape character preceding the delimiter start is // itself escaped: "abc x:\\{}" // we have to consume one backward slash sbuf.append(messagePattern, i, j - 1); deeplyAppendParameter(parameterStyle, sbuf, argArray[L], new HashMap<>()); i = j + 2; } } else { // normal case sbuf.append(messagePattern, i, j); deeplyAppendParameter(parameterStyle, sbuf, argArray[L], new HashMap<>()); i = j + 2; } } } // append the characters following the last {} pair. sbuf.append(messagePattern, i, messagePattern.length()); return new FormattingTuple(sbuf.toString(), argArray, throwable); } final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) { if (delimeterStartIndex == 0) { return false; } char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1); if (potentialEscape == ESCAPE_CHAR) { return true; } else { return false; } } final static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) { if (delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) { return true; } else { return false; } } // special treatment of array values was suggested by 'lizongbo' private static void deeplyAppendParameter(String parameterStyle, StringBuilder sbuf, Object o, Map seenMap) { if (o == null) { sbuf.append("null"); return; } if (!o.getClass().isArray()) { safeObjectAppend(parameterStyle, sbuf, o); } else { // check for primitive array types because they // unfortunately cannot be cast to Object[] if (o instanceof boolean[]) { booleanArrayAppend(sbuf, (boolean[]) o); } else if (o instanceof byte[]) { byteArrayAppend(sbuf, (byte[]) o); } else if (o instanceof char[]) { charArrayAppend(sbuf, (char[]) o); } else if (o instanceof short[]) { shortArrayAppend(sbuf, (short[]) o); } else if (o instanceof int[]) { intArrayAppend(sbuf, (int[]) o); } else if (o instanceof long[]) { longArrayAppend(sbuf, (long[]) o); } else if (o instanceof float[]) { floatArrayAppend(sbuf, (float[]) o); } else if (o instanceof double[]) { doubleArrayAppend(sbuf, (double[]) o); } else { objectArrayAppend(parameterStyle, sbuf, (Object[]) o, seenMap); } } } private static void safeObjectAppend(String parameterStyle, StringBuilder sbuf, Object o) { try { String oAsString = o.toString(); if(parameterStyle == null) sbuf.append(oAsString); else sbuf.append(parameterStyle.replace("${parameter}", oAsString)); } catch (Throwable t) { Reporter.error("Failed toString() invocation on an object of type [" + o.getClass().getName() + "]", t); sbuf.append("[FAILED toString()]"); } } private static void objectArrayAppend(String parameterStyle, StringBuilder sbuf, Object[] a, Map seenMap) { sbuf.append('['); if (!seenMap.containsKey(a)) { seenMap.put(a, null); final int len = a.length; for (int i = 0; i < len; i++) { deeplyAppendParameter(parameterStyle, sbuf, a[i], seenMap); if (i != len - 1) sbuf.append(", "); } // allow repeats in siblings seenMap.remove(a); } else { sbuf.append("..."); } sbuf.append(']'); } private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(String.valueOf(a[i])); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void byteArrayAppend(StringBuilder sbuf, byte[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(String.valueOf(a[i])); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void charArrayAppend(StringBuilder sbuf, char[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void shortArrayAppend(StringBuilder sbuf, short[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(String.valueOf(a[i])); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void intArrayAppend(StringBuilder sbuf, int[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(String.valueOf(a[i])); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void longArrayAppend(StringBuilder sbuf, long[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(String.valueOf(a[i])); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void floatArrayAppend(StringBuilder sbuf, float[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(String.valueOf(a[i])); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void doubleArrayAppend(StringBuilder sbuf, double[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(String.valueOf(a[i])); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } /** * Helper method to determine if an {@link Object} array contains a {@link Throwable} as last element * * @param argArray * The arguments off which we want to know if it contains a {@link Throwable} as last element * @return if the last {@link Object} in argArray is a {@link Throwable} this method will return it, * otherwise it returns null */ public static Throwable getThrowableCandidate(final Object[] argArray) { return NormalizedParameters.getThrowableCandidate(argArray); } /** * Helper method to get all but the last element of an array * * @param argArray * The arguments from which we want to remove the last element * * @return a copy of the array without the last element */ public static Object[] trimmedCopy(final Object[] argArray) { return NormalizedParameters.trimmedCopy(argArray); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy