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 */
017
018package org.apache.logging.log4j.core.config.plugins.convert;
019
020import java.io.File;
021import java.math.BigDecimal;
022import java.math.BigInteger;
023import java.net.MalformedURLException;
024import java.net.URI;
025import java.net.URISyntaxException;
026import java.net.URL;
027import java.nio.charset.Charset;
028import java.security.Provider;
029import java.security.Security;
030import java.util.regex.Pattern;
031
032import javax.xml.bind.DatatypeConverter;
033
034import org.apache.logging.log4j.Level;
035import org.apache.logging.log4j.Logger;
036import org.apache.logging.log4j.core.appender.rolling.action.Duration;
037import org.apache.logging.log4j.core.config.plugins.Plugin;
038import org.apache.logging.log4j.core.util.CronExpression;
039import org.apache.logging.log4j.core.util.Loader;
040import org.apache.logging.log4j.status.StatusLogger;
041
042/**
043 * Collection of basic TypeConverter implementations. May be used to register additional TypeConverters or find
044 * registered TypeConverters.
045 *
046 * @since 2.1 Moved to the {@code convert} package.
047 */
048public final class TypeConverters {
049
050    /**
051     * The {@link Plugin#category() Plugin Category} to use for {@link TypeConverter} plugins.
052     *
053     * @since 2.1
054     */
055    public static final String CATEGORY = "TypeConverter";
056
057    /**
058     * Parses a {@link String} into a {@link BigDecimal}.
059     */
060    @Plugin(name = "BigDecimal", category = CATEGORY)
061    public static class BigDecimalConverter implements TypeConverter<BigDecimal> {
062        @Override
063        public BigDecimal convert(final String s) {
064            return new BigDecimal(s);
065        }
066    }
067
068    /**
069     * Parses a {@link String} into a {@link BigInteger}.
070     */
071    @Plugin(name = "BigInteger", category = CATEGORY)
072    public static class BigIntegerConverter implements TypeConverter<BigInteger> {
073        @Override
074        public BigInteger convert(final String s) {
075            return new BigInteger(s);
076        }
077    }
078
079    /**
080     * Converts a {@link String} into a {@link Boolean}.
081     */
082    @Plugin(name = "Boolean", category = CATEGORY)
083    public static class BooleanConverter implements TypeConverter<Boolean> {
084        @Override
085        public Boolean convert(final String s) {
086            return Boolean.valueOf(s);
087        }
088    }
089
090    /**
091     * Converts a {@link String} into a {@code byte[]}.
092     * 
093     * The supported formats are:
094     * <ul>
095     * <li>0x0123456789ABCDEF</li>
096     * <li>Base64:ABase64String</li>
097     * <li>String</li>
098     * </ul>
099     */
100    @Plugin(name = "ByteArray", category = CATEGORY)
101    public static class ByteArrayConverter implements TypeConverter<byte[]> {
102
103        private static final String PREFIX_0x = "0x";
104        private static final String PREFIX_BASE64 = "Base64:";
105
106        @Override
107        public byte[] convert(final String value) {
108            byte[] bytes;
109            if (value == null || value.isEmpty()) {
110                bytes = new byte[0];
111            } else if (value.startsWith(PREFIX_BASE64)) {
112                final String lexicalXSDBase64Binary = value.substring(PREFIX_BASE64.length());
113                bytes = DatatypeConverter.parseBase64Binary(lexicalXSDBase64Binary);
114            } else if (value.startsWith(PREFIX_0x)) {
115                final String lexicalXSDHexBinary = value.substring(PREFIX_0x.length());
116                bytes = DatatypeConverter.parseHexBinary(lexicalXSDHexBinary);
117            } else {
118                bytes = value.getBytes(Charset.defaultCharset());
119            }
120            return bytes;
121        }
122    }
123
124    /**
125     * Converts a {@link String} into a {@link Byte}.
126     */
127    @Plugin(name = "Byte", category = CATEGORY)
128    public static class ByteConverter implements TypeConverter<Byte> {
129        @Override
130        public Byte convert(final String s) {
131            return Byte.valueOf(s);
132        }
133    }
134
135    /**
136     * Converts a {@link String} into a {@link Character}.
137     */
138    @Plugin(name = "Character", category = CATEGORY)
139    public static class CharacterConverter implements TypeConverter<Character> {
140        @Override
141        public Character convert(final String s) {
142            if (s.length() != 1) {
143                throw new IllegalArgumentException("Character string must be of length 1: " + s);
144            }
145            return Character.valueOf(s.toCharArray()[0]);
146        }
147    }
148
149    /**
150     * Converts a {@link String} into a {@code char[]}.
151     */
152    @Plugin(name = "CharacterArray", category = CATEGORY)
153    public static class CharArrayConverter implements TypeConverter<char[]> {
154        @Override
155        public char[] convert(final String s) {
156            return s.toCharArray();
157        }
158    }
159
160    /**
161     * Converts a {@link String} into a {@link Charset}.
162     */
163    @Plugin(name = "Charset", category = CATEGORY)
164    public static class CharsetConverter implements TypeConverter<Charset> {
165        @Override
166        public Charset convert(final String s) {
167            return Charset.forName(s);
168        }
169    }
170
171    /**
172     * Converts a {@link String} into a {@link Class}.
173     */
174    @Plugin(name = "Class", category = CATEGORY)
175    public static class ClassConverter implements TypeConverter<Class<?>> {
176        @Override
177        public Class<?> convert(final String s) throws ClassNotFoundException {
178            return Loader.loadClass(s);
179        }
180    }
181
182    @Plugin(name = "CronExpression", category = CATEGORY)
183    public static class CronExpressionConverter implements TypeConverter<CronExpression> {
184        @Override
185        public CronExpression convert(final String s) throws Exception {
186            return new CronExpression(s);
187        }
188    }
189
190    /**
191     * Converts a {@link String} into a {@link Double}.
192     */
193    @Plugin(name = "Double", category = CATEGORY)
194    public static class DoubleConverter implements TypeConverter<Double> {
195        @Override
196        public Double convert(final String s) {
197            return Double.valueOf(s);
198        }
199    }
200
201    /**
202     * Converts a {@link String} into a {@link Duration}.
203     * @since 2.5
204     */
205    @Plugin(name = "Duration", category = CATEGORY)
206    public static class DurationConverter implements TypeConverter<Duration> {
207        @Override
208        public Duration convert(final String s) {
209            return Duration.parse(s);
210        }
211    }
212
213    /**
214     * Converts a {@link String} into a {@link File}.
215     */
216    @Plugin(name = "File", category = CATEGORY)
217    public static class FileConverter implements TypeConverter<File> {
218        @Override
219        public File convert(final String s) {
220            return new File(s);
221        }
222    }
223
224    /**
225     * Converts a {@link String} into a {@link Float}.
226     */
227    @Plugin(name = "Float", category = CATEGORY)
228    public static class FloatConverter implements TypeConverter<Float> {
229        @Override
230        public Float convert(final String s) {
231            return Float.valueOf(s);
232        }
233    }
234
235    /**
236     * Converts a {@link String} into a {@link Integer}.
237     */
238    @Plugin(name = "Integer", category = CATEGORY)
239    public static class IntegerConverter implements TypeConverter<Integer> {
240        @Override
241        public Integer convert(final String s) {
242            return Integer.valueOf(s);
243        }
244    }
245
246    /**
247     * Converts a {@link String} into a Log4j {@link Level}. Returns {@code null} for invalid level names.
248     */
249    @Plugin(name = "Level", category = CATEGORY)
250    public static class LevelConverter implements TypeConverter<Level> {
251        @Override
252        public Level convert(final String s) {
253            return Level.valueOf(s);
254        }
255    }
256
257    /**
258     * Converts a {@link String} into a {@link Long}.
259     */
260    @Plugin(name = "Long", category = CATEGORY)
261    public static class LongConverter implements TypeConverter<Long> {
262        @Override
263        public Long convert(final String s) {
264            return Long.valueOf(s);
265        }
266    }
267
268    /**
269     * Converts a {@link String} into a {@link Pattern}.
270     */
271    @Plugin(name = "Pattern", category = CATEGORY)
272    public static class PatternConverter implements TypeConverter<Pattern> {
273        @Override
274        public Pattern convert(final String s) {
275            return Pattern.compile(s);
276        }
277    }
278
279    /**
280     * Converts a {@link String} into a {@link Provider}.
281     */
282    @Plugin(name = "SecurityProvider", category = CATEGORY)
283    public static class SecurityProviderConverter implements TypeConverter<Provider> {
284        @Override
285        public Provider convert(final String s) {
286            return Security.getProvider(s);
287        }
288    }
289
290    /**
291     * Converts a {@link String} into a {@link Short}.
292     */
293    @Plugin(name = "Short", category = CATEGORY)
294    public static class ShortConverter implements TypeConverter<Short> {
295        @Override
296        public Short convert(final String s) {
297            return Short.valueOf(s);
298        }
299    }
300
301    /**
302     * Returns the given {@link String}, no conversion takes place.
303     */
304    @Plugin(name = "String", category = CATEGORY)
305    public static class StringConverter implements TypeConverter<String> {
306        @Override
307        public String convert(final String s) {
308            return s;
309        }
310    }
311
312    /**
313     * Converts a {@link String} into a {@link URI}.
314     */
315    @Plugin(name = "URI", category = CATEGORY)
316    public static class UriConverter implements TypeConverter<URI> {
317        @Override
318        public URI convert(final String s) throws URISyntaxException {
319            return new URI(s);
320        }
321    }
322
323    /**
324     * Converts a {@link String} into a {@link URL}.
325     */
326    @Plugin(name = "URL", category = CATEGORY)
327    public static class UrlConverter implements TypeConverter<URL> {
328        @Override
329        public URL convert(final String s) throws MalformedURLException {
330            return new URL(s);
331        }
332    }
333
334    /**
335     * Converts a String to a given class if a TypeConverter is available for that class. Falls back to the provided
336     * default value if the conversion is unsuccessful. However, if the default value is <em>also</em> invalid, then
337     * {@code null} is returned (along with a nasty status log message).
338     * 
339     * @param s
340     *        the string to convert
341     * @param clazz
342     *        the class to try to convert the string to
343     * @param defaultValue
344     *        the fallback object to use if the conversion is unsuccessful
345     * @return the converted object which may be {@code null} if the string is invalid for the given type
346     * @throws NullPointerException
347     *         if {@code clazz} is {@code null}
348     * @throws IllegalArgumentException
349     *         if no TypeConverter exists for the given class
350     */
351    public static Object convert(final String s, final Class<?> clazz, final Object defaultValue) {
352        final TypeConverter<?> converter = TypeConverterRegistry.getInstance().findCompatibleConverter(clazz);
353        if (s == null) {
354            // don't debug print here, resulting output is hard to understand
355            // LOGGER.debug("Null string given to convert. Using default [{}].", defaultValue);
356            return parseDefaultValue(converter, defaultValue);
357        }
358        try {
359            return converter.convert(s);
360        } catch (final Exception e) {
361            LOGGER.warn("Error while converting string [{}] to type [{}]. Using default value [{}].", s, clazz,
362                    defaultValue, e);
363            return parseDefaultValue(converter, defaultValue);
364        }
365    }
366
367    private static Object parseDefaultValue(final TypeConverter<?> converter, final Object defaultValue) {
368        if (defaultValue == null) {
369            return null;
370        }
371        if (!(defaultValue instanceof String)) {
372            return defaultValue;
373        }
374        try {
375            return converter.convert((String) defaultValue);
376        } catch (final Exception e) {
377            LOGGER.debug("Can't parse default value [{}] for type [{}].", defaultValue, converter.getClass(), e);
378            return null;
379        }
380    }
381
382    private static final Logger LOGGER = StatusLogger.getLogger();
383
384}