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