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.visitors;
019
020import java.lang.annotation.Annotation;
021import java.lang.reflect.Member;
022import java.util.Map;
023import java.util.Objects;
024
025import org.apache.logging.log4j.Logger;
026import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters;
027import org.apache.logging.log4j.core.lookup.StrSubstitutor;
028import org.apache.logging.log4j.status.StatusLogger;
029import org.apache.logging.log4j.util.Strings;
030
031/**
032 * Base class for PluginVisitor implementations. Provides convenience methods as well as all method implementations
033 * other than the {@code visit} method.
034 *
035 * @param <A> the Plugin annotation type.
036 */
037public abstract class AbstractPluginVisitor<A extends Annotation> implements PluginVisitor<A> {
038
039    /** Status logger. */
040    protected static final Logger LOGGER = StatusLogger.getLogger();
041
042    /**
043     *
044     */
045    protected final Class<A> clazz;
046    /**
047     *
048     */
049    protected A annotation;
050    /**
051     *
052     */
053    protected String[] aliases;
054    /**
055     *
056     */
057    protected Class<?> conversionType;
058    /**
059     *
060     */
061    protected StrSubstitutor substitutor;
062    /**
063     *
064     */
065    protected Member member;
066
067    /**
068     * This constructor must be overridden by implementation classes as a no-arg constructor.
069     *
070     * @param clazz the annotation class this PluginVisitor is for.
071     */
072    protected AbstractPluginVisitor(final Class<A> clazz) {
073        this.clazz = clazz;
074    }
075
076    @SuppressWarnings("unchecked")
077    @Override
078    public PluginVisitor<A> setAnnotation(final Annotation anAnnotation) {
079        final Annotation a = Objects.requireNonNull(anAnnotation, "No annotation was provided");
080        if (this.clazz.isInstance(a)) {
081            this.annotation = (A) a;
082        }
083        return this;
084    }
085
086    @Override
087    public PluginVisitor<A> setAliases(final String... someAliases) {
088        this.aliases = someAliases;
089        return this;
090    }
091
092    @Override
093    public PluginVisitor<A> setConversionType(final Class<?> aConversionType) {
094        this.conversionType = Objects.requireNonNull(aConversionType, "No conversion type class was provided");
095        return this;
096    }
097
098    @Override
099    public PluginVisitor<A> setStrSubstitutor(final StrSubstitutor aSubstitutor) {
100        this.substitutor = Objects.requireNonNull(aSubstitutor, "No StrSubstitutor was provided");
101        return this;
102    }
103
104    @Override
105    public PluginVisitor<A> setMember(final Member aMember) {
106        this.member = aMember;
107        return this;
108    }
109
110    /**
111     * Removes an Entry from a given Map using a key name and aliases for that key. Keys are case-insensitive.
112     *
113     * @param attributes the Map to remove an Entry from.
114     * @param name       the key name to look up.
115     * @param aliases    optional aliases of the key name to look up.
116     * @return the value corresponding to the given key or {@code null} if nonexistent.
117     */
118    protected static String removeAttributeValue(final Map<String, String> attributes,
119                                                 final String name,
120                                                 final String... aliases) {
121        for (final Map.Entry<String, String> entry : attributes.entrySet()) {
122            final String key = entry.getKey();
123            final String value = entry.getValue();
124            if (key.equalsIgnoreCase(name)) {
125                attributes.remove(key);
126                return value;
127            }
128            if (aliases != null) {
129                for (final String alias : aliases) {
130                    if (key.equalsIgnoreCase(alias)) {
131                        attributes.remove(key);
132                        return value;
133                    }
134                }
135            }
136        }
137        return null;
138    }
139
140    /**
141     * Converts the given value into the configured type falling back to the provided default value.
142     *
143     * @param value        the value to convert.
144     * @param defaultValue the fallback value to use in case of no value or an error.
145     * @return the converted value whether that be based on the given value or the default value.
146     */
147    protected Object convert(final String value, final Object defaultValue) {
148        if (defaultValue instanceof String) {
149            return TypeConverters.convert(value, this.conversionType, Strings.trimToNull((String) defaultValue));
150        }
151        return TypeConverters.convert(value, this.conversionType, defaultValue);
152    }
153}