All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.tidalwave.metadata.spi.impl.MetadataItemAspect Maven / Gradle / Ivy

The newest version!
/***********************************************************************************************************************
 *
 * blueMarine Metadata - open source media workflow
 * Copyright (C) 2007-2011 by Tidalwave s.a.s. (http://www.tidalwave.it)
 *
 ***********************************************************************************************************************
 *
 * 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.
 *
 ***********************************************************************************************************************
 *
 * WWW: http://bluemarine.tidalwave.it
 * SCM: https://kenai.com/hg/bluemarine~metadata-src
 *
 **********************************************************************************************************************/
package it.tidalwave.metadata.spi.impl;

import java.lang.reflect.Method;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyChangeListener;
import java.beans.PropertyDescriptor;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import it.tidalwave.util.logging.Logger;
import net.sf.cglib.proxy.MethodProxy;
import org.jdesktop.beansbinding.BeanProperty;
import org.jdesktop.beansbinding.Property;
import it.tidalwave.beans.AbstractEnhancer;
import it.tidalwave.beans.FastBeanProperty;
import it.tidalwave.beans.JavaBean;
import it.tidalwave.beans.JavaBeanAspect;
import java.io.Serializable;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

/*******************************************************************************
 *
 * This special Interceptor enhances beans providing the implementation of the
 * MetadataItem interface and the special behavior for isXXXAvailable() 
 * properties.
 * 
 * @author  Fabrizio Giudici
 * @version $Id$
 *
 ******************************************************************************/
