Package fr.imag.adele.apam.impl

Source Code of fr.imag.adele.apam.impl.ComponentImpl

/**
* Copyright 2011-2012 Universite Joseph Fourier, LIG, ADELE team
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*/
package fr.imag.adele.apam.impl;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.imag.adele.apam.ApamManagers;
import fr.imag.adele.apam.AttrType;
import fr.imag.adele.apam.CST;
import fr.imag.adele.apam.Component;
import fr.imag.adele.apam.Composite;
import fr.imag.adele.apam.CompositeType;
import fr.imag.adele.apam.DynamicManager;
import fr.imag.adele.apam.Implementation;
import fr.imag.adele.apam.Instance;
import fr.imag.adele.apam.Link;
import fr.imag.adele.apam.RelToResolve;
import fr.imag.adele.apam.RelationDefinition;
import fr.imag.adele.apam.Specification;
import fr.imag.adele.apam.apform.ApformComponent;
import fr.imag.adele.apam.declarations.ComponentDeclaration;
import fr.imag.adele.apam.declarations.InjectedPropertyPolicy;
import fr.imag.adele.apam.declarations.PropertyDefinition;
import fr.imag.adele.apam.declarations.RelationDeclaration;
import fr.imag.adele.apam.declarations.references.resources.ResourceReference;
import fr.imag.adele.apam.util.ApamFilter;
import fr.imag.adele.apam.util.Attribute;
import fr.imag.adele.apam.util.Substitute;
import fr.imag.adele.apam.util.Util;
import fr.imag.adele.apam.util.Visible;

public abstract class ComponentImpl extends ConcurrentHashMap<String, Object> implements Component, Comparable<Component> {

  /**
   * An exception that can be thrown in the case of problems while creating a
   * component
   */
  public static class InvalidConfiguration extends Exception {

    private static final long serialVersionUID = 1L;

    public InvalidConfiguration(String message) {
      super(message);
    }

    public InvalidConfiguration(String message, Throwable cause) {
      super(message, cause);
    }

    public InvalidConfiguration(Throwable cause) {
      super(cause);
    }

  }

  private final Object componentId = new Object(); // only for hashCode

  private static final long serialVersionUID = 1L;

  protected static Logger logger = LoggerFactory
      .getLogger(ComponentImpl.class);
  private final ApformComponent apform;

  private final ComponentDeclaration declaration;
 
  /*
   * set to true if an instantiation failed.
   * True means : do not try to instantiate again
   */
  private boolean instantiateFails = false ;

  // Contains the composite type that was the first to physically deploy that
  // component.
  private CompositeType firstDeployed;

  // Set of Dependencies
  private Map<String, RelationDefinition> relDef = new HashMap<String, RelationDefinition>();
 
  protected final Set<Link> links   = Collections.newSetFromMap(new ConcurrentHashMap<Link, Boolean>());

  protected final Set<Link> invlinks   = Collections.newSetFromMap(new ConcurrentHashMap<Link, Boolean>());

  public ComponentImpl(ApformComponent apform) throws InvalidConfiguration {
    if (apform == null) {
      throw new InvalidConfiguration("Null apform instance while creating component");
    }

    this.apform = apform;
    this.declaration = apform.getDeclaration();
  }

  /**
   * Whether the component is instantiable
   */
  @Override
  public boolean canSee(Component target) {
    return Visible.isVisible(this, target);
  }

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

