    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    }