org.rifidi.designer.entities.databinding.ObservableTreeContentProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.rifidi.designer.entities.databinding.ObservableTreeContentProvider.java

Source

/*
 *  ObservableTreeContentProvider.java
 *
 *  Project:      RiFidi Designer - A Virtualization tool for 3D RFID environments
 *  http://www.rifidi.org
 *  http://rifidi.sourceforge.net
 *  Copyright:       Pramari LLC and the Rifidi Project
 *  License:      Lesser GNU Public License (LGPL)
 *  http://www.opensource.org/licenses/lgpl-license.html
 */
package org.rifidi.designer.entities.databinding;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeListenerProxy;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.databinding.observable.list.IListChangeListener;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.rifidi.designer.entities.Entity;
import org.rifidi.designer.entities.SceneData;
import org.rifidi.designer.entities.databinding.annotations.MonitorThisList;
import org.rifidi.designer.entities.databinding.annotations.MonitoredProperties;

/**
 * This TreeContentProvider takes advantage of eclipse databinding. An object
 * that is given to this contentprovider can use annotations to tell the
 * contentprovider which properties/collections to monitor for changes.
 * 
 * @see MonitoredProperties
 * @see MonitorThisList
 * @author Jochen Mader Jan 17, 2008
 * 
 */

public class ObservableTreeContentProvider implements ITreeContentProvider, PropertyChangeListener {
    /**
     * Logger for this class.
     */
    private static Log logger = LogFactory.getLog(ObservableTreeContentProvider.class);
    /**
     * The viewer for the entites.
     */
    private AbstractTreeViewer viewer;
    /**
     * A map containing all already created adapters.
     */
    private Map<Object, Object> adapterCache;
    /**
     * A map containg all observed elements and their ObserverHelper.
     */
    private Map<Object, ObserverHelper> observedElements;
    /**
     * A map containing the bean as key and another map containing name and
     * listener of its monitored properties.
     */
    private Map<Object, Map<String, PropertyChangeListenerProxy>> monitoredProperties;

    /**
     * Constructor.
     */
    public ObservableTreeContentProvider() {
        adapterCache = new HashMap<Object, Object>();
        observedElements = new HashMap<Object, ObserverHelper>();
        monitoredProperties = new HashMap<Object, Map<String, PropertyChangeListenerProxy>>();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
     */
    public Object[] getChildren(final Object parentElement) {
        monitorElement(parentElement);
        return getAdapter(parentElement).getChildren(parentElement);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
     */
    public Object getParent(final Object element) {
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
     */
    public boolean hasChildren(final Object element) {
        if (getAdapter(element) != null) {
            monitorElement(element);
            if (getAdapter(element).getChildren(element).length > 0) {
                return true;
            }
        }
        return false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
     */
    public Object[] getElements(final Object inputElement) {
        monitorElement(inputElement);
        return getAdapter(inputElement).getChildren(inputElement);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.IContentProvider#dispose()
     */
    public void dispose() {
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
     *      java.lang.Object, java.lang.Object)
     */
    public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
        if (this.viewer != viewer) {
            if (viewer instanceof AbstractTreeViewer) {
                this.viewer = (AbstractTreeViewer) viewer;
            } else {
                throw new RuntimeException(
                        "ObservableTreeContentProvider supports AbstractTreeViewer but got " + viewer.getClass());
            }
        }
        if (oldInput != newInput) {
            adapterCache = new HashMap<Object, Object>();
            observedElements = new HashMap<Object, ObserverHelper>();
            monitoredProperties = new HashMap<Object, Map<String, PropertyChangeListenerProxy>>();
            viewer.refresh();
        }
    }

    /**
     * Helper method to ensure that a given Object is being monitored.
     * 
     * @param element
     */
    @SuppressWarnings("unchecked")
    private void monitorElement(final Object element) {
        if (!observedElements.containsKey(element)) {
            if (element.getClass().isAnnotationPresent(MonitorThisList.class)) {
                String name = ((MonitorThisList) (element.getClass().getAnnotation(MonitorThisList.class))).name();
                name = name.substring(0, 1).toUpperCase() + name.substring(1);
                try {
                    observedElements.put(element, new ObserverHelper(element));
                    ((IObservableList) element.getClass().getMethod("get" + name, null).invoke(element))
                            .addListChangeListener(observedElements.get(element));
                } catch (Exception e) {
                    logger.error(e);
                }
            }
            if (element.getClass().isAnnotationPresent(MonitoredProperties.class)
                    && !monitoredProperties.containsKey(element)) {
                Map<String, PropertyChangeListenerProxy> props = new HashMap<String, PropertyChangeListenerProxy>();
                monitoredProperties.put(element, props);
                for (String name : element.getClass().getAnnotation(MonitoredProperties.class).names()) {
                    PropertyChangeListenerProxy proxy = new PropertyChangeListenerProxy(name, this);
                    ((Entity) element).addPropertyChangeListener(proxy);
                    props.put(name, proxy);
                }
            }
        }
    }

    /**
     * Helper method for retrieving the adapter for an object.
     * 
     * @param adaptable
     * @return
     */
    private IWorkbenchAdapter getAdapter(Object adaptable) {
        if (adapterCache.containsKey(adaptable)) {
            return (IWorkbenchAdapter) adapterCache.get(adaptable);
        }
        IWorkbenchAdapter adapter;
        if (adaptable instanceof IAdaptable) {
            adapter = (IWorkbenchAdapter) ((IAdaptable) adaptable).getAdapter(IWorkbenchAdapter.class);
        } else {
            adapter = (IWorkbenchAdapter) Platform.getAdapterManager().getAdapter(adaptable,
                    IWorkbenchAdapter.class);
        }
        adapterCache.put(adaptable, adapter);
        return adapter;
    }

    /**
     * Helper class to monitor changes to writable lists.
     * 
     * 
     * @author Jochen Mader Jan 31, 2008
     * @tags
     * 
     */
    private class ObserverHelper implements IListChangeListener {

        // the element the list is a part of
        private Object element;

        /**
         * Constructor.
         * 
         * @param element
         */
        @SuppressWarnings("unchecked")
        public ObserverHelper(final Object element) {
            this.element = element;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.core.databinding.observable.list.IListChangeListener#handleListChange(org.eclipse.core.databinding.observable.list.ListChangeEvent)
         */
        public void handleListChange(final ListChangeEvent event) {
            if (element instanceof SceneData) {
                for (ListDiffEntry diff : event.diff.getDifferences()) {
                    if (diff.isAddition()) {
                        viewer.add(((SceneData) element).getGroupedComponentsContainer(), diff.getElement());
                    } else {
                        viewer.remove(((SceneData) element).getGroupedComponentsContainer(),
                                new Object[] { diff.getElement() });
                    }
                }
                return;
            }
            for (ListDiffEntry diff : event.diff.getDifferences()) {
                if (diff.isAddition()) {
                    viewer.add(element, diff.getElement());
                } else {
                    viewer.remove(element, new Object[] { diff.getElement() });
                }
            }
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
     */
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getOldValue() != null) {
            viewer.remove(evt.getSource(), new Object[] { evt.getOldValue() });
        }
        viewer.add(evt.getSource(), evt.getNewValue());
        viewer.refresh(evt.getSource());
    }

}