/**
   * Retrieves an interface from its name. NF Interfaces (both client/server) must have a name
   * ending in "-controller".
   *
   * @see org.objectweb.fractal.api.Component#getFcInterface(String)
   */
  public Object getFcInterface(String interfaceName) throws NoSuchInterfaceException {
    if (interfaceName.endsWith("-controller")
        && !(Constants.ATTRIBUTE_CONTROLLER.equals(interfaceName))) {
      if (this.nfItfs == null) {
        // Check: is it needed to do this?... the addControllers method, or equivalent, should have
        // been called at construction,
        //        and also maybe we're not using a controller config file
        addControllers(
            this.componentParameters.getControllerDescription().getControllersSignatures());
      }
      if (this.nfItfs.containsKey(interfaceName)) {
        return this.nfItfs.get(interfaceName);
      }
      // TODO: Check how are the collective NF interfaces handled here ... should they also finish
      // by "-controller" ?
      throw new NoSuchInterfaceException(interfaceName);
    }

    if (this.fItfs.containsKey(interfaceName)) {
      return this.fItfs.get(interfaceName);
    } else {
      if (interfaceName.equals(Constants.COMPONENT)) {
        return this;
      }

      // maybe the member of a collection itf?
      InterfaceType itfType = ((ComponentType) this.getFcType()).getFcInterfaceType(interfaceName);
      if ((itfType != null) && itfType.isFcCollectionItf()) {
        try {
          // generate the corresponding interface locally
          Interface interface_reference =
              RepresentativeInterfaceClassGenerator.instance()
                  .generateFunctionalInterface(interfaceName, this, (PAGCMInterfaceType) itfType);

          ((StubObject) interface_reference).setProxy(this.proxy);
          // keep it in the list of functional interfaces
          this.fItfs.put(interfaceName, interface_reference);
          return interface_reference;
        } catch (Throwable e) {
          logger.info("Could not generate " + interfaceName + " collection interface", e);
        }
      }
    }

    throw new NoSuchInterfaceException(interfaceName);
  }
  /**
   * Instantiates an object controller and generates its representative interface using only the
   * Controller class (which must implement {@link AbstractPAController}.
   *
   * <p>The {@link PAGCMInterfaceType} is obtained from the object after instantiating it.
   *
   * @param controllerClass
   * @return interface generated for the controller
   */
  private PAInterface createControllerRepresentative(Class<?> controllerClass) throws Exception {

    // Instantiate the controller object, setting THIS component as its owner.
    Constructor<?> controllerClassConstructor =
        controllerClass.getConstructor(new Class[] {Component.class});
    AbstractPAController controller =
        (AbstractPAController) controllerClassConstructor.newInstance(new Object[] {this});
    // Obtains the interface type after having instantiated the object
    PAGCMInterfaceType controllerItfType = (PAGCMInterfaceType) controller.getFcItfType();
    String controllerName = controller.getFcItfName();
    // Generates the representative PAInterface
    PAInterface itfRef =
        RepresentativeInterfaceClassGenerator.instance()
            .generateInterface(
                controllerName, this, controllerItfType, controllerItfType.isInternal(), false);
    // itfRef.setFcItfImpl(controller);

    // TODO: This was not done before. Is it needed now? Following the scheme of "addControllers" I
    // would say yes
    ((StubObject) itfRef).setProxy(this.proxy);
    return itfRef;
  }
  /** @param componentType */
  private void addFunctionalInterfaces() {
    InterfaceType[] itfTypes = this.componentParameters.getComponentType().getFcInterfaceTypes();
    this.fItfs = new HashMap<String, Interface>(itfTypes.length + (itfTypes.length / 2));

    try {
      for (int j = 0; j < itfTypes.length; j++) {
        if (!itfTypes[j].isFcCollectionItf()) {
          // itfs members of collection itfs are dynamically generated
          Interface interface_reference =
              RepresentativeInterfaceClassGenerator.instance()
                  .generateFunctionalInterface(
                      itfTypes[j].getFcItfName(), this, (PAGCMInterfaceType) itfTypes[j]);

          // all calls are to be reified
          if (interface_reference != null) {
            this.fItfs.put(interface_reference.getFcItfName(), interface_reference);
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException("cannot create interface references : " + e.getMessage());
    }
  }
  private void addControllers(Map<String, String> controllersConfiguration) {
    // create the interface references tables
    // the size is the addition of :
    // - 1 for the current ItfRef (that is at the same time a binding controller, lifecycle
    // controller,
    // content controller and name controller
    // - the number of client functional interfaces
    // - the number of server functional interfaces
    // ArrayList interface_references_list = new ArrayList(1
    // +componentType.getFcInterfaceTypes().length+controllersConfiguration.size());
    this.nfItfs = new HashMap<String, Interface>(1 + controllersConfiguration.size());

    // add controllers
    // Enumeration controllersInterfaces = controllersConfiguration.propertyNames();
    Iterator<String> iteratorOnControllers = controllersConfiguration.keySet().iterator();
    Class<?> controllerClass = null;
    AbstractPAController currentController;
    PAInterface currentInterface = null;
    Class<?> controllerItf;
    Vector<InterfaceType> nfType = new Vector<InterfaceType>();
    while (iteratorOnControllers.hasNext()) {
      String controllerItfName = iteratorOnControllers.next();
      try {
        controllerItf = Class.forName(controllerItfName);
        controllerClass = Class.forName(controllersConfiguration.get(controllerItf.getName()));
        Constructor<?> controllerClassConstructor =
            controllerClass.getConstructor(new Class<?>[] {Component.class});
        currentController =
            (AbstractPAController) controllerClassConstructor.newInstance(new Object[] {this});
        currentInterface =
            RepresentativeInterfaceClassGenerator.instance()
                .generateControllerInterface(
                    currentController.getFcItfName(),
                    this,
                    (PAGCMInterfaceType) currentController.getFcItfType());
        ((StubObject) currentInterface).setProxy(this.proxy);

      } catch (Exception e) {
        logger.error(
            "could not create controller "
                + controllersConfiguration.get(controllerItfName)
                + " : "
                + e.getMessage());
        continue;
      }

      if (BindingController.class.isAssignableFrom(controllerClass)) {
        if ((this.componentParameters.getHierarchicalType().equals(Constants.PRIMITIVE)
            && (Utils.getClientItfTypes(this.componentParameters.getComponentType()).length == 0)
            && !hasNfInternalServerInterfaces())) {
          if (logger.isDebugEnabled()) {
            logger.debug(
                "user component class of this component does not have any client interface. It will have no BindingController");
          }
          continue;
        }
      }
      if (ContentController.class.isAssignableFrom(controllerClass)) {
        if (Constants.PRIMITIVE.equals(this.componentParameters.getHierarchicalType())) {
          // no content controller here
          continue;
        }
      }
      if (currentInterface != null) {
        this.nfItfs.put(currentController.getFcItfName(), currentInterface);
        nfType.add((InterfaceType) currentInterface.getFcItfType());
      }
    }

    try { // Setting the real NF type, as some controllers may not be generated
      Component boot = Utils.getBootstrapComponent();
      PAGCMTypeFactory type_factory = Utils.getPAGCMTypeFactory(boot);
      InterfaceType[] fItfTypes =
          ((PAComponentType) this.componentParameters.getComponentType()).getFcInterfaceTypes();
      InterfaceType[] nfItfTypes = nfType.toArray(new InterfaceType[] {});
      this.componentParameters.setComponentType(type_factory.createFcType(fItfTypes, nfItfTypes));
    } catch (Exception e) {
      e.printStackTrace();
      logger.warn("NF type could not be set");
    }
  }
  /**
   * Create and add the NF Interfaces using both a component configuration file, and an NF Type.
   *
   * <ol>
   *   <li>Creates NF Interfaces from the declared NF Type. If there is non NF Type, this step is
   *       ignored.
   *   <li>Creates Object Controllers from the Controllers Configuration File. If some of them
   *       duplicates interfaces declared in the NF Type, they are ignored. In other words, the
   *       declared NF Type has priority over the Controllers Configuration File.
   *   <li>Checks that the mandatory interfaces are declared. In particular, if the membrane
   *       controller has been previously declared, it is created here.
   *   <li>Updates the NF Type of the Component.
   * </ol>
   *
   * NOTE: When an NF interface is described in the ADL file, the Factory adds the
   * 'membrane-controller' default implementation automatically, so that it is not necessary to
   * specify the 'membrane-controller' explicitly.
   */
  private void addControllerInterfaces() {

    // Vector to collect the real NF type
    Vector<InterfaceType> nfType = new Vector<InterfaceType>();

    boolean isPrimitive =
        Constants.PRIMITIVE.equals(this.componentParameters.getHierarchicalType());

    // ------------------------------------------------------------
    // 1. Create interfaces from the declared NF type
    // Read the NF Type
    PAComponentType componentType = (PAComponentType) this.componentParameters.getComponentType();
    InterfaceType[] nfItfTypes = componentType.getNfFcInterfaceTypes();
    PAGCMInterfaceType[] pagcmNfItfTypes = new PAGCMInterfaceType[nfItfTypes.length];
    System.arraycopy(nfItfTypes, 0, pagcmNfItfTypes, 0, nfItfTypes.length);
    // Class<?> controllerItf = null;

    for (PAGCMInterfaceType pagcmNfItfType : pagcmNfItfTypes) {

      String itfName = pagcmNfItfType.getFcItfName();
      PAInterface itfRef = null;

      try {
        // some controllers interfaces are ignored
        if (specialCasesForNfType(itfName, pagcmNfItfType, isPrimitive)) continue;

        // TODO: check the case MULTICAST && CLIENT, treated in
        // PAComponentImpl.addControllerInterfaces, but not here

        // Generate the representative interface
        itfRef =
            RepresentativeInterfaceClassGenerator.instance()
                .generateInterface(
                    pagcmNfItfType.getFcItfName(),
                    this,
                    pagcmNfItfType,
                    pagcmNfItfType.isInternal(),
                    false);

        // update the hashmap and the vector of NF types
        this.nfItfs.put(itfName, itfRef);
        nfType.add((InterfaceType) itfRef.getFcItfType());

      } catch (Exception e) {
        throw new ProActiveRuntimeException(
            "Could not create NF interface reference'"
                + itfName
                + "' while instantiating component'"
                + this.componentParameters.getName()
                + "'. "
                + e.getMessage(),
            e);
      }
    }

    // ------------------------------------------------------------
    // 2. Create interfaces from the Controller Configuration file

    // read the Controller Configuration File
    Map<String, String> controllerEntries = null;
    if (this.componentParameters.getControllerDescription().configFileIsSpecified()) {
      // Parse controller config file
      String controllersConfigFileLocation =
          this.componentParameters.getControllerDescription().getControllersConfigFileLocation();
      loggerADL.debug("Parsing Controller Configuration File: " + controllersConfigFileLocation);
      ComponentConfigurationHandler componentConfiguration =
          PAComponentImpl.loadControllerConfiguration(controllersConfigFileLocation);
      controllerEntries = componentConfiguration.getControllers();

      // Create controller objects from the Controller Configuration File
      for (Map.Entry<String, String> controllerEntry : controllerEntries.entrySet()) {

        String controllerName = null;
        String controllerItfName = controllerEntry.getKey();
        String controllerClassName = controllerEntry.getValue();
        Class<?> controllerItf = null;
        Class<?> controllerClass = null;
        AbstractPAController controller = null;
        PAInterface itfRef = null;
        PAGCMInterfaceType controllerItfType = null;

        try {
          // fetch the classes
          controllerItf = Class.forName(controllerItfName);
          controllerClass = Class.forName(controllerClassName);
          // Instantiates the controller object, using 'this' component as owner.
          Constructor<?> controllerClassConstructor =
              controllerClass.getConstructor(new Class[] {Component.class});
          controller =
              (AbstractPAController) controllerClassConstructor.newInstance(new Object[] {this});

          // Obtains the controller interfaceType as declared by the object (in the method
          // setControllerItfType)
          controllerItfType = (PAGCMInterfaceType) controller.getFcItfType();
          // now we can know the name of the controller, and discriminate special cases
          controllerName = controllerItfType.getFcItfName();

          // Some controllers are not created
          if (specialCasesForController(
              controllerName, controllerItfType, isPrimitive, controllersConfigFileLocation)) {
            continue;
          }

          if (!controllerItf.isAssignableFrom(controllerClass)) {
            logger.error(
                "Could not create controller. Class '"
                    + controllerClassName
                    + " does not implement interface '"
                    + controllerItfName
                    + ". Check controller configuration file.");
            continue;
          }

          // Generate the representative interface
          itfRef =
              RepresentativeInterfaceClassGenerator.instance()
                  .generateControllerInterface(controllerName, this, controllerItfType);
          ((StubObject) itfRef).setProxy(this.proxy);

        } catch (Exception e) {
          throw new ProActiveRuntimeException(
              "Could not create representative interface for controller '"
                  + controllerClassName
                  + "' while instantiating component'"
                  + this.componentParameters.getName()
                  + "'. Check your configuration file "
                  + this.componentParameters
                      .getControllerDescription()
                      .getControllersConfigFileLocation()
                  + " : "
                  + e.getMessage(),
              e);
        }

        // add the controller to the controllers interfaces map, and add the controller type to the
        // NF type
        nfItfs.put(controllerName, itfRef);
        nfType.add((InterfaceType) itfRef.getFcItfType());
      }
    }

    // ------------------------------------------------------------
    // 3. Check that the mandatory controllers have been created
    checkMandatoryControllers(nfType);

    // ------------------------------------------------------------
    // 4. Set the real NF type, after having created all the NF interfaces
    try {
      Component boot = Utils.getBootstrapComponent();
      PAGCMTypeFactory tf = Utils.getPAGCMTypeFactory(boot);
      InterfaceType[] f = this.componentParameters.getComponentType().getFcInterfaceTypes();
      InterfaceType[] nf = nfType.toArray(new InterfaceType[] {});
      // Re-Set the real ComponentType
      this.componentParameters.setComponentType(tf.createFcType(f, nf));
    } catch (Exception e) {
      logger.error("NF type could not be set");
      e.printStackTrace();
    }
  }