/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package de.cismet.cids.utils.multibean; import org.jdesktop.observablecollections.ObservableList; import org.jdesktop.observablecollections.ObservableListListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import de.cismet.cids.dynamics.CidsBean; import de.cismet.cids.utils.CidsBeanDeepPropertyListener; /** * DOCUMENT ME! * * @author jruiz * @version $Revision$, $Date$ */ public class MultiBeanHelper implements PropertyChangeListener { //~ Static fields/initializers --------------------------------------------- private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MultiBeanHelper.class); public static final String EVENT_NAME = "refreshed"; //~ Instance fields -------------------------------------------------------- private final Collection<CidsBean> beans = new ArrayList<CidsBean>(); private boolean isLocked = false; private final HashMap<String, CidsBeanDeepPropertyListener> cidsBeanFollowerMap = new HashMap<String, CidsBeanDeepPropertyListener>(); private final HashMap<String, CidsBeanDeepPropertyListener> dummyBeanFollowerMap = new HashMap<String, CidsBeanDeepPropertyListener>(); private final Map<String, ObservableListListener> listListenerMap = new HashMap<String, ObservableListListener>(); private final Map<String, Object> valuesAllEqualsMap = new HashMap<String, Object>(); private final Collection<String> attachedProperties = new ArrayList<String>(); private final Collection<MultiBeanHelperListener> listeners = new ArrayList<MultiBeanHelperListener>(); private CidsBean dummyBean; private boolean loading = false; //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @param listener DOCUMENT ME! */ @Deprecated public void addPropertyChangeListener(final PropertyChangeListener listener) { } /** * DOCUMENT ME! * * @param listener DOCUMENT ME! */ @Deprecated public void removePropertyChangeListener(final PropertyChangeListener listener) { } /** * DOCUMENT ME! * * @param cidsBean DOCUMENT ME! */ public final void deattachCidsBeanTrigger(final CidsBean cidsBean) { for (final String property : getAttachedProperties()) { removeTriggerProperty(cidsBeanFollowerMap, property); } } /** * DOCUMENT ME! * * @param cidsBean DOCUMENT ME! */ public final void attachCidsBeanTrigger(final CidsBean cidsBean) { for (final String attachedProperty : getAttachedProperties()) { addTriggerProperty(cidsBeanFollowerMap, attachedProperty, cidsBean); } } /** * DOCUMENT ME! * * @param cidsBean DOCUMENT ME! */ public final void deattachDummyBeanTrigger(final CidsBean cidsBean) { for (final String property : getAttachedProperties()) { removeTriggerProperty(dummyBeanFollowerMap, property); } } /** * DOCUMENT ME! * * @param cidsBean DOCUMENT ME! */ public final void attachDummyBeanTrigger(final CidsBean cidsBean) { for (final String attachedProperty : getAttachedProperties()) { addTriggerProperty(dummyBeanFollowerMap, attachedProperty, cidsBean); } } /** * DOCUMENT ME! * * @param followerMap DOCUMENT ME! * @param property DOCUMENT ME! * @param cidsBean DOCUMENT ME! */ private void addTriggerProperty(final HashMap<String, CidsBeanDeepPropertyListener> followerMap, final String property, final CidsBean cidsBean) { final CidsBeanDeepPropertyListener follower = new CidsBeanDeepPropertyListener(cidsBean, property); followerMap.put(property, follower); follower.addPropertyChangeListener(this); } /** * DOCUMENT ME! * * @param followerMap DOCUMENT ME! * @param property DOCUMENT ME! */ public final void removeTriggerProperty(final HashMap<String, CidsBeanDeepPropertyListener> followerMap, final String property) { final CidsBeanDeepPropertyListener follower = followerMap.remove(property); if (follower != null) { follower.removePropertyChangeListener(this); } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public Collection<String> getAttachedProperties() { return attachedProperties; } /** * DOCUMENT ME! */ private void refillAttachedProperties() { attachedProperties.clear(); attachedProperties.addAll(createCidsBeanPropertiesPath(null, dummyBean)); } /** * DOCUMENT ME! * * @param path DOCUMENT ME! * @param cidsBean DOCUMENT ME! * * @return DOCUMENT ME! */ private static Collection<String> createCidsBeanPropertiesPath(final String path, final CidsBean cidsBean) { final Collection coll = new ArrayList(); for (final String propertyName : cidsBean.getPropertyNames()) { final String subPath = (path == null) ? propertyName : (path + "." + propertyName); if (cidsBean.getProperty(propertyName) instanceof CidsBean) { coll.addAll(createCidsBeanPropertiesPath(subPath, (CidsBean)cidsBean.getProperty(propertyName))); } else { coll.add(subPath); } } return coll; } /** * DOCUMENT ME! * * @param dummyBean DOCUMENT ME! */ public void setDummyBean(final CidsBean dummyBean) { deattachDummyBeanTrigger(dummyBean); final CidsBean old = this.dummyBean; if (old != null) { for (final String propertyName : getAttachedProperties()) { if (listListenerMap.containsKey(propertyName)) { listListenerMap.remove(propertyName); } } } this.dummyBean = dummyBean; if (dummyBean != null) { refillAttachedProperties(); for (final String propertyName : getAttachedProperties()) { final Object value = dummyBean.getProperty(propertyName); if ((value != null) && (value instanceof ObservableList)) { final ObservableListListener listener = new ObservableListListener() { @Override public void listElementsAdded(final ObservableList list, final int index, final int length) { if (list.equals(dummyBean.getProperty(propertyName))) { for (final CidsBean bean : beans) { for (int i = index; i < (index + length); ++i) { final CidsBean listElement = (CidsBean)list.get(i); bean.getBeanCollectionProperty(propertyName).add(listElement); } } } } @Override public void listElementsRemoved(final ObservableList list, final int index, final List removedList) { if (list.equals(dummyBean.getProperty(propertyName))) { for (final CidsBean bean : beans) { for (final CidsBean listElement : (Collection<CidsBean>)removedList) { bean.getBeanCollectionProperty(propertyName).remove(listElement); } } } } @Override public void listElementReplaced(final ObservableList ol, final int i, final Object o) { } @Override public void listElementPropertyChanged(final ObservableList ol, final int i) { } }; listListenerMap.put(propertyName, listener); } } attachDummyBeanTrigger(dummyBean); } } /** * DOCUMENT ME! * * @param propertyName DOCUMENT ME! * * @return DOCUMENT ME! */ private boolean fillValuesAllEqualsMap(final String propertyName) { if (valuesAllEqualsMap.containsKey(propertyName)) { valuesAllEqualsMap.remove(propertyName); } final Object propValue = dummyBean.getProperty(propertyName); if (propValue instanceof ObservableList) { return false; } else { boolean valuesAllEquals = true; boolean firstObject = true; Object value = null; for (final CidsBean bean : beans) { final boolean valuesEquals; if (bean.getProperty(propertyName) == null) { valuesEquals = value == null; } else { valuesEquals = bean.getProperty(propertyName).equals(value); } if (firstObject || valuesEquals) { value = bean.getProperty(propertyName); } else { valuesAllEquals = false; break; } firstObject = false; } if (valuesAllEquals) { valuesAllEqualsMap.put(propertyName, value); } fireAllEqualsChanged(propertyName, valuesAllEquals); return valuesAllEquals; } } /** * DOCUMENT ME! */ private void refillValuesAllEqualsMap() { fireRefillAllEqualsMapStarted(); valuesAllEqualsMap.clear(); if (dummyBean != null) { for (final String propertyName : getAttachedProperties()) { final Object value = dummyBean.getProperty(propertyName); if (value instanceof ObservableList) { final ObservableList dummyList = (ObservableList)value; for (final CidsBean bean : beans) { dummyList.addAll(bean.getBeanCollectionProperty(propertyName)); } dummyList.addObservableListListener(listListenerMap.get(propertyName)); } fillValuesAllEqualsMap(propertyName); } } fireRefillAllEqualsMapDone(); } /** * DOCUMENT ME! * * @param propertyName DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean isValuesAllEquals(final String propertyName) { return valuesAllEqualsMap.containsKey(propertyName); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public Collection<CidsBean> getBeans() { return new ArrayList<CidsBean>(beans); } /** * DOCUMENT ME! * * @param beans DOCUMENT ME! */ public void setBeans(final Collection<CidsBean> beans) { for (final CidsBean bean : this.beans) { deattachCidsBeanTrigger(bean); } this.beans.clear(); clearDummy(); if (beans != null) { this.beans.addAll(beans); } refillValuesAllEqualsMap(); // set values that are equals for (final String propertyName : valuesAllEqualsMap.keySet()) { try { dummyBean.setProperty(propertyName, valuesAllEqualsMap.get(propertyName)); } catch (final Exception ex) { LOG.error(ex, ex); } } for (final CidsBean bean : beans) { attachCidsBeanTrigger(bean); } } /** * DOCUMENT ME! */ private void clearDummy() { for (final String propertyName : getAttachedProperties()) { if (!(dummyBean.getProperty(propertyName) instanceof ObservableList)) { try { dummyBean.setProperty(propertyName, null); } catch (final Exception ex) { LOG.error("error while setting property on dummybean", ex); } } } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public CidsBean getDummyBean() { return dummyBean; } @Override public void propertyChange(final PropertyChangeEvent evt) { if ((evt.getSource() instanceof CidsBeanDeepPropertyListener) && cidsBeanFollowerMap.containsKey(evt.getPropertyName())) { final CidsBeanDeepPropertyListener follower = (CidsBeanDeepPropertyListener)evt.getSource(); if (follower.getBean().equals(dummyBean)) { if (!isLocked) { try { isLocked = true; for (final CidsBean bean : beans) { try { final String propertyName = evt.getPropertyName(); final Object value = evt.getNewValue(); if (!(value instanceof ObservableList)) { bean.setProperty(propertyName, value); fireAllEqualsChanged(propertyName, true); } } catch (final Exception ex) { LOG.error("error while setting property on collection bean", ex); } } } finally { isLocked = false; } } } else if (beans.contains(follower.getBean())) { if (!isLocked) { try { isLocked = true; final String propertyName = evt.getPropertyName(); fillValuesAllEqualsMap(propertyName); } catch (final Exception ex) { LOG.error("error while setting property on dummybean", ex); } finally { isLocked = false; } } } } } /** * DOCUMENT ME! * * @param listener DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean addListener(final MultiBeanHelperListener listener) { return listeners.add(listener); } /** * DOCUMENT ME! * * @param listener DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean removeListener(final MultiBeanHelperListener listener) { return listeners.remove(listener); } /** * DOCUMENT ME! */ public void fireRefillAllEqualsMapStarted() { setLoading(true); for (final MultiBeanHelperListener listener : listeners) { listener.refillAllEqualsMapStarted(); } } /** * DOCUMENT ME! */ public void fireRefillAllEqualsMapDone() { setLoading(false); for (final MultiBeanHelperListener listener : listeners) { listener.refillAllEqualsMapDone(); } } /** * DOCUMENT ME! * * @param propertyName event DOCUMENT ME! * @param allEquals DOCUMENT ME! */ public void fireAllEqualsChanged(final String propertyName, final boolean allEquals) { if (!isLoading()) { for (final MultiBeanHelperListener listener : listeners) { listener.allEqualsChanged(propertyName, allEquals); } } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean isLoading() { return loading; } /** * DOCUMENT ME! * * @param loading DOCUMENT ME! */ private void setLoading(final boolean loading) { this.loading = loading; } }