File Source: ConfigManager.java

     1  /*
     2   * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy
     5   * of this software and associated documentation files (the "Software"), to deal
     6   * in the Software without restriction, including without limitation the rights
     7   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   * copies of the Software, and to permit persons to whom the Software is
     9   * furnished to do so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in
    12   * all copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  
    23  package com.dmdirc.config;
    24  
    25  import com.dmdirc.interfaces.ConfigChangeListener;
    26  import com.dmdirc.util.MapList;
    27  
    28  import java.io.Serializable;
    29  import java.util.ArrayList;
    30  import java.util.Collections;
    31  import java.util.HashMap;
    32  import java.util.HashSet;
    33  import java.util.List;
    34  import java.util.Map;
    35  import java.util.Set;
    36  import java.util.TreeMap;
    37  
    38  /**
    39   * The config manager manages the various config sources for each entity.
    40   *
    41   * @author chris
    42   */
    43  public class ConfigManager extends ConfigSource implements Serializable,
    44          ConfigChangeListener {
    45  
    46      /**
    47       * A version number for this class. It should be changed whenever the class
    48       * structure is changed (or anything else that would prevent serialized
    49       * objects being unserialized with the new class).
    50       */
    51      private static final long serialVersionUID = 4;
    52  
    53      /** Temporary map for lookup stats. */
             /* 
    P/P       *  Method: com.dmdirc.config.ConfigManager__static_init
              * 
              *  Postconditions:
              *    stats == &new TreeMap(ConfigManager__static_init#1)
              *    new TreeMap(ConfigManager__static_init#1) num objects == 1
              */
    54      private static final Map<String, Integer> stats = new TreeMap<String, Integer>();
    55  
    56      /** A list of sources for this config manager. */
    57      private final List<Identity> sources;
    58  
    59      /** The listeners registered for this manager. */
    60      private final MapList<String, ConfigChangeListener> listeners
    61              = new MapList<String, ConfigChangeListener>();
    62  
    63      /** The ircd this manager is for. */
    64      private String ircd;
    65      /** The network this manager is for. */
    66      private String network;
    67      /** The server this manager is for. */
    68      private String server;
    69      /** The channel this manager is for. */
    70      private String channel;
    71  
    72      /**
    73       * Creates a new instance of ConfigManager.
    74       *
    75       * @param ircd The name of the ircd for this manager
    76       * @param network The name of the network for this manager
    77       * @param server The name of the server for this manager
    78       */
    79      public ConfigManager(final String ircd, final String network,
    80              final String server) {
                 /* 
    P/P           *  Method: void com.dmdirc.config.ConfigManager(String, String, String)
                  * 
                  *  Preconditions:
                  *    (soft) init'ed(com.dmdirc.config.ConfigManager$1__static_init.new int[](ConfigManager$1__static_init#1)[...])
                  * 
                  *  Postconditions:
                  *    java.lang.StringBuilder:toString(...)._tainted == network._tainted
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    this.channel == &amp;java.lang.StringBuilder:toString(...)
                  *    this.ircd == ircd
                  *    init'ed(this.ircd)
                  *    this.listeners == &amp;new MapList(ConfigManager#1)
                  *    this.network == network
                  *    init'ed(this.network)
                  *    this.server == server
                  *    init'ed(this.server)
                  *    ...
                  */
    81          this(ircd, network, server, "<Unknown>");
    82      }
    83  
    84      /**
    85       * Creates a new instance of ConfigManager.
    86       *
    87       * @param ircd The name of the ircd for this manager
    88       * @param network The name of the network for this manager
    89       * @param server The name of the server for this manager
    90       * @param channel The name of the channel for this manager
    91       */
    92      public ConfigManager(final String ircd, final String network,
                     /* 
    P/P               *  Method: void com.dmdirc.config.ConfigManager(String, String, String, String)
                      * 
                      *  Preconditions:
                      *    (soft) init'ed(com.dmdirc.config.ConfigManager$1__static_init.new int[](ConfigManager$1__static_init#1)[...])
                      * 
                      *  Presumptions:
                      *    identity.listeners@102 != null
                      *    java.util.Iterator:next(...)@102 != null
                      * 
                      *  Postconditions:
                      *    java.lang.StringBuilder:toString(...)._tainted == channel._tainted | network._tainted
                      *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                      *    this.channel == &amp;java.lang.StringBuilder:toString(...)
                      *    this.ircd == ircd
                      *    init'ed(this.ircd)
                      *    this.listeners == &amp;new MapList(ConfigManager#1)
                      *    this.network == network
                      *    init'ed(this.network)
                      *    this.server == server
                      *    init'ed(this.server)
                      *    ...
                      * 
                      *  Test Vectors:
                      *    java.util.Iterator:hasNext(...)@102: {0}, {1}
                      */
    93              final String server, final String channel) {
    94          final String chanName = channel + "@" + network;
    95          this.ircd = ircd;
    96          this.network = network;
    97          this.server = server;
    98          this.channel = chanName;
    99          
   100          sources = IdentityManager.getSources(this);
   101  
   102          for (Identity identity : sources) {
   103              identity.addListener(this);
   104          }
   105  
   106          IdentityManager.addConfigManager(this);
   107      }
   108  
   109      /** {@inheritDoc} */
   110      @Override
   111      public String getOption(final String domain, final String option) {
                 /* 
    P/P           *  Method: String getOption(String, String)
                  * 
                  *  Preconditions:
                  *    this.sources != null
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:hasNext(...)@115 == 1
                  *    java.util.Iterator:next(...)@115 != null
                  *    source.file@115 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   112          doStats(domain, option);
   113  
   114          synchronized (sources) {
   115              for (Identity source : sources) {
   116                  if (source.hasOption(domain, option)) {
   117                      return source.getOption(domain, option);
   118                  }
   119              }
   120          }
   121  
   122          throw new IllegalArgumentException("Config option not found: " + domain + "." + option);
   123      }
   124  
   125      /** {@inheritDoc} */
   126      @Override
   127      protected boolean hasOption(final String domain, final String option) {
                 /* 
    P/P           *  Method: bool hasOption(String, String)
                  * 
                  *  Preconditions:
                  *    this.sources != null
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:next(...)@131 != null
                  *    source.file@131 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@131: {1}, {0}
                  */
   128          doStats(domain, option);
   129  
   130          synchronized (sources) {
   131              for (Identity source : sources) {
   132                  if (source.hasOption(domain, option)) {
   133                      return true;
   134                  }
   135              }
   136          }
   137  
   138          return false;
   139      }
   140  
   141      /**
   142       * Returns the name of all the options in the specified domain. If the
   143       * domain doesn't exist, an empty list is returned.
   144       *
   145       * @param domain The domain to search
   146       * @return A list of options in the specified domain
   147       */
   148      public Map<String, String> getOptions(final String domain) {
                 /* 
    P/P           *  Method: Map getOptions(String)
                  * 
                  *  Preconditions:
                  *    this.sources != null
                  * 
                  *  Presumptions:
                  *    java.util.List:get(...).file@153 != null
                  *    java.util.List:get(...)@153 != null
                  *    java.util.List:size(...)@152 >= -231+1
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new HashMap(getOptions#1)
                  *    new HashMap(getOptions#1) num objects == 1
                  */
   149          final Map<String, String> res = new HashMap<String, String>();
   150  
   151          synchronized (sources) {
   152              for (int i = sources.size() - 1; i >= 0; i--) {
   153                 res.putAll(sources.get(i).getOptions(domain));
   154              }
   155          }
   156  
   157          return res;
   158      }
   159  
   160      /**
   161       * Removes the specified identity from this manager.
   162       *
   163       * @param identity The identity to be removed
   164       */
   165      public void removeIdentity(final Identity identity) {
                 /* 
    P/P           *  Method: void removeIdentity(Identity)
                  * 
                  *  Preconditions:
                  *    this.sources != null
                  *    (soft) identity != null
                  *    (soft) identity.file != null
                  *    (soft) identity.listeners != null
                  *    (soft) this.listeners != null
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:next(...)@187 != null
                  *    java.util.Map:keySet(...)@174 != null
                  *    setting.length@187 >= 2
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@173: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@174: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@187: {0}, {1}
                  *    java.util.List:contains(...)@166: {1}, {0}
                  */
   166          if (!sources.contains(identity)) {
   167              return;
   168          }
   169  
   170          final List<String[]> changed = new ArrayList<String[]>();
   171  
   172          // Determine which settings will have changed
   173          for (String domain : identity.getDomains()) {
   174              for (String option : identity.getOptions(domain).keySet()) {
   175                  if (identity.equals(getScope(domain, option))) {
   176                      changed.add(new String[]{domain, option});
   177                  }
   178              }
   179          }
   180  
   181          synchronized (sources) {
   182              identity.removeListener(this);
   183              sources.remove(identity);
   184          }
   185  
   186          // Fire change listeners
   187          for (String[] setting : changed) {
   188              configChanged(setting[0], setting[1]);
   189          }
   190      }
   191  
   192      /**
   193       * Retrieves the identity that currently defines the specified domain and
   194       * option.
   195       *
   196       * @param domain The domain to search for
   197       * @param option The option to search for
   198       * @return The identity that defines that setting, or null on failure
   199       */
   200      protected Identity getScope(final String domain, final String option) {
                 /* 
    P/P           *  Method: Identity getScope(String, String)
                  * 
                  *  Preconditions:
                  *    this.sources != null
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:next(...)@202 != null
                  *    source.file@202 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@202: {1}, {0}
                  */
   201          synchronized (sources) {
   202              for (Identity source : sources) {
   203                  if (source.hasOption(domain, option)) {
   204                      return source;
   205                  }
   206              }
   207          }
   208  
   209          return null;
   210      }
   211      
   212      /**
   213       * Checks whether the specified identity applies to this config manager.
   214       * 
   215       * @param identity The identity to test
   216       * @return True if the identity applies, false otherwise
   217       */
   218      public boolean identityApplies(final Identity identity) {
   219          String comp;
   220  
                 /* 
    P/P           *  Method: bool identityApplies(Identity)
                  * 
                  *  Preconditions:
                  *    identity != null
                  *    identity.myTarget != null
                  *    identity.myTarget.type != null
                  *    (soft) init'ed(com.dmdirc.config.ConfigManager$1__static_init.new int[](ConfigManager$1__static_init#1)[...])
                  *    (soft) init'ed(identity.myTarget.data)
                  *    (soft) init'ed(this.channel)
                  *    (soft) init'ed(this.ircd)
                  *    (soft) init'ed(this.network)
                  *    (soft) init'ed(this.server)
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.ConfigTarget_TYPE:ordinal(...)@221 in {0..7}
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager$1__static_init.new int[](ConfigManager$1__static_init#1)[...]: {1}, {2}, {3}, {4}, {5}, {-231..0, 6..232-1}
                  */
   221          switch (identity.getTarget().getType()) {
   222          case IRCD:
   223              comp = ircd;
   224              break;
   225          case NETWORK:
   226              comp = network;
   227              break;
   228          case SERVER:
   229              comp = server;
   230              break;
   231          case CHANNEL:
   232              comp = channel;
   233              break;
   234          case PROFILE:
   235              // We don't want profiles
   236              comp = null;
   237              break;
   238          default:
   239              comp = "";
   240              break;
   241          }
   242  
   243          return comp != null && comp.equalsIgnoreCase(identity.getTarget().getData());
   244      }
   245  
   246      /**
   247       * Called whenever there is a new identity available. Checks if the
   248       * identity is relevant for this manager, and adds it if it is.
   249       * 
   250       * @param identity The identity to be checked
   251       */
   252      public void checkIdentity(final Identity identity) {
                 /* 
    P/P           *  Method: void checkIdentity(Identity)
                  * 
                  *  Preconditions:
                  *    this.sources != null
                  *    (soft) init'ed(com.dmdirc.config.ConfigManager$1__static_init.new int[](ConfigManager$1__static_init#1)[...])
                  *    (soft) identity != null
                  *    (soft) identity.file != null
                  *    (soft) identity.listeners != null
                  *    (soft) identity.myTarget != null
                  *    (soft) init'ed(identity.myTarget.data)
                  *    (soft) identity.myTarget.type != null
                  *    (soft) init'ed(this.channel)
                  *    (soft) init'ed(this.ircd)
                  *    ...
                  * 
                  *  Presumptions:
                  *    java.util.Map:keySet(...)@262 != null
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@261: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@262: {0}, {1}
                  *    java.util.List:contains(...)@253: {1}, {0}
                  */
   253          if (!sources.contains(identity) && identityApplies(identity)) {
   254              synchronized (sources) {
   255                  sources.add(identity);
   256                  identity.addListener(this);
   257                  Collections.sort(sources);
   258              }
   259              
   260              // Determine which settings will have changed
   261              for (String domain : identity.getDomains()) {
   262                  for (String option : identity.getOptions(domain).keySet()) {
   263                      if (identity.equals(getScope(domain, option))) {
   264                          configChanged(domain, option);
   265                      }
   266                  }
   267              }            
   268          }
   269      }
   270  
   271      /**
   272       * Returns the name of all domains known by this manager.
   273       *
   274       * @return A list of domains known to this manager
   275       */
   276      public Set<String> getDomains() {
                 /* 
    P/P           *  Method: Set getDomains()
                  * 
                  *  Preconditions:
                  *    this.sources != null
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:next(...)@280 != null
                  *    source.file@280 != null
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new HashSet(getDomains#1)
                  *    new HashSet(getDomains#1) num objects == 1
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@280: {1}, {0}
                  */
   277          final Set<String> res = new HashSet<String>();
   278  
   279          synchronized (sources) {
   280              for (Identity source : sources) {
   281                  res.addAll(source.getDomains());
   282              }
   283          }
   284          
   285          return res;
   286      }
   287  
   288      /**
   289       * Retrieves a list of sources for this config manager.
   290       * @return This config manager's sources.
   291       */
   292      public List<Identity> getSources() {
                 /* 
    P/P           *  Method: List getSources()
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new ArrayList(getSources#1)
                  *    new ArrayList(getSources#1) num objects == 1
                  */
   293          return new ArrayList<Identity>(sources);
   294      }
   295  
   296      /**
   297       * Migrates this ConfigManager from its current configuration to the
   298       * appropriate one for the specified new parameters, firing listeners where
   299       * settings have changed.
   300       *
   301       * @param ircd The new name of the ircd for this manager
   302       * @param network The new name of the network for this manager
   303       * @param server The new name of the server for this manager
   304       */
   305      public void migrate(final String ircd, final String network, final String server) {
                 /* 
    P/P           *  Method: void migrate(String, String, String)
                  * 
                  *  Preconditions:
                  *    (soft) init'ed(com.dmdirc.config.ConfigManager$1__static_init.new int[](ConfigManager$1__static_init#1)[...])
                  *    (soft) this.listeners != null
                  *    (soft) this.sources != null
                  * 
                  *  Postconditions:
                  *    java.lang.StringBuilder:toString(...)._tainted == network._tainted
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    this.channel == &amp;java.lang.StringBuilder:toString(...)
                  *    this.ircd == ircd
                  *    init'ed(this.ircd)
                  *    this.network == network
                  *    init'ed(this.network)
                  *    this.server == server
                  *    init'ed(this.server)
                  */
   306          migrate(ircd, network, server, "<Unknown>");
   307      }
   308  
   309      /**
   310       * Migrates this ConfigManager from its current configuration to the
   311       * appropriate one for the specified new parameters, firing listeners where
   312       * settings have changed.
   313       *
   314       * @param ircd The new name of the ircd for this manager
   315       * @param network The new name of the network for this manager
   316       * @param server The new name of the server for this manager
   317       * @param channel The new name of the channel for this manager
   318       */
   319      public void migrate(final String ircd, final String network, final String server,
   320              final String channel) {
                 /* 
    P/P           *  Method: void migrate(String, String, String, String)
                  * 
                  *  Preconditions:
                  *    (soft) init'ed(com.dmdirc.config.ConfigManager$1__static_init.new int[](ConfigManager$1__static_init#1)[...])
                  *    (soft) this.listeners != null
                  *    (soft) this.sources != null
                  * 
                  *  Presumptions:
                  *    identity.file@326 != null
                  *    identity.file@333 != null
                  *    identity.listeners@326 != null
                  *    identity.listeners@333 != null
                  *    identity.myTarget.type@326 != null
                  *    ...
                  * 
                  *  Postconditions:
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    this.channel == &amp;java.lang.StringBuilder:toString(...)
                  *    this.ircd == ircd
                  *    init'ed(this.ircd)
                  *    this.network == network
                  *    init'ed(this.network)
                  *    this.server == server
                  *    init'ed(this.server)
                  * 
                  *  Test Vectors:
                  *    java.util.Iterator:hasNext(...)@326: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@333: {0}, {1}
                  */
   321          this.ircd = ircd;
   322          this.network = network;
   323          this.server = server;
   324          this.channel = channel + "@" + network;
   325  
   326          for (Identity identity : new ArrayList<Identity>(sources)) {
   327              if (!identityApplies(identity)) {
   328                  removeIdentity(identity);
   329              }
   330          }
   331  
   332          final List<Identity> newSources = IdentityManager.getSources(this);
   333          for (Identity identity : newSources) {
   334              checkIdentity(identity);
   335          }
   336      }
   337  
   338      /**
   339       * Records the lookup request for the specified domain & option.
   340       *
   341       * @param domain The domain that is being looked up
   342       * @param option The option that is being looked up
   343       */
   344      protected static void doStats(final String domain, final String option) {
                 /* 
    P/P           *  Method: void doStats(String, String)
                  * 
                  *  Presumptions:
                  *    java.lang.Integer:intValue(...)@348 <= 232-2
                  *    java.util.Map:get(...)@348 != null
                  */
   345          final String key = domain + "." + option;
   346  
   347          try {
   348              stats.put(key, 1 + (stats.containsKey(key) ? stats.get(key) : 0));
   349          } catch (NullPointerException ex) {
   350              // JVM bugs ftl.
   351          }
   352      }
   353  
   354      /**
   355       * Retrieves the statistic map.
   356       *
   357       * @return A map of config options to lookup counts
   358       */
   359      public static Map<String, Integer> getStats() {
                 /* 
    P/P           *  Method: Map getStats()
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new TreeMap(ConfigManager__static_init#1)
                  */
   360          return stats;
   361      }
   362  
   363      /**
   364       * Adds a change listener for the specified domain.
   365       *
   366       * @param domain The domain to be monitored
   367       * @param listener The listener to register
   368       */
   369      public void addChangeListener(final String domain,
   370              final ConfigChangeListener listener) {
                 /* 
    P/P           *  Method: void addChangeListener(String, ConfigChangeListener)
                  * 
                  *  Preconditions:
                  *    this.listeners != null
                  */
   371          addListener(domain, listener);
   372      }
   373  
   374      /**
   375       * Adds a change listener for the specified domain and key.
   376       *
   377       * @param domain The domain of the option
   378       * @param key The option to be monitored
   379       * @param listener The listener to register
   380       */
   381      public void addChangeListener(final String domain, final String key,
   382              final ConfigChangeListener listener) {
                 /* 
    P/P           *  Method: void addChangeListener(String, String, ConfigChangeListener)
                  * 
                  *  Preconditions:
                  *    this.listeners != null
                  */
   383          addListener(domain + "." + key, listener);
   384      }
   385  
   386      /**
   387       * Removes the specified listener for all domains and options.
   388       *
   389       * @param listener The listener to be removed
   390       */
   391      public void removeListener(final ConfigChangeListener listener) {
                 /* 
    P/P           *  Method: void removeListener(ConfigChangeListener)
                  * 
                  *  Preconditions:
                  *    this.listeners != null
                  */
   392          synchronized (listeners) {
   393              listeners.removeFromAll(listener);
   394          }
   395      }
   396  
   397      /**
   398       * Adds the specified listener to the internal map/list.
   399       *
   400       * @param key The key to use (domain or domain.key)
   401       * @param listener The listener to register
   402       */
   403      private void addListener(final String key,
   404              final ConfigChangeListener listener) {
                 /* 
    P/P           *  Method: void addListener(String, ConfigChangeListener)
                  * 
                  *  Preconditions:
                  *    this.listeners != null
                  */
   405          synchronized (listeners) {
   406              listeners.add(key, listener);
   407          }
   408      }
   409  
   410      /** {@inheritDoc} */
   411      @Override
   412      public void configChanged(final String domain, final String key) {
                 /* 
    P/P           *  Method: void configChanged(String, String)
                  * 
                  *  Preconditions:
                  *    this.listeners != null
                  * 
                  *  Presumptions:
                  *    java.util.Iterator:next(...)@424 != null
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.util.MapList:containsKey(...)@416: {0}, {1}
                  *    com.dmdirc.util.MapList:containsKey(...)@420: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@424: {0}, {1}
                  */
   413          final List<ConfigChangeListener> targets
   414                  = new ArrayList<ConfigChangeListener>();
   415  
   416          if (listeners.containsKey(domain)) {
   417              targets.addAll(listeners.get(domain));
   418          }
   419  
   420          if (listeners.containsKey(domain + "." + key)) {
   421              targets.addAll(listeners.get(domain + "." + key));
   422          }
   423  
   424          for (ConfigChangeListener listener : targets) {
   425              listener.configChanged(domain, key);
   426          }
   427      }
   428  }








SofCheck Inspector Build Version : 2.17854
ConfigManager.java 2009-Jun-25 01:54:24
ConfigManager.class 2009-Sep-02 17:04:17
ConfigManager$1.class 2009-Sep-02 17:04:17