<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */
package org.apache.logging.log4j.core.config;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.plugins.PluginManager;
import org.apache.logging.log4j.core.config.plugins.PluginType;
import org.apache.logging.log4j.core.helpers.FileUtils;
import org.apache.logging.log4j.core.helpers.Loader;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.PropertiesUtil;

/**
 * ConfigurationFactory allows the configuration implementation to be
 * dynamically chosen in 1 of 3 ways:
 * <ol>
 * <li>A system property named "log4j.configurationFactory" can be set with the
 * name of the ConfigurationFactory to be used.</li>
 * <li>
 * {@linkplain #setConfigurationFactory(ConfigurationFactory)} can be called
 * with the instance of the ConfigurationFactory to be used. This must be called
 * before any other calls to Log4j.</li>
 * <li>
 * A ConfigurationFactory implementation can be added to the classpath and
 * configured as a plugin. The Order annotation should be used to configure the
 * factory to be the first one inspected. See
 * {@linkplain XMLConfigurationFactory} for an example.</li>
 * </ol>
 *
 * If the ConfigurationFactory that was added returns null on a call to
 * getConfiguration the any other ConfigurationFactories found as plugins will
 * be called in their respective order. DefaultConfiguration is always called
 * last if no configuration has been returned.
 */
public abstract class ConfigurationFactory {
    /**
     * Allow the ConfigurationFactory class to be specified as a system property.
     */
    public static final String CONFIGURATION_FACTORY_PROPERTY = "log4j.configurationFactory";

    /**
     * Allow the location of the configuration file to be specified as a system property.
     */
    public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile";

    /**
     * Allow subclasses access to the status logger without creating another instance.
     */
    protected static final Logger LOGGER = StatusLogger.getLogger();

    /**
     * File name prefix for test configurations.
     */
    protected static final String TEST_PREFIX = "log4j2-test";

    /**
     * File name prefix for standard configurations.
     */
    protected static final String DEFAULT_PREFIX = "log4j2";

    private static volatile List<ConfigurationFactory> factories = null;

    private static ConfigurationFactory configFactory = new Factory();

    /**
     * Returns the ConfigurationFactory.
     * @return the ConfigurationFactory.
     */
    public static ConfigurationFactory getInstance() {
        if (factories == null) {
            synchronized(TEST_PREFIX) {
                if (factories == null) {
                    final List<ConfigurationFactory> list = new ArrayList<ConfigurationFactory>();
                    final String factoryClass = PropertiesUtil.getProperties().getStringProperty(CONFIGURATION_FACTORY_PROPERTY);
                    if (factoryClass != null) {
                        addFactory(list, factoryClass);
                    }
                    final PluginManager manager = new PluginManager("ConfigurationFactory");
                    manager.collectPlugins();
                    final Map<String, PluginType<?>> plugins = manager.getPlugins();
                    final Set<WeightedFactory> ordered = new TreeSet<WeightedFactory>();
                    for (final PluginType<?> type : plugins.values()) {
                        try {
                            @SuppressWarnings("unchecked")
                            final Class<ConfigurationFactory> clazz = (Class<ConfigurationFactory>)type.getPluginClass();
                            final Order order = clazz.getAnnotation(Order.class);
                            if (order != null) {
                                final Integer weight = order.value();
                                ordered.add(new WeightedFactory(weight, clazz));
                            }
                        } catch (final Exception ex) {
                            LOGGER.warn("Unable to add class " + type.getPluginClass());
                        }
                    }
                    for (final WeightedFactory wf : ordered) {
                        addFactory(list, wf.factoryClass);
                    }
                    factories = Collections.unmodifiableList(list);
                }
            }
        }

        return configFactory;
    }

    @SuppressWarnings("unchecked")
    private static void addFactory(final List<ConfigurationFactory> list, final String factoryClass) {
        try {
            addFactory(list, (Class<ConfigurationFactory>) Class.forName(factoryClass));
        } catch (final ClassNotFoundException ex) {
            LOGGER.error("Unable to load class " + factoryClass, ex);
        } catch (final Exception ex) {
            LOGGER.error("Unable to load class " + factoryClass, ex);
        }
    }

    private static void addFactory(final List<ConfigurationFactory> list,
                                   final Class<ConfigurationFactory> factoryClass) {
        try {
            list.add(factoryClass.newInstance());
        } catch (final Exception ex) {
            LOGGER.error("Unable to create instance of " + factoryClass.getName(), ex);
        }
    }

    /**
     * Set the configuration factory. This method is not intended for general use and may not be thread safe.
     * @param factory the ConfigurationFactory.
     */
    public static void setConfigurationFactory(final ConfigurationFactory factory) {
        configFactory = factory;
    }

    /**
     * Reset the ConfigurationFactory to the default. This method is not intended for general use and may
     * not be thread safe.
     */
    public static void resetConfigurationFactory() {
        configFactory = new Factory();
    }

    /**
     * Remove the ConfigurationFactory. This method is not intended for general use and may not be thread safe.
     * @param factory The factory to remove.
     */
    public static void removeConfigurationFactory(final ConfigurationFactory factory) {
        if (configFactory == factory) {
            configFactory = new Factory();
        }
    }

    protected abstract String[] getSupportedTypes();

    protected boolean isActive() {
        return true;
    }

    public abstract Configuration getConfiguration(ConfigurationSource source);

    /**
     * Returns the Configuration.
     * @param name The configuration name.
     * @param configLocation The configuration location.
     * @return The Configuration.
     */
    public Configuration getConfiguration(final String name, final URI configLocation) {
        if (!isActive()) {
            return null;
        }
        if (configLocation != null) {
            final ConfigurationSource source = getInputFromURI(configLocation);
            if (source != null) {
                return getConfiguration(source);
            }
        }
        return null;
    }

    /**
     * Load the configuration from a URI.
     * @param configLocation A URI representing the location of the configuration.
     * @return The ConfigurationSource for the configuration.
     */
    protected ConfigurationSource getInputFromURI(final URI configLocation) {
        final File configFile = FileUtils.fileFromURI(configLocation);
        if (configFile != null && configFile.exists() && configFile.canRead()) {
            try {
                return new ConfigurationSource(new FileInputStream(configFile), configFile);
            } catch (final FileNotFoundException ex) {
                LOGGER.error("Cannot locate file " + configLocation.getPath(), ex);
            }
        }
        final String scheme = configLocation.getScheme();
        if (scheme == null || scheme.equals("classloader")) {
            final ClassLoader loader = this.getClass().getClassLoader();
            final ConfigurationSource source = getInputFromResource(configLocation.getPath(), loader);
            if (source != null) {
                return source;
            }
        }
        try {
            return new ConfigurationSource(configLocation.toURL().openStream(), configLocation.getPath());
        } catch (final MalformedURLException ex) {
            LOGGER.error("Invalid URL " + configLocation.toString(), ex);
        } catch (final IOException ex) {
            LOGGER.error("Unable to access " + configLocation.toString(), ex);
        } catch (final Exception ex) {
            LOGGER.error("Unable to access " + configLocation.toString(), ex);
        }
        return null;
    }

    /**
     * Load the configuration from the location represented by the String.
     * @param config The configuration location.
     * @param loader The default ClassLoader to use.
     * @return The InputSource to use to read the configuration.
     */
    protected ConfigurationSource getInputFromString(final String config, final ClassLoader loader) {
        try {
            final URL url = new URL(config);
            return new ConfigurationSource(url.openStream(), FileUtils.fileFromURI(url.toURI()));
        } catch (final Exception ex) {
            final ConfigurationSource source = getInputFromResource(config, loader);
            if (source == null) {
                try {
                    final File file = new File(config);
                    return new ConfigurationSource(new FileInputStream(file), file);
                } catch (final FileNotFoundException fnfe) {
                    // Ignore the exception
                }
            }
            return source;
        }
    }

    /**
     * Retrieve the configuration via the ClassLoader.
     * @param resource The resource to load.
     * @param loader The default ClassLoader to use.
     * @return The ConfigurationSource for the configuration.
     */
    protected ConfigurationSource getInputFromResource(final String resource, final ClassLoader loader) {
        final URL url = Loader.getResource(resource, loader);
        if (url == null) {
            return null;
        }
        InputStream is = null;
        try {
            is = url.openStream();
        } catch (final IOException ioe) {
            return null;
        }
        if (is == null) {
            return null;
        }

        if (FileUtils.isFile(url)) {
            try {
                return new ConfigurationSource(is, FileUtils.fileFromURI((url.toURI())));
            } catch (final URISyntaxException ex) {
                // Just ignore the exception.
            }
        }
        return new ConfigurationSource(is, resource);
    }

    /**
     * Factory that chooses a ConfigurationFactory based on weighting.
     */
    private static class WeightedFactory implements Comparable<WeightedFactory> {
        private final int weight;
        private final Class<ConfigurationFactory> factoryClass;

        /**
         * Constructor.
         * @param weight The weight.
         * @param clazz The class.
         */
        public WeightedFactory(final int weight, final Class<ConfigurationFactory> clazz) {
            this.weight = weight;
            this.factoryClass = clazz;
        }

        @Override
        public int compareTo(final WeightedFactory wf) {
            final int w = wf.weight;
            if (weight == w) {
                return 0;
            } else if (weight > w) {
                return -1;
            } else {
                return 1;
            }
        }
    }

    /**
     * Default Factory.
     */
    private static class Factory extends ConfigurationFactory {