  @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;
  }

  /**
   * Components are uniquely represented in the Apam state model, so we use
   * reference equality in all comparisons.
   */
  @Override
  public final boolean equals(Object o) {
    return (this == o);
  }

  /**
   * To be called when the object is fully loaded and chained. Terminate the
   * generic initialization : computing the dependencies, and the properties
   *
   * @param initialProperties
   */
  public void finishInitialize(Map<String, String> initialProperties) {
    initializeRelations();
    initializeProperties(initialProperties);
  }

  /**
   * Get the value of all the properties in the component.
   *
   */
  @Override
  public Map<String, Object> getAllProperties() {
    return Collections.unmodifiableMap(this);
  }

  @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;
  }

  @Override
  public final ApformComponent getApformComponent() {
    return apform;
  }
 
  /**
   * Tries to find the definition of a property, the property can be valued in this component
   * or in its defining group
   *
   * @param attribute
   * @return
   */ 
  @Override
  public PropertyDefinition getPropertyDefinition(String attribute) {
    return getAttrDefinition(attribute);
  }
 

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

  @Override
  public final ComponentDeclaration getDeclaration() {
    return declaration;
  }

  /**
   * 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;
  }

  private Set<Component> getFInalLinkDests(String depName) {

    Set<Component> dests = new HashSet<Component>();
    // if (depName.equals(CST.REL_GROUP)) {
    // dests.add(getGroup()) ;
    // return dests ;
    // }
    // if (depName.equals(CST.REL_MEMBERS)) {
    // dests.addAll(getMembers()) ;
    // return dests ;
    // }
    if (depName.equals(CST.REL_COMPOSITE)) {
      if (this instanceof Instance) {
        dests.add(((Instance) this).getComposite());
      }
      return dests;
    }
    if (depName.equals(CST.REL_COMPOTYPE)) {
      if (this instanceof Instance) {
        dests.add(((Instance) this).getComposite().getCompType());
      } else if (this instanceof Implementation) {
        dests.addAll(((Implementation) this).getInCompositeType());
      }
      return dests;
    }
    if (depName.equals(CST.REL_CONTAINS)) {
      if (this instanceof Composite) {
        dests.addAll(((Composite) this).getContainInsts());
      } else if (this instanceof CompositeType) {
        dests.addAll(((CompositeType) this).getImpls());
      }
      return dests;
    }

    if (depName.equals(CST.REL_SPEC)) {
      if (this instanceof Specification) {
        dests.add(this);
      } else if (this instanceof Implementation) {
        dests.add(((Implementation) this).getSpec());
      } else if (this instanceof Instance) {
        dests.add(((Instance) this).getSpec());
      }
      return dests;
    }
    if (depName.equals(CST.REL_IMPL)) {
      if (this instanceof Implementation) {
        dests.add(this);
      } else if (this instanceof Instance) {
        dests.add(((Instance) this).getImpl());
      }
      return dests;
    }
    if (depName.equals(CST.REL_INST)) {
      if (this instanceof Instance) {
        dests.add(this);
      }
      return dests;
    }

    if (depName.equals(CST.REL_IMPLS)) {
      if (this instanceof Specification) {
        dests.addAll(this.getMembers());
      } else if (this instanceof Implementation) {
        dests.addAll(((Implementation) this).getSpec().getMembers());
      } else {
        dests.addAll(((Instance) this).getSpec().getMembers());
      }
      return dests;
    }
    if (depName.equals(CST.REL_INSTS)) {
      if (this instanceof Implementation) {
        dests.addAll(this.getMembers());
      } else if (this instanceof Instance) {
        dests.addAll(((Instance) this).getImpl().getMembers());
      }
      return dests;
    }
    if (depName.equals(CST.REL_INST)) {
      if (this instanceof Instance) {
        dests.add(this);
      }
      return dests;
    }
    return dests;
  }

  @Override
  public CompositeType getFirstDeployed() {
    return firstDeployed == null ? CompositeTypeImpl.getRootCompositeType()
        : firstDeployed;
  }

  @Override
  public Link getInvLink(Component destInst) {
    if (destInst == null) {
      return null;
    }
    for (Link link : invlinks) {
      if (link.getDestination() == destInst) {
        return link;
      }
    }
    return null;
  }

  @Override
  public Link getInvLink(Component destInst, String depName) {
    if (destInst == null) {
      return null;
    }
    for (Link link : invlinks) {
      if ((link.getDestination() == destInst)
          && (link.getName().equals(depName))) {
        return link;
      }
    }
    return null;
  }

  @Override
  public Set<Link> getInvLinks() {
    return Collections.unmodifiableSet(invlinks);
  }

  @Override
  public Set<Link> getInvLinks(Component destInst) {
    if (destInst == null) {
      return null;
    }
    Set<Link> w = new HashSet<Link>();
    for (Link link : invlinks) {
      if (link.getDestination() == destInst) {
        w.add(link);
      }
    }
    return w;
  }

  @Override
  public Set<Link> getInvLinks(String depName) {
    Set<Link> w = new HashSet<Link>();
    for (Link link : invlinks) {
      if ((link.getDestination() == this)
          && (link.getName().equals(depName))) {
        w.add(link);
      }
    }
    return w;
  }

  @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);
  }

  /**
   * resolve
   */
  @Override
  public Component getLinkDest(String depName) {
    Link link = getLink(depName);
    return (link == null) ? null : link.getDestination();
  }

  // =================================== Links =========
  /**
   * returns the connections towards the service instances actually used.
   * return only APAM links.
   */
  @Override
  public Set<Component> getLinkDests(String depName) {
    if (depName == null || depName.isEmpty()) {
      return null;
    }

    if (CST.isFinalRelation(depName)) {
      return getFInalLinkDests(depName);
    }

    Set<Component> dests = new HashSet<Component>();
    for (Link link : getLinks(depName)) {
      if (link.getName().equals(depName)) {
        dests.add(link.getDestination());
      }
    }
    return dests;
  }

  /**
   * Inherits and resolve
   */
  @Override
  public Set<Link> getLinks(String relName) {
    Set<Link> dests = getExistingLinks(relName);
    if (!dests.isEmpty()) {
      return dests;
    }

    // None are present. Try to resolve
    RelationDefinition rel = getRelation(relName);
    if (rel == null) {
      logger.error("relation " + relName + " undefined for " + this);
      return null;
    }

    // if (!rel.isLazy())
    // return dests ;

    Component source = rel.getRelSource(this);
    CST.apamResolver.resolveLink(source, rel);
    return getExistingLinks(relName);
  }

  /**
   * Only return the links instantiated on that source component (no
   * inheritance, no resolution)
   */
  public Set<Link> getLocalLinks() {
    return Collections.unmodifiableSet(links);
  }

  protected RelationDefinition getLocalRelation(String id) {
    return relDef.get(id);
  }

  @Override
  public Collection<RelationDefinition> getLocalRelations() {
    return Collections.unmodifiableCollection(relDef.values());
  }

  @Override
  public final String getName() {
    return declaration.getName();
  }

  /**
   * Get the value of the property.
   *
   * Attributes are supposed to be correct and inherited statically
   *
   */
  @Override
  public String getProperty(String attr) {
    return Util.toStringAttrValue(getPropertyObject(attr));
  }

  /**
   * Get the value of a property, the property can be valued in this component
   * or in its defining group Return will be an object of type int, String,
   * boolean for attributes declared int, String, boolean String for an
   * enumeration.
   *
   * For sets, the return will be an array of the corresponding types. i.e;
   * int[], String[] and so on. Returns null if attribute is not defined or
   * not set.
   */
  @Override
  public Object getPropertyObject(String attribute) {
    Object value = get(attribute);
    if (value == null) {
      return null;
      // all attributes, including default values are already there
      // if(value==null && getDeclaration()!=null)
      // value=getDeclaration().getProperty(attribute);
    }

    return Substitute.substitute(attribute, value, this);
  }

  @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);
  }

  // Set<ResourceReference> allResources = new HashSet<ResourceReference> () ;
  // Component current = this ;
  // while (current != null) {
  // if (current.getDeclaration().getProvidedResources() != null)
  // allResources.addAll (current.getDeclaration().getProvidedResources()) ;
  // current = current.getGroup() ;
  // }
  // return allResources ;
  // }

  /**
   * WARNING : no resolution
   */
  @Override
  public Set<Component> getRawLinkDests() {
    Set<Component> dests = new HashSet<Component>();

    for (Link link : getRawLinks()) {
      dests.add(link.getDestination());
    }
    return dests;
  }

  /**
   * No resolution
   *
   * @param depName
   * @return
   */
  public Set<Component> getRawLinkDests(String depName) {
    if (depName == null || depName.isEmpty()) {
      return null;
    }

    if (CST.isFinalRelation(depName)) {
      return getFInalLinkDests(depName);
    }

    Set<Component> dests = new HashSet<Component>();
    for (Link link : getRawLinks()) {
      if (link.getName().equals(depName)) {
        dests.add(link.getDestination());
      }
    }
    return dests;
  }

  /**
   * 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;
  }

  /**
   * Return the relation with name "id" if it can be applied to this
   * component.
   *
   * 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.
   *
   * 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;
  }

  @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;
  }

  @Override
  public Map<String, String> getValidAttributes() {
    Map<String, String> ret = new HashMap<String, String>();
    for (PropertyDefinition def : declaration.getPropertyDefinitions()) {
      ret.put(def.getName(), def.getType());
    }
    if (getGroup() != null) {
      ret.putAll(getGroup().getValidAttributes());
    }
    return ret;
  }

  /**
   * Override to make hash code conform to the equality definition
   */
  @Override
  public final int hashCode() {
    return componentId.hashCode();
  }

  /**
   * Provided a component, compute its effective relations definition, adding group
   * constraint and flags. It is supposed to be correct !! No failure expected
   *
   * Does not add those dependencies defined "above".
   * For relations refined locally, merge the local definition with the group definition.
   *
   * For instances only, add the relation definition overridden by the composite.
   *
   * 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();
    }

  }

  /**
   * Given a relation declared in this component, checks if the provided
   * override relation matches the relation declaration.
   *
   * 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;
  }
 
  /**
   * To be called once the Apam entity is fully initialized.
   *
   * Computes all its attributes, including inheritance. Checks if initial properties are consistent with the
   * declarations.
   *
   * 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());
      }
    }
  }

  /**
   * 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;
  }

  /**
   * Whether this component is a descendant of the specified component
   */
  @Override
  public boolean isDescendantOf(Component group) {
    assert group != null;
    return group.isAncestorOf(this);
  }

  public boolean isInternalInstantiable () {
    if (declaration.isDefinedInstantiable() || getGroup() == null) {
      return (declaration.isInstantiable())  ;
    }
    return ((ComponentImpl)getGroup()).isInternalInstantiable();
  }
 
  /**
   * Whether the component can be instantiated
   */
  @Override
  public boolean isInstantiable() {
    if (instantiateFails) return false ;
    if (!isInternalInstantiable()) return false ;
   
    if (this instanceof Implementation) {
      if (isSingleton() && !getMembers().isEmpty() ) return false;     
    }
    return true ;
  }

  /**
   * Set the flag InstantiateFails which when true means : do not try to instantiate.
   * Should be used only by the resolver when createinstance fails
   * @param doNotTry
   */
  public void setInstantiateFails (boolean doNotTryToInstantiate) {
    instantiateFails = doNotTryToInstantiate ;
  }
 
  /**
   * Whether the component is shared
   */
  @Override
  public boolean isShared() {
    if (declaration.isDefinedShared() || getGroup() == null) {
      return declaration.isShared();
    }
    return getGroup().isShared();
  }

  /**
   * Whether the component is singleton
   */
  @Override
  public boolean isSingleton() {
    if (declaration.isDefinedSingleton() || getGroup() == null) {
      return declaration.isSingleton();
    }
    return getGroup().isSingleton();
  }

  public boolean isSubstitute(String attr) {
    PropertyDefinition def = getDeclaration().getPropertyDefinition(attr);
    return (def != null && def.hasDefaultValue() && (def.getDefaultValue().charAt(0) == '$' || def.getDefaultValue().charAt(0) == '@'));
  }

  /**
   * The link keeps the relToResolve used for its resolution.
   * We make the hypothesis that the definition did not change,
   * and the source and destination did not change of composite.
   *
   * @param link
   * @return
   */
