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