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.List;
020
021import org.apache.logging.log4j.core.LogEvent;
022import org.apache.logging.log4j.core.config.Configuration;
023import org.apache.logging.log4j.core.config.plugins.Plugin;
024import org.apache.logging.log4j.core.layout.PatternLayout;
025
026/**
027 * Style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
028 */
029@Plugin(name = "style", category = "Converter")
030@ConverterKeys({ "style" })
031public final class StyleConverter extends LogEventPatternConverter implements AnsiConverter {
032
033    /**
034     * Gets an instance of the class.
035     * 
036     * @param config
037     *            The current Configuration.
038     * @param options
039     *            pattern options, may be null. If first element is "short", only the first line of the throwable will
040     *            be formatted.
041     * @return instance of class.
042     */
043    public static StyleConverter newInstance(final Configuration config, final String[] options) {
044        if (options.length < 1) {
045            LOGGER.error("Incorrect number of options on style. Expected at least 1, received " + options.length);
046            return null;
047        }
048        if (options[0] == null) {
049            LOGGER.error("No pattern supplied on style");
050            return null;
051        }
052        if (options[1] == null) {
053            LOGGER.error("No style attributes provided");
054            return null;
055        }
056        final PatternParser parser = PatternLayout.createPatternParser(config);
057        final List<PatternFormatter> formatters = parser.parse(options[0]);
058        final String style = AnsiEscape.createSequence(options[1].split("\\s*,\\s*"));
059        final boolean noConsoleNoAnsi = options.length > 2
060                && (PatternParser.NO_CONSOLE_NO_ANSI + "=true").equals(options[2]);
061        final boolean hideAnsi = noConsoleNoAnsi && System.console() == null;
062        return new StyleConverter(formatters, style, hideAnsi);
063    }
064
065    private final List<PatternFormatter> patternFormatters;
066
067    private final boolean noAnsi;
068
069    private final String style;
070
071    private final String defaultStyle;
072
073    /**
074     * Constructs the converter.
075     * 
076     * @param patternFormatters
077     *            The PatternFormatters to generate the text to manipulate.
078     * @param style
079     *            The style that should encapsulate the pattern.
080     * @param noAnsi
081     *            If true, do not output ANSI escape codes. 
082     */
083    private StyleConverter(final List<PatternFormatter> patternFormatters, final String style, boolean noAnsi) {
084        super("style", "style");
085        this.patternFormatters = patternFormatters;
086        this.style = style;
087        this.defaultStyle = AnsiEscape.getDefaultStyle();
088        this.noAnsi = noAnsi;
089    }
090
091    /**
092     * {@inheritDoc}
093     */
094    @Override
095    public void format(final LogEvent event, final StringBuilder toAppendTo) {
096        final StringBuilder buf = new StringBuilder();
097        for (final PatternFormatter formatter : patternFormatters) {
098            formatter.format(event, buf);
099        }
100
101        if (buf.length() > 0) {
102            if (noAnsi) {
103                // faster to test and do this than setting style and defaultStyle to empty strings.
104                toAppendTo.append(buf.toString());
105            } else {
106                toAppendTo.append(style).append(buf.toString()).append(defaultStyle);
107            }
108        }
109    }
110
111    @Override
112    public boolean handlesThrowable() {
113        for (final PatternFormatter formatter : patternFormatters) {
114            if (formatter.handlesThrowable()) {
115                return true;
116            }
117        }
118        return false;
119    }
120
121    /**
122     * Returns a String suitable for debugging.
123     * 
124     * @return a String suitable for debugging.
125     */
126    @Override
127    public String toString() {
128        StringBuilder sb = new StringBuilder();
129        sb.append(super.toString());
130        sb.append("[style=");
131        sb.append(style);
132        sb.append(", defaultStyle=");
133        sb.append(defaultStyle);
134        sb.append(", patternFormatters=");
135        sb.append(patternFormatters);
136        sb.append(", noAnsi=");
137        sb.append(noAnsi);
138        sb.append("]");
139        return sb.toString();
140    }
141
142}