//  public boolean isValidLink(Link link) {
//    return link.isValid() ;
//    RelToResolve relToResolve = link.getRelToResolve();
//    if (relToResolve == null) return false ;
//   
//
//    return relToResolve.matchRelationConstraints(link.getDestination());
//  }

  @Override
  public boolean match(ApamFilter goal) {
    if (goal == null) {
      return true;
    }
    return goal.match(this);
  }

  @Override
  public boolean match(String goal) {
    if (goal == null) {
      return true;
    }
    ApamFilter f = ApamFilter.newInstance(goal);
    if (f == null) {
      return false;
    }
    return goal == null || f.match(this.getAllProperties());
  }

  @Override
  public boolean matchRelation(RelToResolve dep) {
    return dep.matchRelation(this);
  }

  @Override
  public boolean matchRelationConstraints(RelToResolve dep) {
    return dep.matchRelationConstraints(dep.getTargetKind(),
        this.getAllProperties());
  }

  @Override
  public boolean matchRelationTarget(RelToResolve dep) {
    return dep.matchRelationTarget(this);
  }

  /**
   * set the value, update apform and the platform, notify managers and
   * propagates to the members, recursively
   *
   * @param com
   *            the component on which ot set the attribute
   * @param attr
   *            attribute name
   * @param value
   *            attribute value
   */

  private void propagate(String attr, Object value) {
    if (value == null) {
      return;
    }
    Object oldValue = get(attr);
    if (oldValue != null && oldValue.equals(value.toString())) {
      return;
    }

    // Change value
    put(attr, value);

    // Notify the execution platform
    if (get(attr) == value) {
      getApformComponent().setProperty(attr, value.toString());
    }

    /*
     * notify property managers
     */
    if (oldValue == null) {
      ApamManagers.notifyAttributeAdded(this, attr, value.toString());
    } else {
      ApamManagers.notifyAttributeChanged(this, attr, value.toString(),
          oldValue.toString());
    }

    // Propagate to members recursively
    for (Component member : getMembers()) {
      ((ComponentImpl) member).propagate(attr, value);
    }
  }

  /*
   * Filter evaluation on the properties of this component
   */

  /**
   * TODO. Should we notify at all levels ?
   *
   * @param ent
   * @param attr
   */
  private void propagateRemove(String attr) {

    remove(attr);
    for (Component member : getMembers()) {
      ((ComponentImpl) member).propagateRemove(attr);
    }
  }

  /**
   * This methods adds a newly created component to the Apam state model, so
   * that it is visible to the external API
   */
  public abstract void register(Map<String, String> initialProperties)
      throws InvalidConfiguration;

  public void removeInvLink(Link link) {
    invlinks.remove(link);
    // if (invLinks.isEmpty()) {
    /*
     * This instance is no longer used. We do not set it unused
     * setUsed(false); setOwner(CompositeImpl.getRootAllComposites());
     *
     * Because it must stay in the same composite since it may be the target
     * of an "OWN" clause, and must not be changed. In case it will be
     * re-used (local).
     */
    // }
  }

  // @Override
  public void removeLink(Link link) {
    if (getApformComponent().remLink(link.getDestination(), link.getName())) {
      links.remove(link);
      // TODO distriman: check if we have the destination implementation,
      // is this the right way to do it?

      // if(link.getDestination().getImpl()!=null)
      // ((ImplementationImpl)
      // getImpl()).removeUses(link.getDestination().getImpl());

    } else {
      logger.error("INTERNAL ERROR: link from " + this + " to "
          + link.getDestination()
          + " could not be removed in the real instance.");
    }
  }

  /**
   * Removes the specifed property
   *
   */
  @Override
  public boolean removeProperty(String attr) {
    return removeProperty(attr, false);
  }

  /**
   * 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;
  }

  /**
   * Sets all the values of the specified properties
   *
   * We validate all attributes before actually modifying the value to avoid
   * partial modifications ?
   */
  @Override
  public boolean setAllProperties(Map<String, String> properties) {
    for (Map.Entry<String, String> entry : properties.entrySet()) {

      if (!setProperty(entry.getKey(), entry.getValue())) {
        return false;
      }
    }
    return true;
  }

  public void setFirstDeployed(CompositeType father) {
    firstDeployed = father;
  }

  /**
   * Set the value of the property in the Apam state model. Changing an
   * attribute notifies the property manager, the underlying execution
   * platform (apform), and propagates to members
   */
  @Override
  public boolean setProperty(String attr, Object value) {
    return setProperty(attr, value, false);
  }

  /**
   * 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 String toString() {
    return getKind() + " " + getName();
  }

  /**
   * This method removes a component from the Apam state model, it must ensure
   * that the component is no longer referenced by any other component or
   * visible by the external API. Should be called ONLY from the Broker.
   */
  abstract void unregister();

  /**
   * 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.
   *
   * 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.
   *
   * 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;
  }

}
TOP

Related Classes of fr.imag.adele.apam.impl.ComponentImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.