public class MetadataItemAspect extends JavaBeanAspect 
  {
    private final static String CLASS = MetadataItemAspect.class.getName();
    private final static Logger logger = Logger.getLogger(CLASS);
    private final static long serialVersionUID = 6408780450730439759L;
    
    private final static String AVAILABLE_SUFFIX = "Available";
     
//    private final static Property AVAILABLE_PROPERTY = BeanProperty.create("available");
    
//    private final static String PROP_LATEST_MODIFICATION_TIME = "latestModificationTime";
    
    private final static List METADATAITEM_METHODS = Arrays.asList(JavaBean.class.getMethods());
        
//    private Date latestModificationTime;
    
//    private boolean available;
    
//    private boolean oldAvailable;
    
    interface SerializablePropertyChangeListener extends PropertyChangeListener, Serializable
      {
      }
    
    /***************************************************************************
     *
     * Provides support for updating properties latestModificationTime
     * and available when the inner beans are updated.
     * 
     **************************************************************************/
//    private final PropertyChangeListener innerBeanListener = new SerializablePropertyChangeListener() 
//      {
//        public void propertyChange (final PropertyChangeEvent event) 
//          {
//            if (event.getPropertyName().equals(PROP_LATEST_MODIFICATION_TIME))
//              {
//                updateLatestModificationTime((Date)event.getNewValue());
//              }
//            
//            else
//              {
//                updateAvailability(oldAvailable, (Boolean)AVAILABLE_PROPERTY.getValue(bean));
//              }
//          }
//      };

    /***************************************************************************
     *
     * 
     **************************************************************************/
    public MetadataItemAspect (@Nonnull final Object bean, 
                               @Nonnull final AbstractEnhancer enhancer,
                               @Nonnull final Object ... arguments)
      {
        super(fixInitialStatus(bean), enhancer, arguments);
        
//        for (final JavaBean enhancedObject : getEnhancedObjects())
//          {
//            enhancedObject.addPropertyChangeListener(innerBeanListener);
//          }
        
        for (final Object argument : arguments)
          {
            if (argument instanceof Date)
              {
                // TODO: fail assertion to spot points where you call this
//                this.latestModificationTime = (Date)argument;        
                break;
              }
          }
        
//        oldAvailable = (Boolean)AVAILABLE_PROPERTY.getValue(bean);  
      }

    /***************************************************************************
     *
     * {@inheritDoc}
     * 
     **************************************************************************/
//    public boolean isAvailable()
//      {
//        return available;
//      }
    
    /***************************************************************************
     *
     * {@inheritDoc}
     * 
     **************************************************************************/
//    public Date getLatestModificationTime() 
//      {
//        return latestModificationTime;
//      }
    
    /***************************************************************************
     *
     * {@inheritDoc}
     * 
     **************************************************************************/
    @Override
    @CheckForNull
    public Object intercept (@Nonnull final Object object, 
                             @Nonnull final Method method, 
                             @Nonnull final Object[] methodParameters, 
                             @Nonnull final MethodProxy proxy) 
      throws Throwable 
      {
        Object result;
        final String methodName = method.getName();
        
        if (method.getName().startsWith("get") && (methodParameters.length == 0))
          {
            result = interceptGet(object, method, methodParameters, proxy);
          }
        
        else if (methodName.startsWith("set") && (methodParameters.length == 1))
          {
            result = interceptSet(object, method, methodParameters, proxy);
          }
        
        else
          {
            result = super.intercept(object, method, methodParameters, proxy);        
          }

        return result;
      }
    
    /***************************************************************************
     *
     * 
     **************************************************************************/
    @CheckForNull
    private Object interceptGet (@Nonnull final Object object, 
                                 @Nonnull final Method method, 
                                 @Nonnull final Object[] methodParameters, 
                                 @Nonnull final MethodProxy proxy) 
      throws Throwable 
      {
        Object result;
        
        if (METADATAITEM_METHODS.contains(method))
          {
            result = method.invoke(this, methodParameters);
          }
        
        else
          {
            result = super.intercept(object, method, methodParameters, proxy);   
          }
        
        return result;
      }

    /***************************************************************************
     *
     * 
     **************************************************************************/
    @CheckForNull
    private Object interceptSet (@Nonnull final Object object, 
                                 @Nonnull final Method method, 
                                 @Nonnull final Object[] methodParameters, 
                                 @Nonnull final MethodProxy proxy)
      throws Throwable                                
      {
//        final boolean oldAvailable = (Boolean)AVAILABLE_PROPERTY.getValue(bean);
        Object result = super.intercept(object, method, methodParameters, proxy);   
        final String changedPropertyName = getPropertyName(method);
        final boolean isAvailabilityProperty = changedPropertyName.endsWith(AVAILABLE_SUFFIX);

        // if property is set to non null, setPropertyAvailability(true)
        if (!isAvailabilityProperty && (methodParameters[0] != null))
          {
            final String propertyAvailabilityName = changedPropertyName + AVAILABLE_SUFFIX;
            final Property propertyAvailability = BeanProperty.create(propertyAvailabilityName);
            final boolean oldValue = (Boolean)propertyAvailability.getValue(bean);

            if (!oldValue)
              {
                logger.finest(">>>> %s - %s set to not null, setting %sAvailable=true", 
                                            object, changedPropertyName, changedPropertyName);
                propertyAvailability.setValue(bean, true);
                propertyChangeSupport.firePropertyChange(propertyAvailabilityName, oldValue, true);
              }
          }

        // if the availability is set to false, setProperty(null)
        else if (isAvailabilityProperty && Boolean.FALSE.equals(methodParameters[0]))
          {
            final String propertyName = changedPropertyName.replaceAll(AVAILABLE_SUFFIX + "$", "");
            final Property property = BeanProperty.create(propertyName);
            final Object oldValue = property.getValue(bean);

            if (oldValue != null)
              {
                logger.finest(">>>> %s - %sAvailable set to false, setting %s=null", 
                                            object, propertyName, propertyName);
                property.setValue(bean, null);
                propertyChangeSupport.firePropertyChange(propertyName, oldValue, null);
              }
          }
        
//        updateAvailability(oldAvailable, (Boolean)AVAILABLE_PROPERTY.getValue(bean));
//        updateLatestModificationTime(new Date());
        
        return result;
      }

    /***************************************************************************
     *
     * Fixes the initial status of the bean setting xxxAvailable
     * properties when the related xxx property is not null.
     * 
     * @param  bean  the bean
     * @return       the bean
     * 
     **************************************************************************/
    @Nonnull
    private static Object fixInitialStatus (@Nonnull final Object bean)
      {
        logger.fine("fixInitialStatus(%s)", bean);
        validateBean(bean);
        
        final Class beanClass = bean.getClass();
        final PropertyDescriptor[] propertyDescriptors;
        
        try
          {
            propertyDescriptors = Introspector.getBeanInfo(beanClass).getPropertyDescriptors();
          }
        catch (IntrospectionException e)
          {
            throw new RuntimeException(e);
          }
        
        for (final PropertyDescriptor propertyDescriptor : propertyDescriptors)
          {
            final String propertyName =  propertyDescriptor.getName();
            
            if (!propertyName.endsWith(AVAILABLE_SUFFIX))
              {
                final Property property = FastBeanProperty.create(propertyName);
                final Property availabilityProperty = FastBeanProperty.create(propertyName + AVAILABLE_SUFFIX);
                // BeanProperty triggers ConcurrentModificationException:
                // see https://beansbinding.dev.java.net/issues/show_bug.cgi?id=57
//                final Property property = BeanProperty.create(propertyName);
//                final Property availabilityProperty = BeanProperty.create(propertyName + AVAILABLE_SUFFIX);
                
                if (availabilityProperty.isReadable(bean) && availabilityProperty.isWriteable(bean))
                  {
//                    logger.finer(">>>> checking property %s", propertyName));

                    if (property.getValue(bean) != null)
                      {
                        if (!(Boolean)availabilityProperty.getValue(bean))
                          {
                            logger.finest(">>>> %s is not null, setting %s%s=true", 
                                          propertyName, propertyName, AVAILABLE_SUFFIX);
                            availabilityProperty.setValue(bean, true);
                          }
                      }
                  }
              }
          }
        
        return bean;
      }

    /***************************************************************************
     *
     * Updates the availability property and eventually fire an event.
     * 
     * @param  oldAvailable  the old value
     * @param  newAvailable  the new value
     * 
     **************************************************************************/
//    private void updateAvailability (final boolean oldAvailable, final boolean available) 
//      {
//        this.available = available;
//        
//        if (available != oldAvailable) 
//          {
//            propertyChangeSupport.firePropertyChange("available", oldAvailable, available);
//          }
//      }

    /***************************************************************************
     *
     * Updates the latestModificationTime property and eventually
     * fires an event.
     * 
     * @param  timestamp  the new timestamp
     * 
     **************************************************************************/
//    private void updateLatestModificationTime (final Date timestamp) 
//      {
//        if ((this.latestModificationTime == null) || this.latestModificationTime.before(timestamp)) 
//          {
//            final Date oldValue = this.latestModificationTime;
//            this.latestModificationTime = timestamp;
//            propertyChangeSupport.firePropertyChange(PROP_LATEST_MODIFICATION_TIME, oldValue,timestamp);
//          }
//      }

    /***************************************************************************
     *
     * Validates the bean, checking if mandatory properties are defined.
     * 
     * @param   bean                      the bean to validate
     * @throws  IllegalArgumentException  if the bean is not valid
     * 
     **************************************************************************/
    private static void validateBean (@Nonnull final Object bean) 
      throws IllegalArgumentException 
      {
//        if (!AVAILABLE_PROPERTY.isReadable(bean)) 
//          {
//            throw new IllegalArgumentException(String.format("Unreadable property %s on %s", AVAILABLE_PROPERTY, bean.getClass()));
//          }
      }
  }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy