001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.webconsole.base;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.net.URL;
024import java.net.URLConnection;
025import java.util.List;
026import java.util.ListIterator;
027import java.util.Locale;
028import java.util.Properties;
029import java.util.ResourceBundle;
030import java.util.ResourceBundle.Control;
031
032/**
033 * A {@link Control} that implements a special lookup
034 * algorithm. See {@link #newBundle}. 
035 */
036public class ConsoleResourceBundleControl extends Control {
037
038    private final List<Class<?>> clses;
039
040    /**
041     * Instantiates a new web console resource bundle control.
042     *
043     * @param clses the classes to use
044     */
045    public ConsoleResourceBundleControl(List<Class<?>> clses) {
046        super();
047        this.clses = clses;
048    }
049
050    @Override
051    public List<String> getFormats(String baseName) {
052        if (baseName == null) {
053            throw new NullPointerException(); // NOPMD
054        }
055        return FORMAT_PROPERTIES;
056    }
057
058    /**
059     * Returns `null` (no fallback).
060     */
061    @Override
062    public Locale getFallbackLocale(String baseName, Locale locale) {
063        return null;
064    }
065
066    /**
067     * Creates a new resource bundle using the classes passed to the
068     * constructor and the base name. For each class (in reverse order)
069     * an attempt is made to load a properties file relative to the
070     * class. If found, the entries are merged with any already existing
071     * entries. The class list usually consists of the web console class
072     * and its ancestor classes. Using this controller, derived classes
073     * can thus override resources from their base classes.
074     */
075    @Override
076    @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis",
077        "PMD.AvoidInstantiatingObjectsInLoops", "PMD.CognitiveComplexity" })
078    public ResourceBundle newBundle(String baseName, Locale locale,
079            String format, ClassLoader loader, boolean reload)
080            throws IllegalAccessException, InstantiationException, IOException {
081        String bundleName = toBundleName(baseName, locale);
082        ResourceBundle bundle = null;
083        final String resourceName = toResourceName(bundleName, "properties");
084        ListIterator<Class<?>> iter = clses.listIterator(clses.size());
085        Properties props = new Properties();
086        while (iter.hasPrevious()) {
087            Class<?> cls = iter.previous();
088            @SuppressWarnings("PMD.CloseResource")
089            InputStream inStream = null;
090            if (reload) {
091                URL url = cls.getResource(resourceName);
092                if (url != null) {
093                    URLConnection connection = url.openConnection();
094                    if (connection != null) {
095                        // Disable caches to get fresh data for
096                        // reloading.
097                        connection.setUseCaches(false);
098                        inStream = connection.getInputStream();
099                    }
100                }
101            } else {
102                inStream = cls.getResourceAsStream(resourceName);
103            }
104            if (inStream == null) {
105                continue;
106            }
107            props.load(inStream);
108            inStream.close();
109            if (bundle == null) {
110                bundle = new ConsoleResourceBundle(props);
111            }
112        }
113        return bundle;
114    }
115}