123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- /*
- * Copyright (c) 2006-2013 DMDirc Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
- package com.dmdirc.config;
-
- import com.dmdirc.interfaces.ConfigChangeListener;
- import com.dmdirc.util.collections.MapList;
- import com.dmdirc.util.validators.Validator;
-
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import java.util.TreeMap;
-
- import lombok.Getter;
- import lombok.extern.slf4j.Slf4j;
-
- /**
- * The config manager manages the various config sources for each entity.
- */
- @Slf4j
- @SuppressWarnings("PMD.UnusedPrivateField")
- public class ConfigManager extends ConfigSource implements ConfigChangeListener,
- IdentityListener {
-
- /** Temporary map for lookup stats. */
- private static final Map<String, Integer> STATS = new TreeMap<String, Integer>();
-
- /** Magical domain to redirect to the version identity. */
- private static final String VERSION_DOMAIN = "version";
-
- /** A list of sources for this config manager. */
- private final List<Identity> sources;
-
- /** The listeners registered for this manager. */
- private final MapList<String, ConfigChangeListener> listeners
- = new MapList<String, ConfigChangeListener>();
-
- /** The config binder to use for this manager. */
- @Getter
- private final ConfigBinder binder = new ConfigBinder(this);
-
- /** The protocol this manager is for. */
- private String protocol;
- /** The ircd this manager is for. */
- private String ircd;
- /** The network this manager is for. */
- private String network;
- /** The server this manager is for. */
- private String server;
- /** The channel this manager is for. */
- private String channel;
-
- /**
- * Creates a new instance of ConfigManager.
- *
- * @param protocol The protocol for this manager
- * @param ircd The name of the ircd for this manager
- * @param network The name of the network for this manager
- * @param server The name of the server for this manager
- * @since 0.6.3
- */
- public ConfigManager(final String protocol, final String ircd,
- final String network, final String server) {
- this(protocol, ircd, network, server, "<Unknown>");
- }
-
- /**
- * Creates a new instance of ConfigManager.
- *
- * @param protocol The protocol for this manager
- * @param ircd The name of the ircd for this manager
- * @param network The name of the network for this manager
- * @param server The name of the server for this manager
- * @param channel The name of the channel for this manager
- * @since 0.6.3
- */
- public ConfigManager(final String protocol, final String ircd,
- final String network, final String server, final String channel) {
- final String chanName = channel + "@" + network;
-
- this.protocol = protocol;
- this.ircd = ircd;
- this.network = network;
- this.server = server;
- this.channel = chanName;
-
- sources = IdentityManager.getIdentityManager().getIdentitiesForManager(this);
-
- log.debug("Found {} source(s) for protocol: {}, ircd: {}, network: {}, server: {}, channel: {}",
- new Object[] { sources.size(), protocol, ircd, network, server, chanName });
-
- for (Identity identity : sources) {
- log.trace("Found {}", identity);
- identity.addListener(this);
- }
-
- IdentityManager.getIdentityManager().registerIdentityListener(this);
- }
-
- /** {@inheritDoc} */
- @Override
- public String getOption(final String domain, final String option,
- final Validator<String> validator) {
- doStats(domain, option);
-
- if (VERSION_DOMAIN.equals(domain)) {
- return IdentityManager.getIdentityManager()
- .getGlobalVersionIdentity().getOption(domain, option, validator);
- }
-
- synchronized (sources) {
- for (Identity source : sources) {
- if (source.hasOption(domain, option, validator)) {
- return source.getOption(domain, option, validator);
- }
- }
- }
-
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean hasOption(final String domain, final String option,
- final Validator<String> validator) {
- doStats(domain, option);
-
- if (VERSION_DOMAIN.equals(domain)) {
- return IdentityManager.getIdentityManager()
- .getGlobalVersionIdentity().hasOption(domain, option, validator);
- }
-
- synchronized (sources) {
- for (Identity source : sources) {
- if (source.hasOption(domain, option, validator)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Returns the name of all the options in the specified domain. If the
- * domain doesn't exist, an empty list is returned.
- *
- * @param domain The domain to search
- * @return A list of options in the specified domain
- */
- public Map<String, String> getOptions(final String domain) {
- if (VERSION_DOMAIN.equals(domain)) {
- return IdentityManager.getIdentityManager()
- .getGlobalVersionIdentity().getOptions(domain);
- }
-
- final Map<String, String> res = new HashMap<String, String>();
-
- synchronized (sources) {
- for (int i = sources.size() - 1; i >= 0; i--) {
- res.putAll(sources.get(i).getOptions(domain));
- }
- }
-
- return res;
- }
-
- /**
- * Removes the specified identity from this manager.
- *
- * @param identity The identity to be removed
- */
- public void removeIdentity(final Identity identity) {
- if (!sources.contains(identity)) {
- return;
- }
-
- final List<String[]> changed = new ArrayList<String[]>();
-
- // Determine which settings will have changed
- for (String domain : identity.getDomains()) {
- for (String option : identity.getOptions(domain).keySet()) {
- if (identity.equals(getScope(domain, option))) {
- changed.add(new String[]{domain, option});
- }
- }
- }
-
- synchronized (sources) {
- identity.removeListener(this);
- sources.remove(identity);
- }
-
- // Fire change listeners
- for (String[] setting : changed) {
- configChanged(setting[0], setting[1]);
- }
- }
-
- /**
- * Retrieves the identity that currently defines the specified domain and
- * option.
- *
- * @param domain The domain to search for
- * @param option The option to search for
- * @return The identity that defines that setting, or null on failure
- */
- protected Identity getScope(final String domain, final String option) {
- if (VERSION_DOMAIN.equals(domain)) {
- return IdentityManager.getIdentityManager()
- .getGlobalVersionIdentity();
- }
-
- synchronized (sources) {
- for (Identity source : sources) {
- if (source.hasOptionString(domain, option)) {
- return source;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Checks whether the specified identity applies to this config manager.
- *
- * @param identity The identity to test
- * @return True if the identity applies, false otherwise
- */
- public boolean identityApplies(final Identity identity) {
- String comp;
-
- switch (identity.getTarget().getType()) {
- case PROTOCOL:
- comp = protocol;
- break;
- case IRCD:
- comp = ircd;
- break;
- case NETWORK:
- comp = network;
- break;
- case SERVER:
- comp = server;
- break;
- case CHANNEL:
- comp = channel;
- break;
- case CUSTOM:
- // We don't want custom identities
- comp = null;
- break;
- default:
- comp = "";
- break;
- }
-
- final boolean result = comp != null
- && identityTargetMatches(identity.getTarget().getData(), comp);
-
- log.trace("Checking if identity {} applies. Comparison: {}, target: {}, result: {}",
- new Object[] { identity, comp, identity.getTarget().getData(), result });
-
- return result;
- }
-
- /**
- * Determines whether the specified identity target matches the desired
- * target. If the desired target is prefixed with "re:", it is treated
- * as a regular expression; otherwise the strings are compared
- * lexigraphically to determine a match.
- *
- * @param desired The target string required by this config manager
- * @param actual The target string supplied by the identity
- * @return True if the identity should be applied, false otherwise
- * @since 0.6.3m2
- */
- protected boolean identityTargetMatches(final String actual, final String desired) {
- return actual.startsWith("re:") ? desired.matches(actual.substring(3))
- : actual.equalsIgnoreCase(desired);
- }
-
- /**
- * Called whenever there is a new identity available. Checks if the
- * identity is relevant for this manager, and adds it if it is.
- *
- * @param identity The identity to be checked
- */
- public void checkIdentity(final Identity identity) {
- if (!sources.contains(identity) && identityApplies(identity)) {
- synchronized (sources) {
- sources.add(identity);
- identity.addListener(this);
- Collections.sort(sources);
- }
-
- // Determine which settings will have changed
- for (String domain : identity.getDomains()) {
- for (String option : identity.getOptions(domain).keySet()) {
- if (identity.equals(getScope(domain, option))) {
- configChanged(domain, option);
- }
- }
- }
- }
- }
-
- /**
- * Returns the name of all domains known by this manager.
- *
- * @return A list of domains known to this manager
- */
- public Set<String> getDomains() {
- final Set<String> res = new HashSet<String>();
-
- synchronized (sources) {
- for (Identity source : sources) {
- res.addAll(source.getDomains());
- }
- }
-
- return res;
- }
-
- /**
- * Retrieves a list of sources for this config manager.
- * @return This config manager's sources.
- */
- public List<Identity> getSources() {
- return new ArrayList<Identity>(sources);
- }
-
- /**
- * Migrates this ConfigManager from its current configuration to the
- * appropriate one for the specified new parameters, firing listeners where
- * settings have changed.
- *
- * @param protocol The protocol for this manager
- * @param ircd The new name of the ircd for this manager
- * @param network The new name of the network for this manager
- * @param server The new name of the server for this manager
- * @since 0.6.3
- */
- public void migrate(final String protocol, final String ircd,
- final String network, final String server) {
- migrate(protocol, ircd, network, server, "<Unknown>");
- }
-
- /**
- * Migrates this ConfigManager from its current configuration to the
- * appropriate one for the specified new parameters, firing listeners where
- * settings have changed.
- *
- * @param protocol The protocol for this manager
- * @param ircd The new name of the ircd for this manager
- * @param network The new name of the network for this manager
- * @param server The new name of the server for this manager
- * @param channel The new name of the channel for this manager
- * @since 0.6.3
- */
- public void migrate(final String protocol, final String ircd,
- final String network, final String server, final String channel) {
- log.debug("Migrating from {{}, {}, {}, {}, {}} to {{}, {}, {}, {}, {}}",
- new Object[] {
- this.protocol, this.ircd, this.network, this.server, this.channel,
- protocol, ircd, network, server, channel,
- });
-
- this.protocol = protocol;
- this.ircd = ircd;
- this.network = network;
- this.server = server;
- this.channel = channel + "@" + network;
-
- for (Identity identity : new ArrayList<Identity>(sources)) {
- if (!identityApplies(identity)) {
- log.debug("Removing identity that no longer applies: {}", identity);
- removeIdentity(identity);
- }
- }
-
- final List<Identity> newSources = IdentityManager.getIdentityManager()
- .getIdentitiesForManager(this);
- for (Identity identity : newSources) {
- log.trace("Testing new identity: {}", identity);
- checkIdentity(identity);
- }
-
- log.debug("New identities: {}", sources);
- }
-
- /**
- * Records the lookup request for the specified domain & option.
- *
- * @param domain The domain that is being looked up
- * @param option The option that is being looked up
- */
- @SuppressWarnings("PMD.AvoidCatchingNPE")
- protected static void doStats(final String domain, final String option) {
- final String key = domain + "." + option;
-
- try {
- STATS.put(key, 1 + (STATS.containsKey(key) ? STATS.get(key) : 0));
- } catch (NullPointerException ex) {
- // JVM bugs ftl.
- }
- }
-
- /**
- * Retrieves the statistic map.
- *
- * @return A map of config options to lookup counts
- */
- public static Map<String, Integer> getStats() {
- return STATS;
- }
-
- /**
- * Adds a change listener for the specified domain.
- *
- * @param domain The domain to be monitored
- * @param listener The listener to register
- */
- public void addChangeListener(final String domain,
- final ConfigChangeListener listener) {
- addListener(domain, listener);
- }
-
- /**
- * Adds a change listener for the specified domain and key.
- *
- * @param domain The domain of the option
- * @param key The option to be monitored
- * @param listener The listener to register
- */
- public void addChangeListener(final String domain, final String key,
- final ConfigChangeListener listener) {
- addListener(domain + "." + key, listener);
- }
-
- /**
- * Removes the specified listener for all domains and options.
- *
- * @param listener The listener to be removed
- */
- public void removeListener(final ConfigChangeListener listener) {
- synchronized (listeners) {
- listeners.removeFromAll(listener);
- }
- }
-
- /**
- * Adds the specified listener to the internal map/list.
- *
- * @param key The key to use (domain or domain.key)
- * @param listener The listener to register
- */
- private void addListener(final String key,
- final ConfigChangeListener listener) {
- synchronized (listeners) {
- listeners.add(key, listener);
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void configChanged(final String domain, final String key) {
- final List<ConfigChangeListener> targets
- = new ArrayList<ConfigChangeListener>();
-
- if (listeners.containsKey(domain)) {
- targets.addAll(listeners.get(domain));
- }
-
- if (listeners.containsKey(domain + "." + key)) {
- targets.addAll(listeners.get(domain + "." + key));
- }
-
- for (ConfigChangeListener listener : targets) {
- listener.configChanged(domain, key);
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void identityAdded(final Identity identity) {
- checkIdentity(identity);
- }
-
- /** {@inheritDoc} */
- @Override
- public void identityRemoved(final Identity identity) {
- removeIdentity(identity);
- }
-
- }
|