@Override
 public AttrType getAttrType(String attr) {
   PropertyDefinition attrDef = getAttrDefinition(attr);
   if (attrDef == null) {
     if (Attribute.isFinalAttribute(attr)) {
       return Attribute.splitType("string");
     }
     return null;
   }
   return Attribute.splitType(attrDef.getType());
 }
  /**
   * An attribute is valid if declared in an ancestor, and not set in an ancestor. Check if the
   * value is conforming to its type (string, int, boolean). Internal attribute (associated with a
   * field) cannot be set. Must be called on the level above.
   *
   * <p>Checks if attr is correctly defined for component ent it must be explicitly defined in its
   * upper groups, for the top group, it must be already existing.
   *
   * <p>boolean Forced = true can be set only by Apform. Needed when an internal attribute is set in
   * the program, Apform propagates the value to the attribute.
   *
   * @param attr
   * @param value
   * @return
   */
  public PropertyDefinition validDef(String attr, boolean forced) {
    if (Attribute.isFinalAttribute(attr)) {
      logger.error("In " + this + ", cannot redefine final attribute \"" + attr + "\"");
      return null;
    }

    if (Attribute.isReservedAttributePrefix(attr)) {
      logger.error("In " + this + ", attribute\"" + attr + "\" is reserved");
      return null;
    }

    PropertyDefinition definition = this.getAttrDefinition(attr);
    if (definition == null) {
      logger.error("In " + this + ", attribute \"" + attr + "\" is undefined.");
      return null;
    }

    /*
     * Internal field attributes cannot be set
     */
    if (definition.getInjected() == InjectedPropertyPolicy.INTERNAL && !forced) {
      logger.error(
          "In "
              + this
              + ", attribute \""
              + attr
              + "\" is an internal field attribute and cannot be set.");
      return null;
    }

    /*
     * if the same attribute exists above, it is a redefinition.
     */
    ComponentImpl group = (ComponentImpl) this.getGroup();
    if (group != null && group.get(attr) != null) {

      Object groupValue = group.get(attr);
      String defaultValue = definition.getDefaultValue();

      boolean isDefault =
          defaultValue != null
              && groupValue.equals(
                  Attribute.checkAttrType(attr, defaultValue, definition.getType()));

      // If the attribute above is the default value, it is allowed to change it
      if (!isDefault && !Attribute.isBuiltAttribute(attr)) {
        logger.error("In " + this + ", cannot redefine attribute \"" + attr + "\"");
        return null;
      }
    }

    return definition;
  }
  /**
   * Warning: to be used only by Apform for setting internal attributes. Only Inhibits the message
   * "Attribute " + attr + " is an internal field attribute and cannot be set.");
   *
   * @param attr
   * @param value
   * @param forced
   * @return
   */
  public boolean setProperty(String attr, Object value, boolean forced) {
    /*
     * Validate that the property is defined and the value is valid Forced
     * means that we can set field attribute
     */
    PropertyDefinition def = validDef(attr, forced);
    if (def == null) {
      return false;
    }
    // At initialization, all valid attributes are ok for specs
    Object val = Attribute.checkAttrType(attr, value, def.getType());
    if (val == null) {
      return false;
    }

    /*
     * Force recalculation of dependencies that may have been invalidated by
     * the property change. This must be done before notification and
     * propagation, otherwise we risk to remove links updated by managers.
     *
     * We remove only those links that are now invalid problematic for those
     * that are not lazy links : assychronous messages, .. and avoiding
     * unnecessary work TODO Check if this must be done for all links or
     * only dynamic links
     */
    Object oldValue = get(attr);
    put(attr, val);

    for (Link incoming : getInvLinks()) {
      // If still valid, do nothing
      if (incoming.hasConstraints() && !incoming.isValid()) {
        incoming.remove();
      }
    }

    // If outgoing constraints have substitution, the link may be now invalid
    for (Link outgoing : getLocalLinks()) {
      //			if (!!!((RelationDefinitionImpl) outgoing.getRelDefinition()).isStaticImplemConstraints()
      //					&& !outgoing.isValid()) {
      outgoing.getRelToResolve().reComputeSubstFilters();
      if (!outgoing.isValid()) {
        outgoing.remove();
      }
    }

    // WARNING : undo the change because propagate needs the initial state
    if (oldValue == null) {
      remove(attr);
    } else {
      put(attr, oldValue);
    }

    // does the change, notifies managers, changes the platform and
    // propagate to members
    this.propagate(attr, val);

    return true;
  }
 @Override
 public Map<String, String> getAllPropertiesString() {
   Map<String, String> ret = new HashMap<String, String>();
   for (Entry<String, Object> e : this.entrySet()) {
     if (!Attribute.isFinalAttribute(e.getKey())) {
       ret.put(e.getKey(), Util.toStringAttrValue(e.getValue()));
     }
   }
   return ret;
 }
  /**
   * Warning: to be used only by Apform for removing internal attributes. Only Inhibits the message
   * "Attribute " + attr + " is an program field attribute and cannot be removed.");
   */
  public boolean removeProperty(String attr, boolean forced) {

    String oldValue = getProperty(attr);

    if (oldValue == null) {
      logger.error("ERROR: \"" + attr + "\" not instanciated");
      return false;
    }

    if (Attribute.isFinalAttribute(attr)) {
      logger.error("ERROR: \"" + attr + "\" is a final attribute");
      return false;
    }

    if (Attribute.isReservedAttributePrefix(attr)) {
      logger.error("ERROR: \"" + attr + "\" is a reserved attribute");
      return false;
    }

    PropertyDefinition propDef = getAttrDefinition(attr);
    if (propDef != null && propDef.getField() != null && !forced) {
      logger.error(
          "In " + this + " attribute " + attr + " is a program field and cannot be removed.");
      return false;
    }

    if (getGroup() != null && getGroup().getProperty(attr) != null) {
      logger.error("In " + this + " attribute " + attr + " inherited and cannot be removed.");
      return false;
    }

    // it is ok, remove it and propagate to members, recursively
    propagateRemove(attr);

    // TODO. Should we notify at all levels ?
    ApamManagers.notifyAttributeRemoved(this, attr, oldValue);

    return true;
  }
  /**
   * 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());
      }
    }
  }