/**
   * Update the program with the name given as argument using the data provided in the stream. This
   * method will first calculate the difference between the previous version of the program and the
   * new version of the program. It then updates the program in the database and notifies the engine
   * of all removed and added rules.
   *
   * @throws IOException in case the stream cannot be read
   * @throws SailException in case the program already exists
   * @throws ParseException in case the program cannot be parsed
   */
  public void updateProgram(String name, InputStream data)
      throws IOException, SailException, ParseException {
    KWRLProgramParserBase parser = new KWRLProgramParser(getValueFactory(), data);
    Program p = parser.parseProgram();
    p.setName(name);

    updateProgram(p);
  }
  /**
   * Add a program to the reasoner. The program is persisted to the database and the reasoning
   * engine is notified of the added rules and immediately calculates the inferences. Inferencing in
   * this case is synchronous, so the method only returns when the first round of reasoning is
   * completed for all added rules.
   *
   * <p>If a program with this name already exists, a SailException is thrown. To update existing
   * programs, please use updateProgram().
   *
   * @param program the program data in KWRL syntax
   * @throws SailException in case the program already exists
   */
  public void addProgram(Program program) throws SailException {
    // store program in the database
    try {
      KiWiReasoningConnection connection = persistence.getConnection();
      try {
        // should not throw an exception and the program should have a database ID afterwards
        connection.storeProgram(program);
        connection.commit();
      } finally {
        connection.close();
      }
    } catch (SQLException ex) {
      throw new SailException("cannot store program in database", ex);
    }

    engine.loadPrograms();

    // now add all added rules to the reasoner
    for (Rule rule : program.getRules()) {
      engine.notifyAddRule(rule);
    }
  }
  /**
   * Update the program given as argument. This method will first calculate the difference between
   * the previous version of the program and the new version of the program. It then updates the
   * program in the database and notifies the engine of all removed and added rules.
   *
   * @param program the updated version of the program
   * @throws SailException in case a database error occurs
   */
  public void updateProgram(Program program) throws SailException {
    Set<Rule> added = new HashSet<Rule>();
    Set<Rule> removed = new HashSet<Rule>();
    try {
      KiWiReasoningConnection connection = persistence.getConnection();
      try {
        // load old version of program and calculate difference
        Program old = connection.loadProgram(program.getName());
        if (old != null) {
          for (Rule r : old.getRules()) {
            if (!program.getRules().contains(r)) {
              removed.add(r);
            }
          }
          for (Rule r : program.getRules()) {
            if (!old.getRules().contains(r)) {
              added.add(r);
            }
          }
        }

        // store program in the database
        connection.updateProgram(program);
        connection.commit();
      } finally {
        connection.close();
      }
    } catch (SQLException ex) {
      throw new SailException("cannot store program in database", ex);
    }

    engine.loadPrograms();

    // if rules have been removed, clean up
    if (removed.size() > 0) {
      engine.notifyRemoveRules();
    }

    // now add all added rules to the reasoner
    for (Rule rule : added) {
      engine.notifyAddRule(rule);
    }
  }