/**
   * Return the relation with name "id" if it can be applied to this component.
   *
   * <p>A relation D can be applied on a component source if D.Id == id D.source must be the name of
   * source or of an ancestor of source, and D.SourceKind == source.getKind.
   *
   * <p>Looks in the group, and then in the composite type, if source in an instance in all
   * composite types if source is an implem.
   *
   * @param source
   * @param id
   * @return
   */
  @Override
  public RelationDefinition getRelation(String id) {
    RelationDefinition dep = null;
    Component group = this;
    while (group != null) {
      dep = ((ComponentImpl) group).getLocalRelation(id);
      if (dep != null) {
        return dep;
      }
      group = group.getGroup();
    }

    // Looking for composite definitions.
    if (this instanceof Instance) {
      CompositeType comptype = ((Instance) this).getComposite().getCompType();
      dep = comptype.getCtxtRelation(this, id);
      if (dep != null) {
        return dep;
      }
    }
    if (this instanceof Implementation) {
      for (CompositeType comptype : ((Implementation) this).getInCompositeType()) {
        dep = comptype.getCtxtRelation(this, id);
        if (dep != null) {
          return dep;
        }
      }
    }
    return null;
  }
 /** Warning : do not resolve ! */
 @Override
 public Set<Link> getRawLinks() {
   Set<Link> allLinks = new HashSet<Link>();
   Component group = this;
   while (group != null) {
     allLinks.addAll(((ComponentImpl) group).getLocalLinks());
     group = group.getGroup();
   }
   return allLinks;
 }
  /** Whether this component is an ancestor of the specified component */
  @Override
  public boolean isAncestorOf(Component member) {

    assert member != null;

    Component ancestor = member.getGroup();
    while (ancestor != null && !ancestor.equals(this)) {
      ancestor = ancestor.getGroup();
    }

    return ancestor != null;
  }
 /**
  * Return the first link having this name, on any ancestor, but do not try to resolve is none are
  * found
  *
  * @param relName
  * @return
  */
 public Link getExistingLink(String relName) {
   Component group = this;
   while (group != null) {
     for (Link link : ((ComponentImpl) group).getLocalLinks()) {
       if (link.getName().equals(relName)) {
         return link;
       }
     }
     group = group.getGroup();
   }
   return null;
 }
 /**
  * Return all the link of that names, but do not resolve if nore are found
  *
  * @param relName
  * @return
  */
 public Set<Link> getExistingLinks(String relName) {
   Set<Link> dests = new HashSet<Link>();
   Component group = this;
   while (group != null) {
     for (Link link : ((ComponentImpl) group).getLocalLinks()) {
       if (link.getName().equals(relName)) {
         dests.add(link);
       }
     }
     group = group.getGroup();
   }
   return dests;
 }
  @Override
  public boolean setLink(Component destInst, String depName) {

    /*
     * Propagate down the group hierarchy.
     *
     * TODO Should we stop in case of a veto from one of the members?
     */
    for (Component member : apamComponent.getMembers()) {
      member.getApformComponent().setLink(destInst, depName);
    }

    return true;
  }
  @Override
  public Set<ResourceReference> getProvidedResources() {
    Set<ResourceReference> provided = new HashSet<ResourceReference>();

    /*
     * add all provided resources declared at all levels of abstraction
     */
    Component ancestor = this;
    while (ancestor != null) {
      provided.addAll(ancestor.getDeclaration().getProvidedResources());
      ancestor = ancestor.getGroup();
    }

    return Collections.unmodifiableSet(provided);
  }
  /**
   * Tries to find the definition of attribute "attr" associated with component "component". Returns
   * null if the attribute is not explicitly defined
   *
   * @param component
   * @param attr
   * @return
   */
  public PropertyDefinition getAttrDefinition(String attr) {

    // PropertyDefinition definition =
    // getDeclaration().getPropertyDefinition(attr);
    // if (definition != null) {
    // return definition;
    // }

    PropertyDefinition definition = null;
    Component group = this; // .getGroup();
    while (group != null) {
      definition = group.getDeclaration().getPropertyDefinition(attr);
      if (definition != null) {
        return definition;
      }
      group = group.getGroup();
    }
    return null;
  }
  @Override
  public Set<RelationDefinition> getRelations() {
    Set<RelationDefinition> relDefs = new HashSet<RelationDefinition>();
    Set<String> processed = new HashSet<String>();

    Component group = this;
    while (group != null) {

      for (RelationDefinition relDef : group.getLocalRelations()) {
        if (!processed.contains(relDef.getName())) {
          relDefs.add(relDef);
          processed.add(relDef.getName());
        }
      }

      group = group.getGroup();
    }

    // Looking for composite definitions.
    if (this instanceof Instance) {
      CompositeType comptype = ((Instance) this).getComposite().getCompType();

      for (RelationDefinition relDef : comptype.getCtxtRelations(this)) {
        if (!processed.contains(relDef.getName())) {
          relDefs.add(relDef);
          processed.add(relDef.getName());
        }
      }
    }
    if (this instanceof Implementation) {
      for (CompositeType comptype : ((Implementation) this).getInCompositeType()) {
        for (RelationDefinition relDef : comptype.getCtxtRelations(this)) {
          if (!processed.contains(relDef.getName())) {
            relDefs.add(relDef);
            processed.add(relDef.getName());
          }
        }
      }
    }

    return relDefs;
  }
  /**
   * Given a relation declared in this component, checks if the provided override relation matches
   * the relation declaration.
   *
   * <p>To be applied on a component C, the override must be such that : id matches the override id
   * source must be the name of C or of an ancestor of C. target must be the same type (resource of
   * component, and its name must match).
   */
  public boolean matchOverride(RelationDeclaration relation, RelationDeclaration override) {

    // Overrides are currently only valid for instance
    boolean match = (this instanceof Instance);
    if (!match) {
      return false;
    }

    // Check if override source matches this component or one of its
    // ancestors

    match = false;
    Component group = this;
    while (group != null && !match) {
      match = override.refines(group.getDeclaration().getReference(), relation);
      group = group.getGroup();
    }

    return match;
  }
  @Override
  public Link getLink(String relName) {
    Component group = this;
    while (group != null) {
      for (Link link : ((ComponentImpl) group).getLocalLinks()) {
        if (link.getName().equals(relName)) {
          return link;
        }
      }
      group = group.getGroup();
    }

    // None are present. Try to resolve
    RelationDefinition rel = getRelation(relName);
    if (rel == null) {
      logger.error("relation " + relName + " undefined for " + this);
      return null;
    }
    Component source = rel.getRelSource(this);
    CST.apamResolver.resolveLink(source, rel);
    return getExistingLink(relName);
  }
  /**
   * To be called once the Apam entity is fully initialized.
   *
   * <p>Computes all its attributes, including inheritance. Checks if initial properties are
   * consistent with the declarations.
   *
   * <p>NOTE this method is also called when the owner changes, to force recalculation of
   * substitutions that depend on the current owner.
   */
  private void initializeProperties(Map<String, String> initialProperties) {

    Component group = getGroup();

    /*
     * Currently the instance declaration may include invalid properties.
     *
     * For declared instances this should not happen as the declaration is validated at build-time. For dynamically
     * created instances (using the APAM API or the apform API directly) this should be validated by this method.
     *
     * However, there are many properties added by the iPOJO apform layer that need to be ignored, so we simply
     * silently ignore all invalid properties.
     *
     * TODO We should be able to distinguish properties specified by the user that must be validated, from properties
     * used internally by the iPOJO apform.
     */
    Set<String> invalidDeclaredProperties = new HashSet<String>();
    for (String property : getDeclaration().getProperties().keySet()) {

      boolean isDefined =
          getDeclaration().isDefined(property)
              || (group != null && group.getPropertyDefinition(property) != null);

      if (!isDefined) {
        invalidDeclaredProperties.add(property);
      }

      if (group != null && group.getProperty(property) != null) {
        invalidDeclaredProperties.add(property);
      }
    }

    getDeclaration().getProperties().keySet().removeAll(invalidDeclaredProperties);

    /*
     * Merge initial and declared properties.
     *
     */
    Map<String, String> fullInitialProperties =
        new HashMap<String, String>(getDeclaration().getProperties());
    if (initialProperties != null) {
      fullInitialProperties.putAll(initialProperties);
      fullInitialProperties.remove("instance.name");
    }

    /*
     * NOTE  In the case of change owner, the initial properties include inherited values from the group,
     * we ignore them to avoid false error messages, they will be added later by the normal inheritance
     * mechanism
     *
     * TODO Distinguish the case of change owner from a real initialization
     */
    if (initialProperties != null && group != null) {
      for (Map.Entry<String, String> initialProperty : initialProperties.entrySet()) {
        if (group.getProperty(initialProperty.getKey()) != null
            && group.getProperty(initialProperty.getKey()).equals(initialProperty.getValue())) {
          fullInitialProperties.remove(initialProperty.getKey());
        }
      }
    }

    // start cleaning the properties (normally empty)
    clear();

    /*
     *  First add the valid attributes.
     */
    for (Map.Entry<String, String> initialProperty : fullInitialProperties.entrySet()) {

      PropertyDefinition def = validDef(initialProperty.getKey(), true);
      if (def != null) {
        Object val =
            Attribute.checkAttrType(
                initialProperty.getKey(), initialProperty.getValue(), def.getType());
        if (val != null) {
          put(initialProperty.getKey(), val);
        }
      }
    }

    /*
     *  then add those coming from its group, avoiding overloads.
     */
    if (group != null) {
      for (String attr : group.getAllProperties().keySet()) {
        if (get(attr) == null) {
          put(attr, ((ComponentImpl) group).get(attr));
        }
      }
    }

    /*
     * Add the default values specified in the group for properties not explicitly specified
     */
    if (group != null) {
      for (PropertyDefinition definition : group.getDeclaration().getPropertyDefinitions()) {
        if (definition.hasDefaultValue()
            && get(definition.getName()) == null
            && definition.getInjected() != InjectedPropertyPolicy.INTERNAL) {
          Object val =
              Attribute.checkAttrType(
                  definition.getName(), definition.getDefaultValue(), definition.getType());
          if (val != null) {
            put(definition.getName(), val);
          }
        }
      }
    }

    /*
     * Set the attribute for the final attributes
     */
    put(CST.SHARED, Boolean.toString(isShared()));
    put(CST.SINGLETON, Boolean.toString(isSingleton()));
    put(CST.INSTANTIABLE, Boolean.toString(isInstantiable()));

    /*
     * Finally add the specific attributes.
     * Should be the only place where instanceof is used.
     */
    put(CST.NAME, apform.getDeclaration().getName());
    if (this instanceof Specification) {
      put(CST.SPECNAME, apform.getDeclaration().getName());
    } else if (this instanceof Implementation) {
      put(CST.IMPLNAME, apform.getDeclaration().getName());
      if (this instanceof CompositeType) {
        put(CST.APAM_COMPOSITETYPE, CST.V_TRUE);
      }
    } else if (this instanceof Instance) {
      put(CST.INSTNAME, apform.getDeclaration().getName());
      if (this instanceof Composite) {

        Composite composite = (Composite) this;
        put(CST.APAM_COMPOSITE, CST.V_TRUE);
        if (composite.getMainInst() != null) {
          put(CST.APAM_MAIN_INSTANCE, composite.getMainInst().getName());
        }
      }
    }

    /*
     *  and propagate, to the platform and to members, in case the spec has been created after the implem
     */
    for (Map.Entry<String, Object> entry : this.entrySet()) {
      for (Component member : getMembers()) {
        ((ComponentImpl) member).propagate(entry.getKey(), entry.getValue());
      }
    }
  }
  /**
   * Provided a component, compute its effective relations definition, adding group constraint and
   * flags. It is supposed to be correct !! No failure expected
   *
   * <p>Does not add those dependencies defined "above". For relations refined locally, merge the
   * local definition with the group definition.
   *
   * <p>For instances only, add the relation definition overridden by the composite.
   *
   * <p>Remove those links that are not valid with the computed relation definition (for
   * changeOwner)
   */
  private void initializeRelations() {

    /*
     * First we need to compute the list of relations that must be locally
     * defined in this component. We consider locally defined relation
     * declarations and overridden inherited relations.
     */
    Set<RelationDeclaration> overrides = null;
    if (this instanceof Instance) {
      overrides =
          ((Instance) this)
              .getComposite()
              .getCompType()
              .getCompoDeclaration()
              .getOverridenDependencies();
    } else overrides = Collections.emptySet();

    Set<RelationDeclaration> localRelations = new HashSet<RelationDeclaration>();
    Set<String> processed = new HashSet<String>();

    Component group = this;
    while (group != null) {

      for (RelationDeclaration relationDeclaration : group.getDeclaration().getRelations()) {

        /*
         * Ignore relations already processed at a lower level
         */
        if (processed.contains(relationDeclaration.getIdentifier())) {
          continue;
        }

        /*
         * Check overridden relations
         */
        boolean matchOverride = false;
        for (RelationDeclaration override : overrides) {
          //					for (RelationDeclaration override : overrides != null ? overrides
          //							: Collections.<RelationDeclaration> emptySet()) {
          if (matchOverride(relationDeclaration, override)) {
            relationDeclaration = relationDeclaration.overriddenBy(override);
            matchOverride = true;
          }
        }

        /*
         * Process locally declared and inherited overridden relations
         */
        if (group == this || matchOverride) {
          localRelations.add(relationDeclaration);
          processed.add(relationDeclaration.getIdentifier());
        }
      }
      group = group.getGroup();
    }

    /*
     * Define all the local relations definition of this component
     */
    for (RelationDeclaration relationDeclaration : localRelations) {
      /*
       * Local declarations may be partial definitions, we need to compute
       * the complete declaration by refining the ancestor definition.
       */
      RelationDefinition base = this.getRelation(relationDeclaration.getIdentifier());
      relationDeclaration =
          (base == null)
              ? relationDeclaration
              : ((RelationDefinitionImpl) base).refinedBy(relationDeclaration);

      relDef.put(
          relationDeclaration.getIdentifier(), new RelationDefinitionImpl(relationDeclaration));
    }

    /*
     * If the component has links, remove those that are invalid (for changeOwner)
     * Also remove the link if it is a promotion, since we are no longer in the same composite
     */
    for (Link localLink : getLocalLinks()) {
      if (localLink.isPromotion() || !localLink.isValid()) localLink.remove();
    }

    for (Link incoming : getInvLinks()) {
      if (incoming.isPromotion() || !incoming.isValid()) incoming.remove();
    }
  }
  @Override
  public boolean createLink(
      Component to, RelToResolve dep, boolean hasConstraints, boolean promotion) {
    // Not a relation : a find
    if (!dep.isRelation()) {
      return true;
    }

    if (CST.isFinalRelation(dep.getName())) {
      logger.error("CreateLink: cannot create predefined relation " + dep.getName());
      return false;
    }

    if ((to == null) || (dep == null)) {
      logger.error("CreateLink: Source or target are null ");
      return false;
    }

    if (!promotion && !canSee(to)) {
      logger.error("CreateLink: Source  " + this + " does not see its target " + to);
      return false;
    }

    if (this.getKind() != dep.getSourceKind()) {
      logger.error(
          "CreateLink: Source kind "
              + getKind()
              + " is not compatible with relation sourceType "
              + dep.getSourceKind());
      return false;
    }

    if (to.getKind() != dep.getTargetKind()) {
      logger.error(
          "CreateLink: Target kind "
              + to.getKind()
              + " is not compatible with relation targetType "
              + dep.getTargetKind());
      return false;
    }

    if (hasConstraints && !dep.matchRelationConstraints(to)) {
      logger.error("CreateLink: Target does not satisfies the constraints");
      return false;
    }

    String depName = dep.getName();

    for (Link link : links) { // check if it already exists
      if ((link.getDestination() == to) && link.getName().equals(depName)) {
        // It exists, do nothing.
        return true;
      }
    }

    // creation
    if (!getApformComponent().checkLink(to, depName)) {
      logger.error(
          "CreateLink: INTERNAL ERROR: link from "
              + this
              + " to "
              + to
              + " could not be created in the real instance.");
      return false;
    }

    Link link = new LinkImpl(this, to, dep, hasConstraints, promotion);
    links.add(link);
    ((ComponentImpl) to).invlinks.add(link);
    getApformComponent().setLink(to, depName);

    // Notify Dynamic managers that a new link has been created
    for (DynamicManager manager : ApamManagers.getDynamicManagers()) {
      manager.addedLink(link);
    }

    return true;
  }
 /** TODO Assumes that all components are in the same name space, including instance !! */
 @Override
 public int compareTo(Component that) {
   return this.getName().compareTo(that.getName());
 }
 /** Whether this component is a descendant of the specified component */
 @Override
 public boolean isDescendantOf(Component group) {
   assert group != null;
   return group.isAncestorOf(this);
 }