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.lang.reflect.Constructor;
020import java.lang.reflect.InvocationTargetException;
021import java.util.Arrays;
022import java.util.List;
023
024import org.apache.logging.log4j.core.LogEvent;
025import org.apache.logging.log4j.core.config.Configuration;
026import org.apache.logging.log4j.core.config.plugins.Plugin;
027import org.apache.logging.log4j.core.layout.PatternLayout;
028import org.apache.logging.log4j.util.PerformanceSensitive;
029
030/**
031 * Style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
032 */
033public abstract class AbstractStyleNameConverter extends LogEventPatternConverter /*TODO: implements AnsiConverter*/ {
034
035    private final List<PatternFormatter> formatters;
036
037    private final String style;
038
039    /**
040     * Constructs the converter.
041     *
042     * @param formatters The PatternFormatters to generate the text to manipulate.
043     * @param styling The styling that should encapsulate the pattern.
044     */
045    protected AbstractStyleNameConverter(final String name, final List<PatternFormatter> formatters,
046                                         final String styling) {
047        super(name, "style");
048        this.formatters = formatters;
049        this.style = styling;
050    }
051
052    /**
053     * Black style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
054     */
055    @Plugin(name = Black.NAME, category = "Converter")
056    @ConverterKeys(Black.NAME)
057    public static final class Black extends AbstractStyleNameConverter {
058
059        /** Black */
060        protected static final String NAME = "black";
061
062        /**
063         * Constructs the converter. This constructor must be public.
064         *
065         * @param formatters The PatternFormatters to generate the text to manipulate.
066         * @param styling The styling that should encapsulate the pattern.
067         */
068        public Black(final List<PatternFormatter> formatters, final String styling) {
069            super(NAME, formatters, styling);
070        }
071
072        /**
073         * Gets an instance of the class (called via reflection).
074         *
075         * @param config The current Configuration.
076         * @param options The pattern options, may be null. If the first element is "short", only the first line of the
077         *            throwable will be formatted.
078         * @return new instance of class or null
079         */
080        public static Black newInstance(final Configuration config, final String[] options) {
081            return newInstance(Black.class, NAME, config, options);
082        }
083    }
084
085    /**
086     * Blue style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
087     */
088    @Plugin(name = Blue.NAME, category = "Converter")
089    @ConverterKeys(Blue.NAME)
090    public static final class Blue extends AbstractStyleNameConverter {
091
092        /** Blue */
093        protected static final String NAME = "blue";
094
095        /**
096         * Constructs the converter. This constructor must be public.
097         *
098         * @param formatters The PatternFormatters to generate the text to manipulate.
099         * @param styling The styling that should encapsulate the pattern.
100         */
101        public Blue(final List<PatternFormatter> formatters, final String styling) {
102            super(NAME, formatters, styling);
103        }
104
105        /**
106         * Gets an instance of the class (called via reflection).
107         *
108         * @param config The current Configuration.
109         * @param options The pattern options, may be null. If the first element is "short", only the first line of the
110         *                throwable will be formatted.
111         * @return new instance of class or null
112         */
113        public static Blue newInstance(final Configuration config, final String[] options) {
114            return newInstance(Blue.class, NAME, config, options);
115        }
116    }
117
118    /**
119     * Cyan style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
120     */
121    @Plugin(name = Cyan.NAME, category = "Converter")
122    @ConverterKeys(Cyan.NAME)
123    public static final class Cyan extends AbstractStyleNameConverter {
124
125        /** Cyan */
126        protected static final String NAME = "cyan";
127
128        /**
129         * Constructs the converter. This constructor must be public.
130         *
131         * @param formatters The PatternFormatters to generate the text to manipulate.
132         * @param styling The styling that should encapsulate the pattern.
133         */
134        public Cyan(final List<PatternFormatter> formatters, final String styling) {
135            super(NAME, formatters, styling);
136        }
137
138        /**
139         * Gets an instance of the class (called via reflection).
140         *
141         * @param config The current Configuration.
142         * @param options The pattern options, may be null. If the first element is "short", only the first line of the
143         *                throwable will be formatted.
144         * @return new instance of class or null
145         */
146        public static Cyan newInstance(final Configuration config, final String[] options) {
147            return newInstance(Cyan.class, NAME, config, options);
148        }
149    }
150
151    /**
152     * Green style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
153     */
154    @Plugin(name = Green.NAME, category = "Converter")
155    @ConverterKeys(Green.NAME)
156    public static final class Green extends AbstractStyleNameConverter {
157
158        /** Green */
159        protected static final String NAME = "green";
160
161        /**
162         * Constructs the converter. This constructor must be public.
163         *
164         * @param formatters The PatternFormatters to generate the text to manipulate.
165         * @param styling The styling that should encapsulate the pattern.
166         */
167        public Green(final List<PatternFormatter> formatters, final String styling) {
168            super(NAME, formatters, styling);
169        }
170
171        /**
172         * Gets an instance of the class (called via reflection).
173         *
174         * @param config The current Configuration.
175         * @param options The pattern options, may be null. If the first element is "short", only the first line of the
176         *                throwable will be formatted.
177         * @return new instance of class or null
178         */
179        public static Green newInstance(final Configuration config, final String[] options) {
180            return newInstance(Green.class, NAME, config, options);
181        }
182    }
183
184    /**
185     * Magenta style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
186     */
187    @Plugin(name = Magenta.NAME, category = "Converter")
188    @ConverterKeys(Magenta.NAME)
189    public static final class Magenta extends AbstractStyleNameConverter {
190
191        /** Magenta */
192        protected static final String NAME = "magenta";
193
194        /**
195         * Constructs the converter. This constructor must be public.
196         *
197         * @param formatters The PatternFormatters to generate the text to manipulate.
198         * @param styling The styling that should encapsulate the pattern.
199         */
200        public Magenta(final List<PatternFormatter> formatters, final String styling) {
201            super(NAME, formatters, styling);
202        }
203
204        /**
205         * Gets an instance of the class (called via reflection).
206         *
207         * @param config The current Configuration.
208         * @param options The pattern options, may be null. If the first element is "short", only the first line of the
209         *                throwable will be formatted.
210         * @return new instance of class or null
211         */
212        public static Magenta newInstance(final Configuration config, final String[] options) {
213            return newInstance(Magenta.class, NAME, config, options);
214        }
215    }
216
217    /**
218     * Red style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
219     */
220    @Plugin(name = Red.NAME, category = "Converter")
221    @ConverterKeys(Red.NAME)
222    public static final class Red extends AbstractStyleNameConverter {
223
224        /** Red */
225        protected static final String NAME = "red";
226
227        /**
228         * Constructs the converter. This constructor must be public.
229         *
230         * @param formatters The PatternFormatters to generate the text to manipulate.
231         * @param styling The styling that should encapsulate the pattern.
232         */
233        public Red(final List<PatternFormatter> formatters, final String styling) {
234            super(NAME, formatters, styling);
235        }
236
237        /**
238         * Gets an instance of the class (called via reflection).
239         *
240         * @param config The current Configuration.
241         * @param options The pattern options, may be null. If the first element is "short", only the first line of the
242         *                throwable will be formatted.
243         * @return new instance of class or null
244         */
245        public static Red newInstance(final Configuration config, final String[] options) {
246            return newInstance(Red.class, NAME, config, options);
247        }
248    }
249
250    /**
251     * White style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
252     */
253    @Plugin(name = White.NAME, category = "Converter")
254    @ConverterKeys(White.NAME)
255    public static final class White extends AbstractStyleNameConverter {
256
257        /** White */
258        protected static final String NAME = "white";
259
260        /**
261         * Constructs the converter. This constructor must be public.
262         *
263         * @param formatters The PatternFormatters to generate the text to manipulate.
264         * @param styling The styling that should encapsulate the pattern.
265         */
266        public White(final List<PatternFormatter> formatters, final String styling) {
267            super(NAME, formatters, styling);
268        }
269
270        /**
271         * Gets an instance of the class (called via reflection).
272         *
273         * @param config The current Configuration.
274         * @param options The pattern options, may be null. If the first element is "short", only the first line of the
275         *                throwable will be formatted.
276         * @return new instance of class or null
277         */
278        public static White newInstance(final Configuration config, final String[] options) {
279            return newInstance(White.class, NAME, config, options);
280        }
281    }
282
283    /**
284     * Yellow style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
285     */
286    @Plugin(name = Yellow.NAME, category = "Converter")
287    @ConverterKeys(Yellow.NAME)
288    public static final class Yellow extends AbstractStyleNameConverter {
289
290        /** Yellow */
291        protected static final String NAME = "yellow";
292
293        /**
294         * Constructs the converter. This constructor must be public.
295         *
296         * @param formatters The PatternFormatters to generate the text to manipulate.
297         * @param styling The styling that should encapsulate the pattern.
298         */
299        public Yellow(final List<PatternFormatter> formatters, final String styling) {
300            super(NAME, formatters, styling);
301        }
302
303        /**
304         * Gets an instance of the class (called via reflection).
305         *
306         * @param config The current Configuration.
307         * @param options The pattern options, may be null. If the first element is "short", only the first line of the
308         *                throwable will be formatted.
309         * @return new instance of class or null
310         */
311        public static Yellow newInstance(final Configuration config, final String[] options) {
312            return newInstance(Yellow.class, NAME, config, options);
313        }
314    }
315
316    /**
317     * Gets an instance of the class (called via reflection).
318     *
319     * @param config The current Configuration.
320     * @param options The pattern options, may be null. If the first element is "short", only the first line of the
321     *                throwable will be formatted.
322     * @return new instance of class or null
323     */
324    protected static <T extends AbstractStyleNameConverter> T newInstance(final Class<T> asnConverterClass,
325                                                                          final String name, final Configuration config,
326                                                                          final String[] options) {
327        final List<PatternFormatter> formatters = toPatternFormatterList(config, options);
328        if (formatters == null) {
329            return null;
330        }
331        try {
332            final Constructor<T> constructor = asnConverterClass.getConstructor(List.class, String.class);
333            return constructor.newInstance(formatters, AnsiEscape.createSequence(name));
334        } catch (final SecurityException e) {
335            LOGGER.error(e.toString(), e);
336        } catch (final NoSuchMethodException e) {
337            LOGGER.error(e.toString(), e);
338        } catch (final IllegalArgumentException e) {
339            LOGGER.error(e.toString(), e);
340        } catch (final InstantiationException e) {
341            LOGGER.error(e.toString(), e);
342        } catch (final IllegalAccessException e) {
343            LOGGER.error(e.toString(), e);
344        } catch (final InvocationTargetException e) {
345            LOGGER.error(e.toString(), e);
346        }
347        return null;
348    }
349
350    /**
351     * Creates a list of PatternFormatter from the given configuration and options or null if no pattern is supplied.
352     *
353     * @param config A configuration.
354     * @param options pattern options.
355     * @return a list of PatternFormatter from the given configuration and options or null if no pattern is supplied.
356     */
357    private static List<PatternFormatter> toPatternFormatterList(final Configuration config, final String[] options) {
358        if (options.length == 0 || options[0] == null) {
359            LOGGER.error("No pattern supplied on style for config=" + config);
360            return null;
361        }
362        final PatternParser parser = PatternLayout.createPatternParser(config);
363        if (parser == null) {
364            LOGGER.error("No PatternParser created for config=" + config + ", options=" + Arrays.toString(options));
365            return null;
366        }
367        return parser.parse(options[0]);
368    }
369
370    /**
371     * {@inheritDoc}
372     */
373    @Override
374    @PerformanceSensitive("allocation")
375    public void format(final LogEvent event, final StringBuilder toAppendTo) {
376        final int start = toAppendTo.length();
377        for (int i = 0; i < formatters.size(); i++) {
378            final PatternFormatter formatter = formatters.get(i);
379            formatter.format(event, toAppendTo);
380        }
381        if (toAppendTo.length() > start) {
382            toAppendTo.insert(start, style);
383            toAppendTo.append(AnsiEscape.getDefaultStyle());
384        }
385    }
386}