Package fr.imag.adele.apam.apform.impl.handlers

Source Code of fr.imag.adele.apam.apform.impl.handlers.PropertyInjectionHandler$Description

/**
* 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.apform.impl.handlers;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.felix.ipojo.ConfigurationException;
import org.apache.felix.ipojo.FieldInterceptor;
import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
import org.apache.felix.ipojo.architecture.HandlerDescription;
import org.apache.felix.ipojo.metadata.Attribute;
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.parser.FieldMetadata;
import org.apache.felix.ipojo.parser.MethodMetadata;

import fr.imag.adele.apam.Component;
import fr.imag.adele.apam.Instance;
import fr.imag.adele.apam.apform.impl.ApamAtomicComponentFactory;
import fr.imag.adele.apam.apform.impl.ApamComponentFactory;
import fr.imag.adele.apam.apform.impl.ApamInstanceManager;
import fr.imag.adele.apam.apform.impl.PropertyCallback;
import fr.imag.adele.apam.declarations.AtomicImplementationDeclaration;
import fr.imag.adele.apam.declarations.ImplementationDeclaration;
import fr.imag.adele.apam.declarations.InjectedPropertyPolicy;
import fr.imag.adele.apam.declarations.PropertyDefinition;
import fr.imag.adele.apam.impl.InstanceImpl;

public class PropertyInjectionHandler extends ApformHandler implements  FieldInterceptor {

  /**
   * The registered name of this iPojo handler
   */
  public static final String NAME = "properties";

  /**
   * Whether this handler is required for the specified configuration
   */
  public static boolean isRequired(AtomicImplementationDeclaration componentDeclaration) {
   
    for (PropertyDefinition definition : componentDeclaration.getPropertyDefinitions()) {

      if (definition.getField() != null)
        return true;

      if (definition.getCallback() != null)
        return true;
    }
   
      return false;
  }
 
  @Override
  public void initializeComponentFactory(ComponentTypeDescription typeDesc, Element metadata) throws ConfigurationException {

    if (!(getFactory() instanceof ApamAtomicComponentFactory))
      return;

    ApamAtomicComponentFactory implementation = (ApamAtomicComponentFactory) getFactory();
    ImplementationDeclaration declaration = implementation.getDeclaration();

    if (!(declaration instanceof AtomicImplementationDeclaration))
      return;

    AtomicImplementationDeclaration primitive = (AtomicImplementationDeclaration) declaration;
    for (PropertyDefinition definition : primitive.getPropertyDefinitions()) {

      if (definition.getField() != null) {
        FieldMetadata field = getPojoMetadata().getField(
            definition.getField());
        if (field == null)
          throw new ConfigurationException("Invalid property definition "  + definition.getName()+ ": the specified field does not exist");

      }

      if (definition.getCallback() != null) {
        MethodMetadata method = getPojoMetadata().getMethod(definition.getCallback());
        if (method == null)
          throw new ConfigurationException("Invalid property definition "  + definition.getName() + ": the specified method does not exist");
      }
    }
  }

  @Override
  public void configure(Element metadata,  @SuppressWarnings("rawtypes") Dictionary configurationthrows ConfigurationException {
    /*
     * Add interceptors to delegate property injection
     *
     * NOTE All validations were already performed when validating the
     * factory @see initializeComponentFactory, including initializing
     * unspecified properties with appropriate default values. Here we just
     * assume metadata is correct.
     */

    if (!(getFactory() instanceof ApamAtomicComponentFactory))
      return;

    ApamAtomicComponentFactory implementation = (ApamAtomicComponentFactory) getFactory();
    ImplementationDeclaration declaration = implementation.getDeclaration();

    if (!(declaration instanceof AtomicImplementationDeclaration))
      return;

    AtomicImplementationDeclaration primitive = (AtomicImplementationDeclaration) declaration;
    for (PropertyDefinition definition : primitive.getPropertyDefinitions()) {

      if (definition.getField() != null) {
        FieldMetadata field = getPojoMetadata().getField(definition.getField());
        getInstanceManager().register(field,this);
      }

      if (definition.getCallback() != null) {
        getInstanceManager().addCallback(new PropertyCallback(getInstanceManager(), definition));
      }
    }
  }
 
  public void setApamComponent(Component component) {
   
    if (component == null)
      return;
   
    ApamAtomicComponentFactory implementation = (ApamAtomicComponentFactory) getFactory();
    ImplementationDeclaration declaration = implementation.getDeclaration();

    if (!(declaration instanceof AtomicImplementationDeclaration))
      return;

    AtomicImplementationDeclaration primitive = (AtomicImplementationDeclaration) declaration;
    for (PropertyDefinition definition : primitive.getPropertyDefinitions()) {

      if (definition.getInjected() == InjectedPropertyPolicy.EXTERNAL)
        continue;
     
      if (definition.getField() != null) {
        FieldMetadata field = getPojoMetadata().getField(definition.getField());
       
        Object fieldValue  = getInstanceManager().getFieldValue(field.getFieldName());
       
        if (definition.getInjected() == InjectedPropertyPolicy.INTERNAL)
          onSet(getInstanceManager().getPojoObject(), field.getFieldName(), fieldValue);
       
        if (definition.getInjected() == InjectedPropertyPolicy.BOTH) {
          Object apamValue  = component.getPropertyObject(definition.getName());
         
          if (apamValue == null)
            onSet(getInstanceManager().getPojoObject(), field.getFieldName(), fieldValue);
        }

      }

    }   
  }

  @Override
  public Object onGet(Object pojo, String fieldName, Object currentValue) {

    if (getInstanceManager().getApamComponent() == null) {
      return currentValue;
    }
   
      if (!(getFactory() instanceof ApamAtomicComponentFactory))
        return currentValue;

      ApamAtomicComponentFactory implementation  = (ApamAtomicComponentFactory) getFactory();
      ImplementationDeclaration declaration    = implementation.getDeclaration();
     
      if (! (declaration instanceof AtomicImplementationDeclaration))
        return currentValue;
     
      AtomicImplementationDeclaration primitive  = (AtomicImplementationDeclaration) declaration;
      for (PropertyDefinition definition : primitive.getPropertyDefinitions()) {
       
        if (definition.getField() != null && definition.getField().equals(fieldName) ) {
         
          String property    = definition.getName();
          Instance instance   = getInstanceManager().getApamComponent();
          /*
           * For primitive property fields, always return the APAM value that is already of the
           * correct type and is always synchronized
           */
          if (! definition.isSet())
            return instance.getPropertyObject(property);
         
          /*
           * For multi-valued property fields, convert the APAM value to a collection that can
           * be injected into the field, and that track modifications
           */
          if (definition.isSet() ) {

            @SuppressWarnings("unchecked")
          Set<String> newValue     = (Set<String>) instance.getPropertyObject(property);
              BoundSet<?> injectedValue  = null;
             
            if (definition.getBaseType().equals("int") && newValue != null) {
             
              injectedValue = new BoundSet<Integer>(newValue,instance,definition) {
             
              protected final Integer cast(String element) {
                return Integer.valueOf(element);
              }

              protected final String uncast(Integer value) {
                return value.toString();
              }
             
              };
            }
            else if (definition.getBaseType().equals("boolean") && newValue != null) {
             
              injectedValue = new BoundSet<Boolean>(newValue,instance,definition) {
             
              protected final Boolean cast(String element) {
                return Boolean.valueOf(element);
              }

              protected final String uncast(Boolean value) {
                return value.toString();
              }
             
              };
               }
            else if (newValue != null) {
             
              injectedValue = new BoundSet<String>(newValue,instance,definition) {
             
              protected final String cast(String element) {
                return element;
              }

              protected final String uncast(String value) {
                return value;
              }
             
              };
               }
            
            if (injectedValue == currentValue)
                continue;

              return injectedValue;

          }
          
         
        }
       
      }
     
      return currentValue;
  }

  @Override
  public void onSet(Object pojo, String fieldName, Object newValue) {

    if (getInstanceManager().getApamComponent() == null) {
      return;
    }

      if (!(getFactory() instanceof ApamAtomicComponentFactory))
        return;

      ApamAtomicComponentFactory implementation  = (ApamAtomicComponentFactory) getFactory();
      ImplementationDeclaration declaration  = implementation.getDeclaration();
     
      if (! (declaration instanceof AtomicImplementationDeclaration))
        return;
     
      AtomicImplementationDeclaration primitive  = (AtomicImplementationDeclaration) declaration;
      for (PropertyDefinition definition : primitive.getPropertyDefinitions()) {
       
        if (definition.getField() != null && definition.getField().equals(fieldName)) {
         
          String property    = definition.getName();
          Instance instance   = getInstanceManager().getApamComponent();

          /*
           * If the field has been modified, and the injection type is not EXTERNAL update the Apam value
           */
              if (newValue != null && newValue instanceof BoundSet<?>)
                newValue = ((BoundSet<?>) newValue).unwrap();

              Object currentValue = instance.getPropertyObject(property);

              if (newValue != null && currentValue != null && currentValue.equals(newValue))
                continue;

              if (newValue == null && currentValue == null)
                continue;

                 if (definition.getInjected() != InjectedPropertyPolicy.EXTERNAL) {

                if (newValue == null)
                  ((InstanceImpl)instance).removeProperty(property,true);
               
                if (newValue != null)
                  ((InstanceImpl)instance).setProperty(property, newValue, true);
              }
              else {
                   System.out.println("ERROR modification of external property "+definition.getName()+" by field"+definition.getField());
          }
         
        }
       
      }
     
      return;
   
  }

  /**
   * The description of this handler instance
   *
   */
  private static class Description extends HandlerDescription {

    private final PropertyInjectionHandler propertyHandler;

    public Description(PropertyInjectionHandler propertyHandler) {
      super(propertyHandler);
      this.propertyHandler = propertyHandler;
    }

    @Override
    public Element getHandlerInfo() {
      Element root = super.getHandlerInfo();

      if (propertyHandler.getInstanceManager() instanceof ApamInstanceManager) {
        ApamInstanceManager instance = (ApamInstanceManager) propertyHandler.getInstanceManager();
        for (PropertyDefinition definition : instance.getFactory().getDeclaration().getPropertyDefinitions()) {

          /*
           * Ignore non injected properties
           */
          if (definition.getField() == null && definition.getCallback() == null)
            continue;

          String name = definition.getName();
          String field = definition.getField();
          String method = definition.getCallback();
          String value = instance.getApamComponent() != null ? instance.getApamComponent().getProperty(name) : null;

          Element property = new Element("property", ApamComponentFactory.APAM_NAMESPACE);
         
          property.addAttribute(new Attribute("name",  ApamComponentFactory.APAM_NAMESPACE,  name));
          property.addAttribute(new Attribute("field", ApamComponentFactory.APAM_NAMESPACE, field != null ? field : ""));
          property.addAttribute(new Attribute("method",ApamComponentFactory.APAM_NAMESPACE, method != null ? method : ""));
          property.addAttribute(new Attribute("value", ApamComponentFactory.APAM_NAMESPACE, value != null ? value : ""));

          root.addElement(property);
        }
      }
      return root;
    }

  }

  @Override
  public HandlerDescription getDescription() {
    return new Description(this);
  }

  @Override
  public void start() {
  }

  @Override
  public void stop() {
  }

  @Override
  public String toString() {
    return "APAM property manager for "  + getInstanceManager().getInstanceName();
  }
 
 
  /**
   * This class handles a typed set that is dynamically bound to a property of an APAM instance.
   * Changes to the set are automatically propagated to the property
   */
  private static abstract class BoundSet<E> implements Set<E> {
   
    private final Instance         instance;
    private final PropertyDefinition  property;
    private final Set<String>      backing;
   
    public BoundSet(Set<String> backing, Instance instance, PropertyDefinition property) {
     
      this.backing   = backing;
      this.instance  = instance;
      this.property  = property;
    }

    private Set<String> unwrap() {
      return backing;
    }

    /**
     * Updates the associated property when the backing collection is changed
     */
    private final void propagate() {
      ((InstanceImpl)instance).setProperty(property.getName(),this.unwrap(),true);
    }
   
    /**
     * Verifies that non-internal properties can only be modified through the APAM API
     */
    private final void checkModifiable() throws UnsupportedOperationException {
      if (property.getInjected()!=InjectedPropertyPolicy.INTERNAL)
        throw new UnsupportedOperationException("Field "+ property.getField()+
            " is associated to non-internal property "+property.getName()+
            " of component "+property.getComponent().getName()+
            " and its value can not be modified");
    }
   
    /**
     * Converts the string element of the backing collection to the type of the view
     */
    protected abstract E cast(String element);
   
    /**
     * Converts a value of the type of the view to a string that can be stored in the
     * backing collection
     */
    protected abstract String uncast(E value);

    /*
     * The Object contract
     */
    public boolean equals(Object o) {
      return o == this || backing.equals(o);
    }
   
    public int hashCode() {
      return backing.hashCode();
    }
   
    public String toString() {
      return backing.toString();
    }

    /*
     * The Collection contract
     *
     */
    public int size() {
      return backing.size();
    }

    public boolean isEmpty() {
      return backing.isEmpty();
    }

    @SuppressWarnings("unchecked")
    public boolean contains(Object o) {
      /*
       * WARNING This implementation restricts the parameter to be a value of the type of
       * the view. This is more restrictive that the contract of this method, but is needed
       * in order to ensure the automatic conversion.
       */
      return backing.contains(uncast((E)o));
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean containsAll(Collection<?> collection) {
      /*
       * WARNING This implementation restricts the parameter to be a value of the type of
       * the view. This is more restrictive that the contract of this method, but is needed
       * in order to ensure the automatic conversion.
       */
      List<String> elements = new ArrayList<String>();
      for(Object element : collection) {
        elements.add(uncast((E)element));
      }
     
      return backing.containsAll(elements);
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
        private final Iterator<String> backing = BoundSet.this.backing.iterator();
       
        public boolean hasNext() {
          return backing.hasNext();
        }
        public E next() {
          return BoundSet.this.cast(backing.next());
        }
       
        public void remove() {
          BoundSet.this.checkModifiable();
          backing.remove();
          BoundSet.this.propagate();
        }
      };
    }

    @Override
    public Object[] toArray() {
      List<E> elements = new ArrayList<E>();
      for(String element : backing) {
        elements.add(cast(element));
      }
     
      return elements.toArray();
    }
   
    public <T extends Object> T[] toArray(T[] a) {
      List<E> elements = new ArrayList<E>();
      for(String element : backing) {
        elements.add(cast(element));
      }
     
      return elements.toArray(a);
    }

    @Override
    public boolean add(E e) {
      checkModifiable();
      boolean added = backing.add(uncast(e));
      if (added)
        propagate();
     
      return added;
    }

    @Override
    public boolean addAll(Collection<? extends E> collection) {
      List<String> elements = new ArrayList<String>();
      for(E element : collection) {
        elements.add(uncast(element));
      }
     
      checkModifiable();
      boolean added = backing.addAll(elements);
      if (added)
        propagate();
     
      return added;
    }
   
    @Override
    @SuppressWarnings("unchecked")
    public boolean remove(Object o) {
      /*
       * WARNING This implementation restricts the parameter to be a value of the type of
       * the view. This is more restrictive that the contract of this method, but is needed
       * in order to ensure the automatic conversion.
       */
      checkModifiable();
      boolean removed = backing.remove(uncast((E)o));
      if (removed)
        propagate();
     
      return removed;
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean removeAll(Collection<?> collection) {
      /*
       * WARNING This implementation restricts the parameter to be a value of the type of
       * the view. This is more restrictive that the contract of this method, but is needed
       * in order to ensure the automatic conversion.
       */
      List<String> elements = new ArrayList<String>();
      for(Object element : collection) {
        elements.add(uncast((E)element));
      }
     
      checkModifiable();
      boolean removed = backing.removeAll(elements);
      if (removed)
        propagate();
      return removed;
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean retainAll(Collection<?> collection) {
      /*
       * WARNING This implementation restricts the parameter to be a value of the type of
       * the view. This is more restrictive that the contract of this method, but is needed
       * in order to ensure the automatic conversion.
       */
      List<String> elements = new ArrayList<String>();
      for(Object element : collection) {
        elements.add(uncast((E)element));
      }
     
      checkModifiable();
      boolean removed = backing.retainAll(elements);
      if (removed)
        propagate();
     
      return removed;
    }
   
    @Override
    public void clear() {
      checkModifiable();
      backing.clear();
      propagate();
    }
  }

}
TOP

Related Classes of fr.imag.adele.apam.apform.impl.handlers.PropertyInjectionHandler$Description

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.