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

it.tidalwave.metadata.viewer.impl.Syncer Maven / Gradle / Ivy

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

import java.lang.reflect.Method;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyDescriptor;
import java.util.Arrays;
import java.util.List;
import it.tidalwave.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.jdesktop.beansbinding.Property;
import it.tidalwave.beans.FastBeanProperty;
import java.util.Date;

/*******************************************************************************
 *
 * @author  Fabrizio Giudici
 * @version $Id$
 *
 ******************************************************************************/
public class Syncer implements PropertyChangeListener
  {
    private static final String CLASS = Syncer.class.getName();
    private static final Logger logger = Logger.getLogger(CLASS);
    
        // FIXME: these should be dynamically taken from JavaBeanAspect
    private static final List JAVA_BEAN_ASPECT_PROPERTIES = 
            Arrays.asList("callback", "callbacks", "class", "propertyChangeListeners", "vetoableChangeListeners");

    private final Bean source;
    
    private final Bean target;
    
    private final boolean forceEDT;
    
    /** My name, useful only for logging. */
    private final String name;

    /***************************************************************************
     *
     *
     **************************************************************************/
    public Syncer (final String name, final Bean source, final Bean target, final boolean copy, final boolean forceEDT)
      {
        this.name = name;
        this.target = target;
        this.source = source;
        this.forceEDT = forceEDT;
        
        if (copy)
          {
            int count = 0;
            final long time = System.currentTimeMillis();
            final PropertyDescriptor[] descriptors = findDescriptors(target.getClass());

            for (final PropertyDescriptor descriptor : descriptors)
              {
                count++;
                final String propertyName = descriptor.getName();

                if (!JAVA_BEAN_ASPECT_PROPERTIES.contains(propertyName) && !propertyName.endsWith("Available"))
                  {
                    final Property property = FastBeanProperty.create(propertyName);
                    final Property availableProperty = FastBeanProperty.create(propertyName + "Available");

                    if (availableProperty.isReadable(source))
                      {
                        final boolean available = availableProperty.getValue(source);
                        availableProperty.setValue(target, available);  

                        if (available)
                          {
                            Object value = property.getValue(source);
                            
                            if (value instanceof Date)
                              {
                                value = new Date(((Date)value).getTime());
                              }

                            logger.fine(">>>> %s: setting initial value %s.%s = %s", name,target, propertyName, value);
                            property.setValue(target, value);
                          }
                      }
                  }
              }

            logger.info(">>>> %s: synced %d properties in %d msec", name, count, System.currentTimeMillis() - time);
          }
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    public void bind()
      {
        addPropertyChangeListener(source, this);    
      }

    /***************************************************************************
     *
     *
     **************************************************************************/
    public void unbind()
      {
        removePropertyChangeListener(source, this);    
      }

    /***************************************************************************
     *
     *
     **************************************************************************/
    public void propertyChange (final PropertyChangeEvent event)
      {
        final String propertyName = event.getPropertyName();
        
        try
          {
            if (!JAVA_BEAN_ASPECT_PROPERTIES.contains(propertyName))
              {
                final Object value = event.getNewValue();
                final Property property = FastBeanProperty.create(propertyName);
                
                if (property.isWriteable(target))
                  {
//                    logger.fine(">>>> syncing %s %s->%s: %s", propertyName, source, target, value));
                    
                    final Object oldValue = property.getValue(target);
                    
                    if (!equals(oldValue, value))
                      {
                        logger.fine(">>>> %s: syncing %s %s->%s: %s", name, propertyName, source, target, value);
                        
                        // TODO: EDT compliance should be instead implemented as a decorator in the bean,
                        // as form.getBean() doesn't get any protection!
                        // TODO: the above could not be a problem if you clarify that getBean() must be used
                        // for aliasing only - make it protected?
                        if (!forceEDT || SwingUtilities.isEventDispatchThread())
                          {
                            property.setValue(target, value);
                          }
                        
                        else
                          {
                            SwingUtilities.invokeAndWait(new Runnable() 
                              {
                                public void run() 
                                  {
                                    property.setValue(target, value);
                                  }
                              });  
                          }
                      }
                  }  
              }
          }
        catch (RuntimeException e)
          {
            throw e;
          }
        catch (Exception e)
          {
            logger.throwing(CLASS, name + ": failed syncing " + propertyName, e);
          }
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    private static void addPropertyChangeListener (final Object bean, final PropertyChangeListener listener)
      {
        try
          {
            final Method method = bean.getClass().getMethod("addPropertyChangeListener", PropertyChangeListener.class);
            method.invoke(bean, listener);
          }
        catch (RuntimeException e)
          {
            throw e;
          }
        catch (Exception e)
          {
            throw new RuntimeException(e);
          }
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    private static void removePropertyChangeListener (final Object bean, final PropertyChangeListener listener)
      {
        try
          {
            final Method method = bean.getClass().getMethod("removePropertyChangeListener", PropertyChangeListener.class);
            method.invoke(bean, listener);
          }
        catch (RuntimeException e)
          {
            throw e;
          }
        catch (Exception e)
          {
            throw new RuntimeException(e);
          }
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    private static PropertyDescriptor[] findDescriptors (final Class clazz) 
      {
        try 
          {
            return Introspector.getBeanInfo(clazz).getPropertyDescriptors();
          }
        catch (IntrospectionException e) 
          {
            throw new RuntimeException(e);
          }
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    private static boolean equals (final Object o1, final Object o2)
      {
        if (o1 == null)
          {
            return o2 == null;
          }
        
        //FIXME: handle arrays
        return o1.equals(o2);
      }
  }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy