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 }