/**
  * Applies a config entry to a component. If the entry is not valid, the method prints an
  * exception and returns false.
  *
  * @param <T> Type of the config option.
  * @param component A component object.
  * @param entry The configuration entry to set.
  * @return True if the config entry could be applied succesfully, otherwise false.
  */
 public <T> boolean applyConfigEntry(AbstractComponent component, ConfigEntry<T> entry) {
   try {
     component.applyConfigEntry(entry);
     pool.addConfigEntry(component, entry, true);
     return true;
   } catch (InvalidConfigOptionValueException e) {
     pool.addConfigEntry(component, entry, false);
     e.printStackTrace();
     return false;
   }
 }
  /**
   * Convenience method for testing purposes. If you know that the type of the value is correct, it
   * is preferable to create a ConfigEntry object and apply it to the component (no type checking
   * necessary).
   *
   * @param <T> Type of the config option (Integer, String etc.).
   * @param component A component.
   * @param optionName The name of the config option.
   * @param value The value of the config option.
   */
  @SuppressWarnings("unchecked")
  public <T> void applyConfigEntry(AbstractComponent component, String optionName, T value) {
    logger.trace(component);
    logger.trace(optionName);
    logger.trace(value);
    logger.trace(value.getClass());
    // first we look whether the component is registered
    if (components.contains(component.getClass())) {

      // look for a config option with the specified name
      ConfigOption<?> option =
          (ConfigOption<?>) componentOptionsByName.get(component.getClass()).get(optionName);
      if (option != null) {
        // check whether the given object has the correct type
        if (!option.checkType(value)) {
          System.out.println(
              "Warning: value "
                  + value
                  + " is not valid for option "
                  + optionName
                  + " in component "
                  + component
                  + ". It does not have the correct type.");
          return;
        }

        // we have checked the type, hence it should now be safe to
        // typecast and
        // create a ConfigEntry object
        ConfigEntry<T> entry = null;
        try {
          entry = new ConfigEntry<T>((ConfigOption<T>) option, value);
          component.applyConfigEntry(entry);
          pool.addConfigEntry(component, entry, true);
        } catch (InvalidConfigOptionValueException e) {
          pool.addConfigEntry(component, entry, false);
          System.out.println(
              "Warning: value "
                  + value
                  + " is not valid for option "
                  + optionName
                  + " in component "
                  + component);
        }
      } else {
        logger.warn("Warning: undefined option " + optionName + " in component " + component);
      }
    } else {
      logger.warn("Warning: unregistered component " + component);
    }
  }
 /**
  * Gets the value of a config option of the specified component. This is done by first checking,
  * which value the given option was set to using {@link #applyConfigEntry(AbstractComponent,
  * ConfigEntry)}. If the value has not been changed, the default value for this option is
  * returned. Note, that this method will not work properly if the component options are changed
  * internally surpassing the component manager (which is discouraged).
  *
  * @param <T> The type of the config option, e.g. String, boolean, integer.
  * @param component The component, which has the specified option.
  * @param option The option for which we want to know its value.
  * @return The value of the specified option in the specified component.
  */
 public <T> T getConfigOptionValue(AbstractComponent component, ConfigOption<T> option) {
   T object = pool.getLastValidConfigValue(component, option);
   if (object == null) {
     return option.getDefaultValue();
   } else {
     return object;
   }
 }
  /**
   * Factory method for creating a reasoner component from a set of knowledge sources.
   *
   * @see #reasoner(Class, AbstractKnowledgeSource)
   * @param <T> The type of this method is a subclass of reasoner component.
   * @param reasoner A class object, where the class is subclass of ReasonerComponent.
   * @param sources A set of knowledge sources.
   * @return A reasoner component.
   */
  public <T extends AbstractReasonerComponent> T reasoner(
      Class<T> reasoner, Set<AbstractKnowledgeSource> sources) {
    if (!reasonerComponents.contains(reasoner)) {
      System.err.println(
          "Warning: reasoner component " + reasoner + " is not a registered reasoner component.");
    }

    T rc = invokeConstructor(reasoner, new Class[] {Set.class}, new Object[] {sources});
    pool.registerComponent(rc);
    return rc;
  }
  /**
   * Factory method for creating a knowledge source.
   *
   * @param <T> The type of this method is a subclass of knowledge source.
   * @param source A registered knowledge source component.
   * @return An instance of the given knowledge source class.
   */
  public <T extends AbstractKnowledgeSource> T knowledgeSource(Class<T> source) {
    if (!knowledgeSources.contains(source)) {
      logger.warn(
          "Warning: knowledge source "
              + source
              + " is not a registered knowledge source component.");
    }

    T ks = invokeConstructor(source, new Class[] {}, new Object[] {});
    pool.registerComponent(ks);
    return ks;
  }
  /**
   * Factory method for creating a learning problem component.
   *
   * @param <T> The type of this method is a subclass of learning problem.
   * @param lpClass A class object, where the class is a subclass of learning problem.
   * @param reasoner A reasoning service object.
   * @return A learning problem component.
   */
  public <T extends AbstractLearningProblem> T learningProblem(
      Class<T> lpClass, AbstractReasonerComponent reasoner) {
    if (!learningProblems.contains(lpClass)) {
      System.err.println(
          "Warning: learning problem "
              + lpClass
              + " is not a registered learning problem component.");
    }

    T lp =
        invokeConstructor(
            lpClass, new Class[] {AbstractReasonerComponent.class}, new Object[] {reasoner});
    pool.registerComponent(lp);
    return lp;
  }
  /**
   * Factory method for creating a learning algorithm, which automagically calls the right
   * constructor for the given problem.
   *
   * @param <T> The type of this method is a subclass of learning algorithm.
   * @param laClass A class object, where the class is subclass of learning algorithm.
   * @param lp A learning problem, which the algorithm should try to solve.
   * @param rs A reasoning service for querying the background knowledge of this learning problem.
   * @return A learning algorithm component.
   * @throws LearningProblemUnsupportedException Thrown when the learning problem and the learning
   *     algorithm are not compatible.
   */
  public <T extends AbstractCELA> T learningAlgorithm(
      Class<T> laClass, AbstractLearningProblem lp, AbstractReasonerComponent rs)
      throws LearningProblemUnsupportedException {
    if (!learningAlgorithms.contains(laClass)) {
      System.err.println(
          "Warning: learning algorithm "
              + laClass
              + " is not a registered learning algorithm component.");
    }

    // find the right constructor: use the one that is registered and
    // has the class of the learning problem as a subclass
    Class<? extends AbstractLearningProblem> constructorArgument = null;
    for (Class<? extends AbstractLearningProblem> problemClass :
        algorithmProblemsMapping.get(laClass)) {
      if (problemClass.isAssignableFrom(lp.getClass())) {
        constructorArgument = problemClass;
      }
    }

    if (constructorArgument == null) {
      throw new LearningProblemUnsupportedException(
          lp.getClass(), laClass, algorithmProblemsMapping.get(laClass));
      //			System.err.println("Warning: No suitable constructor registered for algorithm "
      //					+ laClass.toStringID() + " and problem " + lp.getClass().toStringID()
      //					+ ". Registered constructors for " + laClass.toStringID() + ": "
      //					+ algorithmProblemsMapping.get(laClass) + ".");
      //			return null;
    }

    T la =
        invokeConstructor(
            laClass,
            new Class[] {constructorArgument, AbstractReasonerComponent.class},
            new Object[] {lp, rs});
    pool.registerComponent(la);
    return la;
  }
 /**
  * Retuns a list of all instanciated and registered Components
  *
  * @return Currently active components.
  */
 public List<AbstractComponent> getLiveComponents() {
   return pool.getComponents();
 }
 /**
  * Frees all references to components created by <code>ComponentManager</code>.
  *
  * @see #freeComponent(AbstractComponent)
  */
 public synchronized void freeAllComponents() {
   pool.clearComponents();
 }
 /**
  * The <code>ComponentManager</code> factory methods produce component instances, which can be
  * freed using this method. Calling the factory methods without freeing components when they are
  * not used anymore can (in theory) cause memory problems.
  *
  * @param component The component to free.
  */
 public void freeComponent(AbstractComponent component) {
   pool.unregisterComponent(component);
 }