        /**
         * Default Factory Constructor.
         * @param name The configuration name.
         * @param configLocation The configuration location.
         * @return The Configuration.
         */
        @Override
        public Configuration getConfiguration(final String name, final URI configLocation) {

            if (configLocation == null) {
                final String config = PropertiesUtil.getProperties().getStringProperty(CONFIGURATION_FILE_PROPERTY);
                if (config != null) {
                    final ClassLoader loader = this.getClass().getClassLoader();
                    final ConfigurationSource source = getInputFromString(config, loader);
                    if (source != null) {
                        for (final ConfigurationFactory factory : factories) {
                            final String[] types = factory.getSupportedTypes();
                            if (types != null) {
                                for (final String type : types) {
                                    if (type.equals("*") || config.endsWith(type)) {
                                        final Configuration c = factory.getConfiguration(source);
                                        if (c != null) {
                                            return c;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } else {
                for (final ConfigurationFactory factory : factories) {
                    final String[] types = factory.getSupportedTypes();
                    if (types != null) {
                        for (final String type : types) {
                            if (type.equals("*") || configLocation.getPath().endsWith(type)) {
                                final Configuration config = factory.getConfiguration(name, configLocation);
                                if (config != null) {
                                    return config;
                                }
                            }
                        }
                    }
                }
            }

            Configuration config = getConfiguration(true, name);
            if (config == null) {
                config = getConfiguration(true, null);
                if (config == null) {
                    config = getConfiguration(false, name);
                    if (config == null) {
                        config = getConfiguration(false, null);
                    }
                }
            }
            return config != null ? config : new DefaultConfiguration();
        }

        private Configuration getConfiguration(final boolean isTest, final String name) {
            final boolean named = name != null && name.length() > 0;
            final ClassLoader loader = this.getClass().getClassLoader();
            for (final ConfigurationFactory factory : factories) {
                String configName;
                final String prefix = isTest ? TEST_PREFIX : DEFAULT_PREFIX;
                final String [] types = factory.getSupportedTypes();
                if (types == null) {
                    continue;
                }

                for (final String suffix : types) {
                    if (suffix.equals("*")) {
                        continue;
                    }
                    configName = named ? prefix + name + suffix : prefix + suffix;

                    final ConfigurationSource source = getInputFromResource(configName, loader);
                    if (source != null) {
                        return factory.getConfiguration(source);
                    }
                }
            }
            return null;
        }

        @Override
        public String[] getSupportedTypes() {
            return null;
        }

        @Override
        public Configuration getConfiguration(final ConfigurationSource source) {
            if (source != null) {
                final String config = source.getLocation();
                for (final ConfigurationFactory factory : factories) {
                    final String[] types = factory.getSupportedTypes();
                    if (types != null) {
                        for (final String type : types) {
                            if (type.equals("*") || (config != null && config.endsWith(type))) {
                                final Configuration c = factory.getConfiguration(source);
                                if (c != null) {
                                    return c;
                                } else {
                                    LOGGER.error("Cannot determine the ConfigurationFactory to use for {}", config);
                                    return null;
                                }
                            }
                        }
                    }
                }
            }
            LOGGER.error("Cannot process configuration, input source is null");
            return null;
        }
    }

    /**
     * Represents the source for the logging configuration.
     */
    public static class ConfigurationSource {

        private File file;

        private String location;

        private InputStream stream;

        public ConfigurationSource() {
        }

        public ConfigurationSource(final InputStream stream) {
            this.stream = stream;
            this.file = null;
            this.location = null;
        }

        public ConfigurationSource(final InputStream stream, final File file) {
            this.stream = stream;
            this.file = file;
            this.location = file.getAbsolutePath();
        }

        public ConfigurationSource(final InputStream stream, final String location) {
            this.stream = stream;
            this.location = location;
            this.file = null;
        }

        public File getFile() {
            return file;
        }

        public void setFile(final File file) {
            this.file = file;
        }

        public String getLocation() {
            return location;
        }

        public void setLocation(final String location) {
            this.location = location;
        }

        public InputStream getInputStream() {
            return stream;
        }

        public void setInputStream(final InputStream stream) {
            this.stream = stream;
        }
    }
}
<commit_msg>Remove wasted boxing followed by unboxing.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */
package org.apache.logging.log4j.core.config;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.plugins.PluginManager;
import org.apache.logging.log4j.core.config.plugins.PluginType;
import org.apache.logging.log4j.core.helpers.FileUtils;
import org.apache.logging.log4j.core.helpers.Loader;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.PropertiesUtil;

/**
 * ConfigurationFactory allows the configuration implementation to be
 * dynamically chosen in 1 of 3 ways:
 * <ol>
 * <li>A system property named "log4j.configurationFactory" can be set with the
 * name of the ConfigurationFactory to be used.</li>
 * <li>
 * {@linkplain #setConfigurationFactory(ConfigurationFactory)} can be called
 * with the instance of the ConfigurationFactory to be used. This must be called
 * before any other calls to Log4j.</li>
 * <li>
 * A ConfigurationFactory implementation can be added to the classpath and
 * configured as a plugin. The Order annotation should be used to configure the
 * factory to be the first one inspected. See
 * {@linkplain XMLConfigurationFactory} for an example.</li>
 * </ol>
 *
 * If the ConfigurationFactory that was added returns null on a call to
 * getConfiguration the any other ConfigurationFactories found as plugins will
 * be called in their respective order. DefaultConfiguration is always called
 * last if no configuration has been returned.
 */
public abstract class ConfigurationFactory {
    /**
     * Allow the ConfigurationFactory class to be specified as a system property.
     */
    public static final String CONFIGURATION_FACTORY_PROPERTY = "log4j.configurationFactory";

    /**
     * Allow the location of the configuration file to be specified as a system property.
     */
    public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile";

    /**
     * Allow subclasses access to the status logger without creating another instance.
     */
    protected static final Logger LOGGER = StatusLogger.getLogger();

    /**
     * File name prefix for test configurations.
     */
    protected static final String TEST_PREFIX = "log4j2-test";

    /**
     * File name prefix for standard configurations.
     */
    protected static final String DEFAULT_PREFIX = "log4j2";

    private static volatile List<ConfigurationFactory> factories = null;

    private static ConfigurationFactory configFactory = new Factory();

    /**
     * Returns the ConfigurationFactory.
     * @return the ConfigurationFactory.
     */
    public static ConfigurationFactory getInstance() {
        if (factories == null) {
            synchronized(TEST_PREFIX) {
                if (factories == null) {
                    final List<ConfigurationFactory> list = new ArrayList<ConfigurationFactory>();
                    final String factoryClass = PropertiesUtil.getProperties().getStringProperty(CONFIGURATION_FACTORY_PROPERTY);
                    if (factoryClass != null) {
                        addFactory(list, factoryClass);
                    }
                    final PluginManager manager = new PluginManager("ConfigurationFactory");
                    manager.collectPlugins();
                    final Map<String, PluginType<?>> plugins = manager.getPlugins();
                    final Set<WeightedFactory> ordered = new TreeSet<WeightedFactory>();
                    for (final PluginType<?> type : plugins.values()) {
                        try {
                            @SuppressWarnings("unchecked")
                            final Class<ConfigurationFactory> clazz = (Class<ConfigurationFactory>)type.getPluginClass();
                            final Order order = clazz.getAnnotation(Order.class);
                            if (order != null) {
                                final int weight = order.value();
                                ordered.add(new WeightedFactory(weight, clazz));
                            }
                        } catch (final Exception ex) {
                            LOGGER.warn("Unable to add class " + type.getPluginClass());
                        }
                    }
                    for (final WeightedFactory wf : ordered) {
                        addFactory(list, wf.factoryClass);
                    }
                    factories = Collections.unmodifiableList(list);
                }
            }
        }

        return configFactory;
    }

    @SuppressWarnings("unchecked")
    private static void addFactory(final List<ConfigurationFactory> list, final String factoryClass) {
        try {
            addFactory(list, (Class<ConfigurationFactory>) Class.forName(factoryClass));
        } catch (final ClassNotFoundException ex) {
            LOGGER.error("Unable to load class " + factoryClass, ex);
        } catch (final Exception ex) {
            LOGGER.error("Unable to load class " + factoryClass, ex);
        }
    }

    private static void addFactory(final List<ConfigurationFactory> list,
                                   final Class<ConfigurationFactory> factoryClass) {
        try {
            list.add(factoryClass.newInstance());
        } catch (final Exception ex) {
            LOGGER.error("Unable to create instance of " + factoryClass.getName(), ex);
        }
    }

    /**
     * Set the configuration factory. This method is not intended for general use and may not be thread safe.
     * @param factory the ConfigurationFactory.
     */
    public static void setConfigurationFactory(final ConfigurationFactory factory) {
        configFactory = factory;
    }

    /**
     * Reset the ConfigurationFactory to the default. This method is not intended for general use and may
     * not be thread safe.
     */
    public static void resetConfigurationFactory() {
        configFactory = new Factory();
    }

    /**
     * Remove the ConfigurationFactory. This method is not intended for general use and may not be thread safe.
     * @param factory The factory to remove.
     */
    public static void removeConfigurationFactory(final ConfigurationFactory factory) {
        if (configFactory == factory) {
            configFactory = new Factory();
        }
    }

    protected abstract String[] getSupportedTypes();

    protected boolean isActive() {
        return true;
    }

    public abstract Configuration getConfiguration(ConfigurationSource source);

    /**
     * Returns the Configuration.
     * @param name The configuration name.
     * @param configLocation The configuration location.
     * @return The Configuration.
     */
    public Configuration getConfiguration(final String name, final URI configLocation) {
        if (!isActive()) {
            return null;
        }
        if (configLocation != null) {
            final ConfigurationSource source = getInputFromURI(configLocation);
            if (source != null) {
                return getConfiguration(source);
            }
        }
        return null;
    }

    /**
     * Load the configuration from a URI.
     * @param configLocation A URI representing the location of the configuration.
     * @return The ConfigurationSource for the configuration.
     */
    protected ConfigurationSource getInputFromURI(final URI configLocation) {
        final File configFile = FileUtils.fileFromURI(configLocation);
        if (configFile != null && configFile.exists() && configFile.canRead()) {
            try {
                return new ConfigurationSource(new FileInputStream(configFile), configFile);
            } catch (final FileNotFoundException ex) {
                LOGGER.error("Cannot locate file " + configLocation.getPath(), ex);
            }
        }
        final String scheme = configLocation.getScheme();
        if (scheme == null || scheme.equals("classloader")) {
            final ClassLoader loader = this.getClass().getClassLoader();
            final ConfigurationSource source = getInputFromResource(configLocation.getPath(), loader);
            if (source != null) {
                return source;
            }
        }
        try {
            return new ConfigurationSource(configLocation.toURL().openStream(), configLocation.getPath());
        } catch (final MalformedURLException ex) {
            LOGGER.error("Invalid URL " + configLocation.toString(), ex);
        } catch (final IOException ex) {
            LOGGER.error("Unable to access " + configLocation.toString(), ex);
        } catch (final Exception ex) {
            LOGGER.error("Unable to access " + configLocation.toString(), ex);
        }
        return null;
    }

    /**
     * Load the configuration from the location represented by the String.
     * @param config The configuration location.
     * @param loader The default ClassLoader to use.
     * @return The InputSource to use to read the configuration.
     */
    protected ConfigurationSource getInputFromString(final String config, final ClassLoader loader) {
        try {
            final URL url = new URL(config);
            return new ConfigurationSource(url.openStream(), FileUtils.fileFromURI(url.toURI()));
        } catch (final Exception ex) {
            final ConfigurationSource source = getInputFromResource(config, loader);
            if (source == null) {
                try {
                    final File file = new File(config);
                    return new ConfigurationSource(new FileInputStream(file), file);
                } catch (final FileNotFoundException fnfe) {
                    // Ignore the exception
                }
            }
            return source;
        }
    }

    /**
     * Retrieve the configuration via the ClassLoader.
     * @param resource The resource to load.
     * @param loader The default ClassLoader to use.
     * @return The ConfigurationSource for the configuration.
     */
    protected ConfigurationSource getInputFromResource(final String resource, final ClassLoader loader) {
        final URL url = Loader.getResource(resource, loader);
        if (url == null) {
            return null;
        }
        InputStream is = null;
        try {
            is = url.openStream();
        } catch (final IOException ioe) {
            return null;
        }
        if (is == null) {
            return null;
        }

        if (FileUtils.isFile(url)) {
            try {
                return new ConfigurationSource(is, FileUtils.fileFromURI((url.toURI())));
            } catch (final URISyntaxException ex) {
                // Just ignore the exception.
            }
        }
        return new ConfigurationSource(is, resource);
    }

    /**
     * Factory that chooses a ConfigurationFactory based on weighting.
     */
    private static class WeightedFactory implements Comparable<WeightedFactory> {
        private final int weight;
        private final Class<ConfigurationFactory> factoryClass;

        /**
         * Constructor.
         * @param weight The weight.
         * @param clazz The class.
         */
        public WeightedFactory(final int weight, final Class<ConfigurationFactory> clazz) {
            this.weight = weight;
            this.factoryClass = clazz;
        }

        @Override
        public int compareTo(final WeightedFactory wf) {
            final int w = wf.weight;
            if (weight == w) {
                return 0;
            } else if (weight > w) {
                return -1;
            } else {
                return 1;
            }
        }
    }

    /**
     * Default Factory.
     */
    private static class Factory extends ConfigurationFactory {

        /**
         * Default Factory Constructor.
         * @param name The configuration name.
         * @param configLocation The configuration location.
         * @return The Configuration.
         */
        @Override
        public Configuration getConfiguration(final String name, final URI configLocation) {

            if (configLocation == null) {
                final String config = PropertiesUtil.getProperties().getStringProperty(CONFIGURATION_FILE_PROPERTY);
                if (config != null) {
                    final ClassLoader loader = this.getClass().getClassLoader();
                    final ConfigurationSource source = getInputFromString(config, loader);
                    if (source != null) {
                        for (final ConfigurationFactory factory : factories) {
                            final String[] types = factory.getSupportedTypes();
                            if (types != null) {
                                for (final String type : types) {
                                    if (type.equals("*") || config.endsWith(type)) {
                                        final Configuration c = factory.getConfiguration(source);
                                        if (c != null) {
                                            return c;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } else {
                for (final ConfigurationFactory factory : factories) {
                    final String[] types = factory.getSupportedTypes();
                    if (types != null) {
                        for (final String type : types) {
                            if (type.equals("*") || configLocation.getPath().endsWith(type)) {
                                final Configuration config = factory.getConfiguration(name, configLocation);
                                if (config != null) {
                                    return config;
                                }
                            }
                        }
                    }
                }
            }

            Configuration config = getConfiguration(true, name);
            if (config == null) {
                config = getConfiguration(true, null);
                if (config == null) {
                    config = getConfiguration(false, name);
                    if (config == null) {
                        config = getConfiguration(false, null);
                    }
                }
            }
            return config != null ? config : new DefaultConfiguration();
        }

        private Configuration getConfiguration(final boolean isTest, final String name) {
            final boolean named = name != null && name.length() > 0;
            final ClassLoader loader = this.getClass().getClassLoader();
            for (final ConfigurationFactory factory : factories) {
                String configName;
                final String prefix = isTest ? TEST_PREFIX : DEFAULT_PREFIX;
                final String [] types = factory.getSupportedTypes();
                if (types == null) {
                    continue;
                }

                for (final String suffix : types) {
                    if (suffix.equals("*")) {
                        continue;
                    }
                    configName = named ? prefix + name + suffix : prefix + suffix;

                    final ConfigurationSource source = getInputFromResource(configName, loader);
                    if (source != null) {
                        return factory.getConfiguration(source);
                    }
                }
            }
            return null;
        }

        @Override
        public String[] getSupportedTypes() {
            return null;
        }

        @Override
        public Configuration getConfiguration(final ConfigurationSource source) {
            if (source != null) {
                final String config = source.getLocation();
                for (final ConfigurationFactory factory : factories) {
                    final String[] types = factory.getSupportedTypes();
                    if (types != null) {
                        for (final String type : types) {
                            if (type.equals("*") || (config != null && config.endsWith(type))) {
                                final Configuration c = factory.getConfiguration(source);
                                if (c != null) {
                                    return c;
                                } else {
                                    LOGGER.error("Cannot determine the ConfigurationFactory to use for {}", config);
                                    return null;
                                }
                            }
                        }
                    }
                }
            }
            LOGGER.error("Cannot process configuration, input source is null");
            return null;
        }
    }

    /**
     * Represents the source for the logging configuration.
     */
    public static class ConfigurationSource {

        private File file;

        private String location;

        private InputStream stream;

        public ConfigurationSource() {
        }

        public ConfigurationSource(final InputStream stream) {
            this.stream = stream;
            this.file = null;
            this.location = null;
        }

        public ConfigurationSource(final InputStream stream, final File file) {
            this.stream = stream;
            this.file = file;
            this.location = file.getAbsolutePath();
        }

        public ConfigurationSource(final InputStream stream, final String location) {
            this.stream = stream;
            this.location = location;
            this.file = null;
        }

        public File getFile() {
            return file;
        }

        public void setFile(final File file) {
            this.file = file;
        }

        public String getLocation() {
            return location;
        }

        public void setLocation(final String location) {
            this.location = location;
        }

        public InputStream getInputStream() {
            return stream;
        }

        public void setInputStream(final InputStream stream) {
            this.stream = stream;
        }
    }
}

{'fraction_non_alphanumeric': 0.07966926821729388, 'fraction_numerical': 0.0013394122041058135, 'mean_word_length': 1.7431639934996115, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 34, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4048180', 'n_tokens_mistral': 8737, 'n_tokens_neox': 8453, 'n_words': 2781}