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}