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 */
017package org.apache.logging.log4j.util;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Map;
023import java.util.Objects;
024import java.util.concurrent.ConcurrentHashMap;
025import java.util.regex.Matcher;
026import java.util.regex.Pattern;
027
028/**
029 * A source for global configuration properties.
030 *
031 * @since 2.10.0
032 */
033public interface PropertySource {
034
035    /**
036     * Returns the order in which this PropertySource has priority. A higher value means that the source will be
037     * applied later so as to take precedence over other property sources.
038     *
039     * @return priority value
040     */
041    int getPriority();
042
043    /**
044     * Iterates over all properties and performs an action for each key/value pair.
045     *
046     * @param action action to perform on each key/value pair
047     */
048    default void forEach(BiConsumer<String, String> action) {
049    }
050
051    /**
052     * Converts a list of property name tokens into a normal form. For example, a list of tokens such as
053     * "foo", "bar", "baz", might be normalized into the property name "log4j2.fooBarBaz".
054     *
055     * @param tokens list of property name tokens
056     * @return a normalized property name using the given tokens
057     */
058    default CharSequence getNormalForm(Iterable<? extends CharSequence> tokens) {
059        return null;
060    }
061
062    /**
063     * For PropertySources that cannot iterate over all the potential properties this provides a direct lookup.
064     * @param key The key to search for.
065     * @return The value or null;
066     * @since 2.13.0
067     */
068    default String getProperty(String key) {
069        return null;
070    }
071
072
073    /**
074     * For PropertySources that cannot iterate over all the potential properties this provides a direct lookup.
075     * @param key The key to search for.
076     * @return The value or null;
077     * @since 2.13.0
078     */
079    default boolean containsProperty(String key) {
080        return false;
081    }
082
083    /**
084     * Comparator for ordering PropertySource instances by priority.
085     *
086     * @since 2.10.0
087     */
088    class Comparator implements java.util.Comparator<PropertySource>, Serializable {
089        private static final long serialVersionUID = 1L;
090
091        @Override
092        public int compare(final PropertySource o1, final PropertySource o2) {
093            return Integer.compare(Objects.requireNonNull(o1).getPriority(), Objects.requireNonNull(o2).getPriority());
094        }
095    }
096
097    /**
098     * Utility methods useful for PropertySource implementations.
099     *
100     * @since 2.10.0
101     */
102    final class Util {
103        private static final String PREFIXES = "(?i:^log4j2?[-._/]?|^org\\.apache\\.logging\\.log4j\\.)?";
104        private static final Pattern PROPERTY_TOKENIZER = Pattern.compile(PREFIXES + "([A-Z]*[a-z0-9]+|[A-Z0-9]+)[-._/]?");
105        private static final Map<CharSequence, List<CharSequence>> CACHE = new ConcurrentHashMap<>();
106
107        /**
108         * Converts a property name string into a list of tokens. This will strip a prefix of {@code log4j},
109         * {@code log4j2}, {@code Log4j}, or {@code org.apache.logging.log4j}, along with separators of
110         * dash {@code -}, dot {@code .}, underscore {@code _}, and slash {@code /}. Tokens can also be separated
111         * by camel case conventions without needing a separator character in between.
112         *
113         * @param value property name
114         * @return the property broken into lower case tokens
115         */
116        public static List<CharSequence> tokenize(final CharSequence value) {
117            if (CACHE.containsKey(value)) {
118                return CACHE.get(value);
119            }
120            final List<CharSequence> tokens = new ArrayList<>();
121            final Matcher matcher = PROPERTY_TOKENIZER.matcher(value);
122            while (matcher.find()) {
123                tokens.add(matcher.group(1).toLowerCase());
124            }
125            CACHE.put(value, tokens);
126            return tokens;
127        }
128
129        /**
130         * Joins a list of strings using camelCaseConventions.
131         *
132         * @param tokens tokens to convert
133         * @return tokensAsCamelCase
134         */
135        public static CharSequence joinAsCamelCase(final Iterable<? extends CharSequence> tokens) {
136            final StringBuilder sb = new StringBuilder();
137            boolean first = true;
138            for (final CharSequence token : tokens) {
139                if (first) {
140                    sb.append(token);
141                } else {
142                    sb.append(Character.toUpperCase(token.charAt(0)));
143                    if (token.length() > 1) {
144                        sb.append(token.subSequence(1, token.length()));
145                    }
146                }
147                first = false;
148            }
149            return sb.toString();
150        }
151
152        private Util() {
153        }
154    }
155}