File Source: Action.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.actions;
    24  
    25  import com.dmdirc.actions.interfaces.ActionType;
    26  import com.dmdirc.actions.interfaces.ActionComponent;
    27  import com.dmdirc.actions.interfaces.ActionComparison;
    28  import com.dmdirc.config.prefs.PreferencesSetting;
    29  import com.dmdirc.config.prefs.PreferencesType;
    30  import com.dmdirc.logger.ErrorLevel;
    31  import com.dmdirc.logger.Logger;
    32  import com.dmdirc.util.ConfigFile;
    33  import com.dmdirc.util.InvalidConfigFileException;
    34  
    35  import java.io.File;
    36  import java.io.IOException;
    37  import java.io.Serializable;
    38  import java.util.ArrayList;
    39  import java.util.Arrays;
    40  import java.util.HashMap;
    41  import java.util.List;
    42  import java.util.Map;
    43  
    44  /**
    45   * Describes a single action.
    46   *
    47   * @author chris
    48   */
    49  public class Action extends ActionModel implements Serializable {
    50  
    51      /**
    52       * A version number for this class. It should be changed whenever the class
    53       * structure is changed (or anything else that would prevent serialized
    54       * objects being unserialized with the new class).
    55       */
    56      private static final long serialVersionUID = 1;
    57  
    58      /** The domain name for condition trees. */
             /* 
    P/P       *  Method: com.dmdirc.actions.Action__static_init
              * 
              *  Postconditions:
              *    DOMAIN_CONDITIONTREE != null
              *    DOMAIN_FORMAT != null
              *    DOMAIN_METADATA != null
              *    DOMAIN_RESPONSE != null
              *    DOMAIN_TRIGGERS != null
              */
    59      private static final String DOMAIN_CONDITIONTREE = "conditiontree".intern();
    60      /** The domain name for format changes. */
    61      private static final String DOMAIN_FORMAT = "format".intern();
    62      /** The domain name for meta-data. */
    63      private static final String DOMAIN_METADATA = "metadata".intern();
    64      /** The domain name for response information. */
    65      private static final String DOMAIN_RESPONSE = "response".intern();
    66      /** The domain name for triggers. */
    67      private static final String DOMAIN_TRIGGERS = "triggers".intern();
    68  
    69      /** The location of the file we're reading/saving. */
    70      private String location;
    71  
    72      /** The config file we're using. */
    73      protected ConfigFile config;
    74  
    75      /**
    76       * Creates a new instance of Action. The group and name specified must
    77       * be the group and name of a valid action already saved to disk.
    78       *
    79       * @param group The group the action belongs to
    80       * @param name The name of the action
    81       */
    82      public Action(final String group, final String name) {
                 /* 
    P/P           *  Method: void com.dmdirc.actions.Action(String, String)
                  * 
                  *  Preconditions:
                  *    init'ed(com/dmdirc/Main.configdir)
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.logger.ErrorLevel.HIGH)
                  *    init'ed(com.dmdirc.logger.ErrorLevel.MEDIUM)
                  *    init'ed(java.io.File.separator)
                  * 
                  *  Postconditions:
                  *    com/dmdirc/Main.configdir == One-of{old com/dmdirc/Main.configdir, &java.lang.StringBuilder:toString(...)}
                  *    com/dmdirc/Main.configdir != null
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    java.lang.StringBuilder:toString(...)._tainted == 0
                  *    this.conditions == &new ArrayList(ActionModel#1)
                  *    init'ed(this.config)
                  *    this.group == group
                  *    init'ed(this.group)
                  *    this.location == &java.lang.StringBuilder:toString(...)
                  *    this.name == name
                  *    ...
                  */
    83          super(group, name);
    84  
    85          location = ActionManager.getDirectory() + group + File.separator + name;
    86  
    87          try {
    88              config = new ConfigFile(location);
    89              config.read();
    90              loadActionFromConfig();
    91          } catch (InvalidConfigFileException ex) {
    92              // This isn't a valid config file. Maybe it's a properties file?
    93              Logger.userError(ErrorLevel.MEDIUM, "Unable to parse action file: "
    94                      + group + "/" + name + ": " + ex.getMessage());
    95          } catch (IOException ex) {
    96              Logger.userError(ErrorLevel.HIGH, "I/O error when loading action: "
    97                      + group + "/" + name + ": " + ex.getMessage());
    98          }
    99      }
   100  
   101      /**
   102       * Creates a new instance of Action with the specified properties and saves
   103       * it to disk.
   104       *
   105       * @param group The group the action belongs to
   106       * @param name The name of the action
   107       * @param triggers The triggers to use
   108       * @param response The response to use
   109       * @param conditions The conditions to use
   110       * @param newFormat The new formatter to use
   111       */
   112      public Action(final String group, final String name,
   113              final ActionType[] triggers, final String[] response,
   114              final List<ActionCondition> conditions, final String newFormat) {
                 /* 
    P/P           *  Method: void com.dmdirc.actions.Action(String, String, ActionType[], String[], List, String)
                  * 
                  *  Preconditions:
                  *    conditions != null
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    java.lang.StringBuilder:toString(...)._tainted == 0
                  *    new ConditionTree(readTerm#1)._tainted == 0
                  *    init'ed(this.conditionTree)
                  *    init'ed(this.conditions)
                  *    init'ed(this.config)
                  *    init'ed(this.group)
                  *    init'ed(this.location)
                  *    init'ed(this.modified)
                  *    init'ed(this.name)
                  *    ...
                  */
   115          this(group, name, triggers, response, conditions,
   116                  ConditionTree.createConjunction(conditions.size()), newFormat);
   117      }
   118  
   119      /**
   120       * Creates a new instance of Action with the specified properties and saves
   121       * it to disk.
   122       *
   123       * @param group The group the action belongs to
   124       * @param name The name of the action
   125       * @param triggers The triggers to use
   126       * @param response The response to use
   127       * @param conditions The conditions to use
   128       * @param conditionTree The condition tree to use
   129       * @param newFormat The new formatter to use
   130       */
   131      public Action(final String group, final String name,
   132              final ActionType[] triggers, final String[] response,
   133              final List<ActionCondition> conditions,
   134              final ConditionTree conditionTree, final String newFormat) {
                 /* 
    P/P           *  Method: void com.dmdirc.actions.Action(String, String, ActionType[], String[], List, ConditionTree, String)
                  * 
                  *  Preconditions:
                  *    init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    init'ed(com/dmdirc/Main.configdir)
                  *    response != null
                  *    triggers != null
                  *    (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#71).type != null
                  *    (soft) conditions init'ed
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  *    (soft) init'ed(response[...])
                  *    (soft) triggers.length <= 232-1
                  *    (soft) init'ed(triggers[...])
                  * 
                  *  Presumptions:
                  *    init'ed(java.io.File.separator)
                  * 
                  *  Postconditions:
                  *    com/dmdirc/Main.configdir == One-of{old com/dmdirc/Main.configdir, &amp;java.lang.StringBuilder:toString(...)}
                  *    com/dmdirc/Main.configdir != null
                  *    init'ed(com/dmdirc/ServerManager.me)
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    java.lang.StringBuilder:toString(...)._tainted == 0
                  *    this.conditionTree == conditionTree
                  *    init'ed(this.conditionTree)
                  *    this.conditions == conditions
                  *    init'ed(this.conditions)
                  *    init'ed(this.config)
                  *    ...
                  */
   135          super(group, name, triggers, response, conditions, conditionTree, newFormat);
   136  
   137          final String dir = ActionManager.getDirectory() + group + File.separator;
   138          location = dir + name;
   139  
   140          new File(dir).mkdirs();
   141          
   142          ActionManager.processEvent(CoreActionType.ACTION_CREATED, null, this);
   143  
   144          save();
   145          
   146          ActionManager.registerAction(this);
   147      }
   148  
   149      /**
   150       * Loads this action from the config instance.
   151       */
   152      protected void loadActionFromConfig() {
                 /* 
    P/P           *  Method: void loadActionFromConfig()
                  * 
                  *  Preconditions:
                  *    this.config != null
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  *    (soft) init'ed(this.triggers[0])
                  *    (soft) init'ed(this.triggers[...])
                  *    (soft) this.conditions != null
                  *    (soft) init'ed(this.group)
                  *    (soft) init'ed(this.name)
                  * 
                  *  Presumptions:
                  *    com.dmdirc.util.ConfigFile:getFlatDomain(...)@154 != null
                  *    com.dmdirc.util.ConfigFile:getFlatDomain(...)@163 != null
                  *    com.dmdirc.util.ConfigFile:getFlatDomain(...)@166 != null
                  *    com.dmdirc.util.ConfigFile:getFlatDomain(...)@175 != null
                  *    com.dmdirc.util.ConfigFile:getFlatDomain(...)@185 != null
                  *    ...
                  * 
                  *  Postconditions:
                  *    init'ed(com/dmdirc/ServerManager.me)
                  *    possibly_updated(this.conditionTree)
                  *    possibly_updated(this.newFormat)
                  *    this.response == One-of{old this.response, &amp;new String[](loadActionFromConfig#1)}
                  *    init'ed(this.response[...])
                  *    this.triggers == One-of{&amp;new ActionType[](loadTriggers#1), old this.triggers}
                  *    init'ed(this.triggers[0])
                  *    init'ed(this.triggers[...])
                  *    new ActionType[](loadTriggers#1) num objects <= 1
                  *    init'ed(new ActionType[](loadTriggers#1).length)
                  *    ...
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.util.ConfigFile:isFlatDomain(...)@153: {0}, {1}
                  *    com.dmdirc.util.ConfigFile:isFlatDomain(...)@162: {0}, {1}
                  *    com.dmdirc.util.ConfigFile:isFlatDomain(...)@174: {0}, {1}
                  *    com.dmdirc.util.ConfigFile:isFlatDomain(...)@185: {0}, {1}
                  *    com.dmdirc.util.ConfigFile:isKeyDomain(...)@179: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@166: {0}, {1}
                  *    java.util.List:size(...)@185: {-231..0}, {1..232-1}
                  */
   153          if (config.isFlatDomain(DOMAIN_TRIGGERS)) {
   154              if (!loadTriggers(config.getFlatDomain(DOMAIN_TRIGGERS))) {
   155                  return;
   156              }
   157          } else {
   158              error("No trigger specified");
   159              return;
   160          }
   161  
   162          if (config.isFlatDomain(DOMAIN_RESPONSE)) {
   163              response = new String[config.getFlatDomain(DOMAIN_RESPONSE).size()];
   164  
   165              int i = 0;
   166              for (String line: config.getFlatDomain(DOMAIN_RESPONSE)) {
   167                  response[i++] = line;
   168              }
   169          } else {
   170              error("No response specified");
   171              return;
   172          }
   173  
   174          if (config.isFlatDomain(DOMAIN_FORMAT)) {
   175              newFormat = config.getFlatDomain(DOMAIN_FORMAT).size() == 0 ? "" :
   176                  config.getFlatDomain(DOMAIN_FORMAT).get(0);
   177          }
   178  
   179          for (int cond = 0; config.isKeyDomain("condition " + cond); cond++) {
   180              if (!readCondition(config.getKeyDomain("condition " + cond))) {
   181                  return;
   182              }
   183          }
   184  
   185          if (config.isFlatDomain(DOMAIN_CONDITIONTREE)
   186                  && config.getFlatDomain(DOMAIN_CONDITIONTREE).size() > 0) {
   187              conditionTree = ConditionTree.parseString(
   188                      config.getFlatDomain(DOMAIN_CONDITIONTREE).get(0));
   189  
   190              if (conditionTree == null) {
   191                  error("Unable to parse condition tree");
   192                  return;
   193              }
   194  
   195              if (conditionTree.getMaximumArgument() >= conditions.size()) {
   196                  error("Condition tree references condition "
   197                          + conditionTree.getMaximumArgument() + " but there are"
   198                          + " only " + conditions.size() + " conditions");
   199                  return;
   200              }
   201          }
   202  
   203          ActionManager.registerAction(this);
   204  
   205          checkMetaData();
   206      }
   207  
   208      /**
   209       * Checks to see if this action contains group meta-data, and adds it to
   210       * the group as appropriate.
   211       */
   212      private void checkMetaData() {
                 /* 
    P/P           *  Method: void checkMetaData()
                  * 
                  *  Preconditions:
                  *    this.config != null
                  *    (soft) init'ed(this.group)
                  * 
                  *  Presumptions:
                  *    com.dmdirc.util.ConfigFile:getKeyDomain(...)@215 != null
                  *    com.dmdirc.util.ConfigFile:getKeyDomain(...)@244 != null
                  *    java.util.Map:get(...)@309 != null
                  *    myGroup.settings != null
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.util.ConfigFile:isKeyDomain(...)@213: {0}, {1}
                  *    com.dmdirc.util.ConfigFile:isKeyDomain(...)@242: {0}, {1}
                  *    java.util.Map:containsKey(...)@217: {0}, {1}
                  *    java.util.Map:containsKey(...)@221: {0}, {1}
                  *    java.util.Map:containsKey(...)@225: {0}, {1}
                  *    java.util.Map:containsKey(...)@233: {0}, {1}
                  *    java.util.Map:containsKey(...)@246: {0}, {1}
                  */
   213          if (config.isKeyDomain(DOMAIN_METADATA)) {
   214              final ActionGroup myGroup = ActionManager.getGroup(group);
   215              final Map<String, String> data = config.getKeyDomain(DOMAIN_METADATA);
   216  
   217              if (data.containsKey("description")) {
   218                  myGroup.setDescription(data.get("description"));
   219              }
   220  
   221              if (data.containsKey("author")) {
   222                  myGroup.setAuthor(data.get("author"));
   223              }
   224              
   225              if (data.containsKey("version")) {
   226                  try {
   227                      myGroup.setVersion(Integer.parseInt(data.get("version")));
   228                  } catch (NumberFormatException ex) {
   229                      // Do nothing
   230                  }
   231              }
   232              
   233              if (data.containsKey("component")) {
   234                  try {
   235                      myGroup.setComponent(Integer.parseInt(data.get("component")));
   236                  } catch (NumberFormatException ex) {
   237                      // Do nothing
   238                  }
   239              }
   240          }
   241  
   242          for (int i = 0; config.isKeyDomain("setting " + i); i++) {
   243              final ActionGroup myGroup = ActionManager.getGroup(group);
   244              final Map<String, String> data = config.getKeyDomain("setting " + i);
   245              
   246              if (data.containsKey("type") && data.containsKey("setting")
   247                      && data.containsKey("title") && data.containsKey("default")
   248                      && data.containsKey("tooltip")) {
   249                  ActionManager.registerDefault(data.get("setting"), data.get("default"));
   250                  myGroup.getSettings().put(data.get("setting"), new PreferencesSetting(
   251                          PreferencesType.valueOf(data.get("type")), "actions",
   252                          data.get("setting"), data.get("title"), data.get("tooltip")));
   253              }
   254          }
   255      }
   256  
   257      /**
   258       * Loads a list of triggers with the specified names.
   259       * 
   260       * @param newTriggers A list of trigger names
   261       * @return True if all triggers are valid and compatible, false otherwise.
   262       */
   263      private boolean loadTriggers(final List<String> newTriggers) {
                 /* 
    P/P           *  Method: bool loadTriggers(List)
                  * 
                  *  Preconditions:
                  *    newTriggers != null
                  *    (soft) init'ed(this.group)
                  *    (soft) init'ed(this.name)
                  * 
                  *  Presumptions:
                  *    java.util.List:size(...)@264 >= 0
                  *    this.triggers[i].type != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  *    this.triggers == &amp;new ActionType[](loadTriggers#1)
                  *    possibly_updated(this.triggers[0])
                  *    possibly_updated(this.triggers[...])
                  *    new ActionType[](loadTriggers#1) num objects == 1
                  *    this.triggers.length <= 232-1
                  *    init'ed(this.triggers[0])
                  * 
                  *  Test Vectors:
                  *    java.lang.Object:equals(...)@272: {1}, {0}
                  */
   264          triggers = new ActionType[newTriggers.size()];
   265  
   266          for (int i = 0; i < triggers.length; i++) {
   267              triggers[i] = ActionManager.getActionType(newTriggers.get(i));
   268  
   269              if (triggers[i] == null) {
   270                  error("Invalid trigger specified: " + newTriggers.get(i));
   271                  return false;
   272              } else if (i != 0 && !triggers[i].getType().equals(triggers[0].getType())) {
   273                  error("Triggers are not compatible");
   274                  return false;
   275              }
   276          }
   277  
   278          return true;
   279      }
   280  
   281      /**
   282       * Called to save the action.
   283       */
   284      public void save() {
                 /* 
    P/P           *  Method: void save()
                  * 
                  *  Preconditions:
                  *    init'ed(this.modified)
                  *    (soft) init'ed(com.dmdirc.actions.ConditionTree$1__static_init.new int[](ConditionTree$1__static_init#1)[...])
                  *    (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#72).type != null
                  *    (soft) init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  *    (soft) init'ed(this.conditionTree)
                  *    (soft) init'ed(this.conditionTree.argument)
                  *    (soft) init'ed(this.conditionTree.leftArg)
                  *    (soft) this.conditionTree.op != null
                  *    (soft) init'ed(this.conditionTree.rightArg)
                  *    ...
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.logger.ErrorLevel.HIGH)
                  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
                  *    com.dmdirc.util.ConfigFile:getFlatDomain(...)@314 != null
                  *    com.dmdirc.util.ConfigFile:getFlatDomain(...)@319 != null
                  *    condition.comparison@323 != null
                  *    ...
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    init'ed(this.modified)
                  *    new ArrayList(ServerManager#1) num objects == 0, if init'ed
                  *    new ServerManager(getServerManager#1) num objects == 0, if init'ed
                  *    new ServerManager(getServerManager#1).servers == null
                  * 
                  *  Test Vectors:
                  *    this.modified: {1}, {0}
                  *    this.conditionTree: Addr_Set{null}, Inverse{null}
                  *    this.config: Addr_Set{null}, Inverse{null}
                  *    this.newFormat: Addr_Set{null}, Inverse{null}
                  *    this.triggers[...]: Inverse{null}, Addr_Set{null}
                  *    com.dmdirc.util.ConfigFile:isKeyDomain(...)@343: {0}, {1}
                  *    com.dmdirc.util.ConfigFile:isKeyDomain(...)@347: {0}, {1}
                  *    condition.arg@323: {-231..-2, 0..232-1}, {-1}
                  *    java.util.Iterator:hasNext(...)@323: {0}, {1}
                  */
   285          if (!isModified()) {
   286              return;
   287          }
   288          
   289          final ConfigFile newConfig = new ConfigFile(location);
   290  
   291          final List<String> triggerNames = new ArrayList<String>();
   292          final List<String> responseLines = new ArrayList<String>();
   293  
   294          for (ActionType trigger : triggers) {
   295              if (trigger == null) {
   296                  Logger.appError(ErrorLevel.LOW, "ActionType was null",
   297                          new IllegalArgumentException("Triggers: "
   298                          + Arrays.toString(triggers)));
   299                  continue;
   300              }
   301  
   302              triggerNames.add(trigger.toString());
   303          }
   304  
   305          for (String line : response) {
   306              responseLines.add(line);
   307          }
   308  
   309          newConfig.addDomain(DOMAIN_TRIGGERS, triggerNames);
   310          newConfig.addDomain(DOMAIN_RESPONSE, responseLines);
   311  
   312          if (conditionTree != null) {
   313              newConfig.addDomain(DOMAIN_CONDITIONTREE, new ArrayList<String>());
   314              newConfig.getFlatDomain(DOMAIN_CONDITIONTREE).add(conditionTree.toString());
   315          }
   316  
   317          if (newFormat != null) {
   318              newConfig.addDomain(DOMAIN_FORMAT, new ArrayList<String>());
   319              newConfig.getFlatDomain(DOMAIN_FORMAT).add(newFormat);
   320          }
   321  
   322          int i = 0;
   323          for (ActionCondition condition : conditions) {
   324              final Map<String, String> data = new HashMap<String, String>();
   325  
   326              data.put("argument", String.valueOf(condition.getArg()));
   327              
   328              if (condition.getArg() == -1) {
   329                  data.put("starget", condition.getStarget());
   330              } else {
   331                  data.put("component", condition.getComponent().toString());
   332              }
   333              
   334              data.put("comparison", condition.getComparison().toString());
   335              data.put("target", condition.getTarget());
   336  
   337              newConfig.addDomain("condition " + i, data);
   338              i++;
   339          }
   340  
   341          if (config != null) {
   342              // Preserve any meta-data
   343              if (config.isKeyDomain(DOMAIN_METADATA)) {
   344                  newConfig.addDomain(DOMAIN_METADATA, config.getKeyDomain(DOMAIN_METADATA));
   345              }
   346  
   347              for (i = 0; config.isKeyDomain("setting " + i); i++) {
   348                  newConfig.addDomain("setting " + i, config.getKeyDomain("setting " + i));
   349              }
   350          }
   351  
   352          try {
   353              newConfig.write();
   354  
   355              resetModified();
   356          } catch (IOException ex) {
   357              Logger.userError(ErrorLevel.HIGH, "I/O error when saving action: "
   358                      + group + "/" + name + ": " + ex.getMessage());
   359          }
   360          ActionManager.processEvent(CoreActionType.ACTION_UPDATED, null, this);
   361      }
   362  
   363      /**
   364       * Reads a condition from the specified configuration section.
   365       * 
   366       * @param data The relevant section of the action configuration
   367       * @return True if the condition is valid, false otherwise
   368       */
   369      private boolean readCondition(final Map<String,String> data) {
                 /* 
    P/P           *  Method: bool readCondition(Map)
                  * 
                  *  Preconditions:
                  *    data != null
                  *    (soft) this.conditions != null
                  *    (soft) init'ed(this.group)
                  *    (soft) init'ed(this.name)
                  *    (soft) this.triggers != null
                  *    (soft) this.triggers.length >= 1
                  *    (soft) this.triggers[0] != null
                  *    (soft) this.triggers[0].type != null
                  * 
                  *  Presumptions:
                  *    appliesTo(...)@414 != null
                  *    getType(...)@385 != getType(...)
                  *    java.util.Map:get(...)@408 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    java.lang.Integer:parseInt(...)@379: {-231..-2}, {0..232-2}, {-1}
                  *    java.lang.Object:equals(...)@414: {0}, {1}
                  *    java.lang.Object:equals(...)@414: {1}, {0}
                  *    java.util.Map:get(...)@393: Inverse{null}, Addr_Set{null}
                  *    java.util.Map:get(...)@422: Inverse{null}, Addr_Set{null}
                  */
   370          int arg = 0;
   371          ActionComponent component = null;
   372          ActionComparison comparison = null;
   373          String target = "";
   374          String starget = null;
   375  
   376          // ------ Read the argument
   377  
   378          try {
   379              arg = Integer.parseInt(data.get("argument"));
   380          } catch (NumberFormatException ex) {
   381              error("Invalid argument number specified: " + data.get("argument"));
   382              return false;
   383          }
   384  
   385          if (arg < -1 || arg >= triggers[0].getType().getArity()) {
   386              error("Invalid argument number specified: " + arg);
   387              return false;
   388          }
   389  
   390          // ------ Read the component or the source
   391  
   392          if (arg == -1) {
   393              starget = data.get("starget");
   394              
   395              if (starget == null) {
   396                  error("No starget specified");
   397                  return false;
   398              }
   399          } else {
   400              component = readComponent(data, arg);
   401              if (component == null) {
   402                  return false;
   403              }            
   404          }
   405  
   406          // ------ Read the comparison
   407  
   408          comparison = ActionManager.getActionComparison(data.get("comparison"));
   409          if (comparison == null) {
   410              error("Invalid comparison specified: " + data.get("comparison"));
   411              return false;
   412          }
   413  
   414          if ((arg != -1 && !comparison.appliesTo().equals(component.getType())) 
   415              || (arg == -1 && !comparison.appliesTo().equals(String.class))) {
   416              error("Comparison cannot be applied to specified component: " + data.get("comparison"));
   417              return false;
   418          }
   419  
   420          // ------ Read the target
   421  
   422          target = data.get("target");
   423  
   424          if (target == null) {
   425              error("No target specified for condition");
   426              return false;
   427          }
   428  
   429          if (arg == -1) {
   430              conditions.add(new ActionCondition(starget, comparison, target));
   431          } else {
   432              conditions.add(new ActionCondition(arg, component, comparison, target));
   433          }
   434          
   435          return true;
   436      }
   437  
   438      /**
   439       * Reads a component from the specified data section for the specified argument.
   440       * 
   441       * @param data The relevant section of the action configuration
   442       * @param arg The argument number that the component should apply to
   443       * @return The corresponding ActionComponent, or null if the specified
   444       * component is invalid.
   445       */
   446      private ActionComponent readComponent(final Map<String, String> data, final int arg) {
                 /* 
    P/P           *  Method: ActionComponent readComponent(Map, int)
                  * 
                  *  Preconditions:
                  *    data != null
                  *    (soft) arg >= 0
                  *    (soft) init'ed(this.group)
                  *    (soft) init'ed(this.name)
                  *    (soft) this.triggers != null
                  *    (soft) this.triggers.length >= 1
                  *    (soft) this.triggers[0] != null
                  *    (soft) this.triggers[0].type != null
                  * 
                  *  Presumptions:
                  *    appliesTo(...)@467 != null
                  *    getArgTypes(...).length@454 >= 1
                  *    arg < getArgTypes(...).length@454
                  *    getArgTypes(...).length@467 >= 1
                  *    arg < getArgTypes(...).length@467
                  *    ...
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  *    new ActionComponentChain(readComponent#1) num objects <= 1
                  *    possibly_updated(new ActionComponentChain(readComponent#1).components)
                  *    new ArrayList(ActionComponentChain#1) num objects <= 1
                  * 
                  *  Test Vectors:
                  *    java.lang.Object:equals(...)@467: {1}, {0}
                  *    java.lang.String:indexOf(...)@450: {-231..-2, 0..232-1}, {-1}
                  */
   447          final String componentName = data.get("component");
   448          ActionComponent component;
   449  
   450          if (componentName.indexOf('.') == -1) {
   451              component = ActionManager.getActionComponent(componentName);
   452          } else {
   453              try {
   454                  component = new ActionComponentChain(triggers[0].getType().getArgTypes()[arg],
   455                          componentName);
   456              } catch (IllegalArgumentException iae) {
   457                  error(iae.getMessage());
   458                  return null;
   459              }
   460          }
   461  
   462          if (component == null) {
   463              error("Unknown component: " + componentName);
   464              return null;
   465          }
   466  
   467          if (!component.appliesTo().equals(triggers[0].getType().getArgTypes()[arg])) {
   468              error("Component cannot be applied to specified arg in condition: " + componentName);
   469              return null;
   470          }
   471  
   472          return component;
   473      }
   474  
   475      /**
   476       * Raises a trivial error, informing the user of the problem.
   477       *
   478       * @param message The message to be raised
   479       */
   480      private void error(final String message) {
                 /* 
    P/P           *  Method: void error(String)
                  * 
                  *  Preconditions:
                  *    init'ed(this.group)
                  *    init'ed(this.name)
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.logger.ErrorLevel.LOW)
                  */
   481          Logger.userError(ErrorLevel.LOW, "Error when parsing action: "
   482                  + group + "/" + name + ": " + message);
   483      }
   484  
   485      /** {@inheritDoc} */
   486      @Override
   487      public void setName(final String newName) {
                 /* 
    P/P           *  Method: void setName(String)
                  * 
                  *  Preconditions:
                  *    init'ed(com/dmdirc/Main.configdir)
                  *    init'ed(this.location)
                  *    init'ed(this.group)
                  * 
                  *  Presumptions:
                  *    init'ed(java.io.File.separator)
                  * 
                  *  Postconditions:
                  *    com/dmdirc/Main.configdir == One-of{old com/dmdirc/Main.configdir, &amp;java.lang.StringBuilder:toString(...)}
                  *    com/dmdirc/Main.configdir != null
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    java.lang.StringBuilder:toString(...)._tainted == 0
                  *    this.location == &amp;java.lang.StringBuilder:toString(...)
                  *    this.modified == 1
                  *    this.name == newName
                  *    init'ed(this.name)
                  */
   488          super.setName(newName);
   489  
   490          new File(location).delete();        
   491          location = ActionManager.getDirectory() + group + File.separator + newName;
   492  
   493          save();
   494      }
   495  
   496      /** {@inheritDoc} */
   497      @Override
   498      public void setGroup(final String newGroup) {
                 /* 
    P/P           *  Method: void setGroup(String)
                  * 
                  *  Preconditions:
                  *    init'ed(com/dmdirc/Main.configdir)
                  *    init'ed(this.location)
                  *    init'ed(this.name)
                  * 
                  *  Presumptions:
                  *    init'ed(java.io.File.separator)
                  * 
                  *  Postconditions:
                  *    com/dmdirc/Main.configdir == One-of{old com/dmdirc/Main.configdir, &amp;java.lang.StringBuilder:toString(...)}
                  *    com/dmdirc/Main.configdir != null
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    java.lang.StringBuilder:toString(...)._tainted == 0
                  *    this.group == newGroup
                  *    init'ed(this.group)
                  *    this.location == &amp;java.lang.StringBuilder:toString(...)
                  *    this.modified == 1
                  */
   499          super.setGroup(newGroup);
   500  
   501          new File(location).delete();
   502  
   503          final String dir = ActionManager.getDirectory() + group + File.separator;
   504          location = dir + name;
   505  
   506          new File(dir).mkdirs();        
   507  
   508          save();
   509      }
   510  
   511      /**
   512       * Deletes this action.
   513       */
   514      public void delete() {
                 /* 
    P/P           *  Method: void delete()
                  * 
                  *  Preconditions:
                  *    init'ed(com/dmdirc/actions/ActionManager.killSwitch)
                  *    init'ed(this.group)
                  *    init'ed(this.location)
                  *    init'ed(this.name)
                  *    (soft) com.dmdirc.actions.CoreActionType__static_init.new CoreActionType(CoreActionType__static_init#73).type != null
                  *    (soft) init'ed(com/dmdirc/ServerManager.me)
                  * 
                  *  Postconditions:
                  *    com/dmdirc/ServerManager.me == old com/dmdirc/ServerManager.me
                  *    new ArrayList(ServerManager#1) num objects == undefined
                  *    new ArrayList(ServerManager#1) num objects == 0, if init'ed
                  *    new ServerManager(getServerManager#1) num objects == new ArrayList(ServerManager#1) num objects
                  *    new ServerManager(getServerManager#1).servers == undefined
                  *    new ServerManager(getServerManager#1).servers == null
                  */
   515          ActionManager.processEvent(CoreActionType.ACTION_DELETED, null, getGroup(), getName());
   516          new File(location).delete();
   517      }
   518  
   519      /** {@inheritDoc} */
   520      @Override
   521      public String toString() {
                 /* 
    P/P           *  Method: String toString()
                  * 
                  *  Preconditions:
                  *    init'ed(this.conditions)
                  *    init'ed(this.group)
                  *    init'ed(this.location)
                  *    init'ed(this.name)
                  *    init'ed(this.newFormat)
                  *    init'ed(this.response)
                  *    init'ed(this.triggers)
                  * 
                  *  Postconditions:
                  *    java.lang.StringBuilder:toString(...)._tainted == this.location._tainted | this.conditions._tainted | this.group._tainted | this.name._tainted | One-of{this.conditions._tainted | this.group._tainted | this.name._tainted, this.newFormat._tainted}
                  *    init'ed(java.lang.StringBuilder:toString(...)._tainted)
                  *    return_value == &amp;java.lang.StringBuilder:toString(...)
                  */
   522          final String parent = super.toString();
   523  
   524          return parent.substring(0, parent.length() - 1)
   525                  + ",location=" + location + "]";
   526      }
   527  
   528  }








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