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    
018    package org.apache.logging.log4j.core.config.plugins.convert;
019    
020    import java.io.File;
021    import java.math.BigDecimal;
022    import java.math.BigInteger;
023    import java.net.MalformedURLException;
024    import java.net.URI;
025    import java.net.URISyntaxException;
026    import java.net.URL;
027    import java.nio.charset.Charset;
028    import java.security.Provider;
029    import java.security.Security;
030    import java.util.regex.Pattern;
031    import javax.xml.bind.DatatypeConverter;
032    
033    import org.apache.logging.log4j.Level;
034    import org.apache.logging.log4j.Logger;
035    import org.apache.logging.log4j.core.config.plugins.Plugin;
036    import org.apache.logging.log4j.core.util.Loader;
037    import 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     */
045    public 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    }