fix #7665 Use iterator to avoid ConcurrentModificationException
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / ui / element / AbstractFormSection.java
index 783fa240792008cb966ccd2d2bfee1e664a5a6c5..b816fe223241debc93b17d10e1297433b5f40201 100644 (file)
@@ -1,10 +1,11 @@
 /**
- * 
+ *
  */
 package eu.etaxonomy.taxeditor.ui.element;
 
 import java.util.ConcurrentModificationException;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -25,7 +26,8 @@ import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.TypedListener;
 import org.eclipse.swt.widgets.Widget;
-import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.forms.events.ExpansionEvent;
+import org.eclipse.ui.forms.events.IExpansionListener;
 import org.eclipse.ui.forms.widgets.Section;
 import org.eclipse.ui.forms.widgets.TableWrapLayout;
 import org.eclipse.ui.forms.widgets.ToggleHyperlink;
@@ -33,27 +35,35 @@ import org.eclipse.ui.forms.widgets.ToggleHyperlink;
 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
-import eu.etaxonomy.taxeditor.store.StoreUtil;
+import eu.etaxonomy.taxeditor.model.AbstractUtility;
+import eu.etaxonomy.taxeditor.model.MessagingUtils;
+import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
 
 /**
  * <p>
- * Abstract AbstractFormSection class.
+ * Abstract super class for a {@link Section} GUI element that visualizes a CDM
+ * entity, manages a conversation and listens to selections
  * </p>
- * 
+ *
+ * @param <ENTITY> A CDM entity which should be visualized by this section.
+ *
  * @author n.hoffmann
  * @created Feb 22, 2010
- * @version 1.0
  * @param <T>
  */
-public abstract class AbstractFormSection<ENTITY> extends Section implements
-               ISelectionChangedListener, ICdmFormElement, IEntityElement<ENTITY>,
-               IConversationEnabled {
+//TODO shouldn't ENTITY be bound with super class ICdmBase for example (AbstractFormSection<ENTITY extends ICdmBase>)?
+public abstract class AbstractFormSection<ENTITY> extends Section implements ISelectionChangedListener, IEntityElement<ENTITY>, IConversationEnabled {
+
+    /**
+     * The default number of columns in detail sections
+     */
+    public static final int DEFAULT_NUM_COLUMNS = 2;
 
        private ISelectionProvider selectionProvider;
 
        private ENTITY entity;
 
-       private Set<ICdmFormElement> elements = new HashSet<ICdmFormElement>();
+       private final Set<ICdmFormElement> elements = new HashSet<>();
 
        protected CdmFormFactory formFactory;
 
@@ -67,7 +77,7 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * Constructor for AbstractFormSection.
         * </p>
-        * 
+        *
         * @param conversation
         *            TODO
         * @param style
@@ -81,67 +91,68 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * @param <ENTITY>
         *            a ENTITY object.
         */
-       protected AbstractFormSection(CdmFormFactory formFactory, ICdmFormElement parentElement,
-                       int style) {
+    protected AbstractFormSection(CdmFormFactory formFactory, ICdmFormElement parentElement, int style) {
                super(parentElement.getLayoutComposite(), style);
 
                this.parentElement = parentElement;
 
                this.formFactory = formFactory;
 
-               this.setLayoutData(CdmFormFactory.FILL());
+               this.setLayoutData(LayoutConstants.FILL());
 
                Composite client = formFactory.createComposite(this, SWT.WRAP);
                client.setBackgroundMode(SWT.INHERIT_DEFAULT);
 
-               TableWrapLayout layout = CdmFormFactory.LAYOUT();
+               TableWrapLayout layout = LayoutConstants.LAYOUT();
                layout.bottomMargin = 10;
                layout.rightMargin = 5;
+               layout.horizontalSpacing = 5;
 
                client.setLayout(layout);
 
                this.setClient(client);
-       }
 
-       /**
-        * <p>
-        * Constructor for AbstractFormSection.
-        * </p>
-        * 
-        * @param formFactory
-        *            a {@link eu.etaxonomy.taxeditor.ui.element.CdmFormFactory}
-        *            object.
-        * @param conversation
-        *            a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
-        *            object.
-        * @param parentElement
-        *            a {@link eu.etaxonomy.taxeditor.ui.element.ICdmFormElement}
-        *            object.
-        * @param selectionProvider
-        *            a {@link org.eclipse.jface.viewers.ISelectionProvider} object.
-        * @param style
-        *            a int.
-        */
-       protected AbstractFormSection(CdmFormFactory formFactory,
-                       ICdmFormElement parentElement,
-                       ISelectionProvider selectionProvider, int style) {
-               this(formFactory, parentElement, style);
-               this.selectionProvider = selectionProvider;
        }
 
+           /**
+     * <p>
+     * Constructor for AbstractFormSection.
+     * </p>
+     *
+     * @param formFactory
+     *            a {@link eu.etaxonomy.taxeditor.ui.element.CdmFormFactory}
+     *            object.
+     * @param conversation
+     *            a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
+     *            object.
+     * @param parentElement
+     *            a {@link eu.etaxonomy.taxeditor.ui.element.ICdmFormElement}
+     *            object.
+     * @param selectionProvider
+     *            a {@link org.eclipse.jface.viewers.ISelectionProvider} object.
+     * @param style
+     *            a int.
+     */
+    protected AbstractFormSection(CdmFormFactory formFactory, ICdmFormElement parentElement, ISelectionProvider selectionProvider, int style) {
+        this(formFactory, parentElement, style);
+        this.selectionProvider = selectionProvider;
+    }
+
        /**
         * <p>
         * Getter for the field <code>propertyChangeListeners</code>.
         * </p>
-        * 
+        *
         * @return a {@link java.util.Set} object.
         */
-       public List<IPropertyChangeListener> getPropertyChangeListeners() {
+       @Override
+    public List<IPropertyChangeListener> getPropertyChangeListeners() {
                return propertyChangeListeners;
        }
 
        /** {@inheritDoc} */
-       public void setPropertyChangeListeners(
+       @Override
+    public void setPropertyChangeListeners(
                        List<IPropertyChangeListener> propertyChangeListeners) {
                this.propertyChangeListeners = propertyChangeListeners;
        }
@@ -150,27 +161,24 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * Setter for the field <code>entity</code>.
         * </p>
-        * 
+        *
         * @param entity
         *            a ENTITY object.
         */
        public void setEntity(ENTITY entity) {
                this.entity = entity;
+               addExpandListener();
        }
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see eu.etaxonomy.taxeditor.forms.IEntityElement#getEntity()
-        */
        /**
         * <p>
         * Getter for the field <code>entity</code>.
         * </p>
-        * 
+        *
         * @return a ENTITY object.
         */
-       public ENTITY getEntity() {
+       @Override
+    public ENTITY getEntity() {
                return entity;
        }
 
@@ -178,7 +186,7 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * getToggle
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.ui.forms.widgets.ToggleHyperlink} object.
         */
        public ToggleHyperlink getToggle() {
@@ -189,7 +197,7 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * getSection
         * </p>
-        * 
+        *
         * @return a {@link eu.etaxonomy.taxeditor.ui.element.AbstractFormSection}
         *         object.
         */
@@ -199,29 +207,28 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
 
        /*
         * (non-Javadoc)
-        * 
+        *
         * @see
         * eu.etaxonomy.taxeditor.forms.IPropertyChangeEmitter#firePropertyChangeEvent
         * ()
         */
        /** {@inheritDoc} */
-       public void firePropertyChangeEvent(CdmPropertyChangeEvent event) {
-               Assert.isNotNull(propertyChangeListeners,
-                               "No property change listeners.");
-               try {
-                       for (Object listener : propertyChangeListeners) {
-                               ((IPropertyChangeListener) listener).propertyChange(event);
-                       }
-               } catch (ConcurrentModificationException e) {
-                       StoreUtil.warn(getClass(),
-                                       "ConcurrentModificationException while handling PropertyChangeEvents."
-                                                       + " It seems like this is not critical");
-               }
+       @Override
+    public void firePropertyChangeEvent(CdmPropertyChangeEvent event) {
+        Assert.isNotNull(propertyChangeListeners, "No property change listeners.");
+        try {
+            for (Object listener : propertyChangeListeners) {
+                ((IPropertyChangeListener) listener).propertyChange(event);
+            }
+        } catch (ConcurrentModificationException e) {
+            MessagingUtils.warn(getClass(), "ConcurrentModificationException while handling PropertyChangeEvents."
+                    + " It seems like this is not critical");
+        }
        }
 
        /**
         * Fires a {@link CdmPropertyChangeEvent} with the given object as source.
-        * 
+        *
         * @param object
         *            the object on which the property changed
         */
@@ -232,7 +239,7 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
        /**
         * Fires a {@link CdmPropertyChangeEvent} with the given object as source
         * also containing the originating event
-        * 
+        *
         * @param object
         *            the object on which the property changed
         * @param originatingEvent
@@ -246,7 +253,7 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
 
        /*
         * (non-Javadoc)
-        * 
+        *
         * @see org.eclipse.swt.widgets.Composite#setFocus()
         */
        /** {@inheritDoc} */
@@ -257,19 +264,20 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
 
        /*
         * (non-Javadoc)
-        * 
+        *
         * @see
         * org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse
         * .jface.util.PropertyChangeEvent)
         */
        /** {@inheritDoc} */
-       public void propertyChange(PropertyChangeEvent event) {
+       @Override
+    public void propertyChange(PropertyChangeEvent event) {
                firePropertyChangeEvent(new CdmPropertyChangeEvent(this, event));
        }
 
        /*
         * (non-Javadoc)
-        * 
+        *
         * @see
         * org.eclipse.swt.widgets.Control#setBackground(org.eclipse.swt.graphics
         * .Color)
@@ -283,42 +291,41 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
                getLayoutComposite().setBackground(color);
                super.setBackground(color);
        }
-       
+
        @Override
        public void setPersistentBackground(Color color) {
                persistentBackgroundColor = color;
                setBackground(color);
        }
-       
+
        @Override
        public Color getPersistentBackground() {
                return persistentBackgroundColor;
        }
-       
+
 
        /**
         * <p>
         * widgetSelected
         * </p>
-        * 
+        *
         * @param e
         *            a {@link org.eclipse.swt.events.SelectionEvent} object.
         */
        public void widgetSelected(SelectionEvent e) {
-               Widget widget = e.widget;
-
-               if (widget instanceof Control) {
-                       Control control = (Control) widget;
-                       if (checkControlAncestryForWidget(control)) {
-                               if (getEntity() != null) {
-                                       IStructuredSelection selection = new StructuredSelection(
-                                                       getEntity());
-                                       if (selectionProvider != null) {
-                                               selectionProvider.setSelection(selection);
-                                       }
-                               }
-                       }
-               }
+        Widget widget = e.widget;
+
+        if (widget instanceof Control) {
+            Control control = (Control) widget;
+            if (checkControlAncestryForWidget(control)) {
+                if (getEntity() != null) {
+                    IStructuredSelection selection = new StructuredSelection(getEntity());
+                    if (selectionProvider != null) {
+                        selectionProvider.setSelection(selection);
+                    }
+                }
+            }
+        }
        }
 
        private boolean checkControlAncestryForWidget(Control control) {
@@ -335,7 +342,8 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
        }
 
        /** {@inheritDoc} */
-       public void setSelected(boolean selected) {
+       @Override
+    public void setSelected(boolean selected) {
                if (selected) {
                        setBackground(Display.getCurrent().getSystemColor(
                                        SWT.COLOR_LIST_SELECTION));
@@ -345,7 +353,8 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
        }
 
        /** {@inheritDoc} */
-       public void selectionChanged(SelectionChangedEvent event) {
+       @Override
+    public void selectionChanged(SelectionChangedEvent event) {
                if (event.getSelection() == CdmFormFactory.EMPTY_SELECTION) {
                        return;
                }
@@ -365,7 +374,7 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * addSelectionListener
         * </p>
-        * 
+        *
         * @param listener
         *            a {@link org.eclipse.swt.events.SelectionListener} object.
         */
@@ -377,7 +386,7 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * removeSelectionListener
         * </p>
-        * 
+        *
         * @param listener
         *            a {@link org.eclipse.swt.events.SelectionListener} object.
         */
@@ -386,7 +395,8 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
        }
 
        /** {@inheritDoc} */
-       public void addElement(ICdmFormElement element) {
+       @Override
+    public void addElement(ICdmFormElement element) {
                elements.add(element);
        }
 
@@ -394,7 +404,7 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * removeElement
         * </p>
-        * 
+        *
         * @param element
         *            a {@link eu.etaxonomy.taxeditor.ui.element.ICdmFormElement}
         *            object.
@@ -408,16 +418,17 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * removeElements
         * </p>
         */
-       public void removeElements() {
-               for (ICdmFormElement childElement : getElements()) {
+       @Override
+    public void removeElements() {
+               for (Iterator<ICdmFormElement> formElementIterator = getElements().iterator();formElementIterator.hasNext();) {
+                   ICdmFormElement childElement = formElementIterator.next();
                        // recursion
                        childElement.removeElements();
 
                        // unregister selection arbitrator
                        if (childElement instanceof ISelectableElement) {
                                ISelectableElement selectableElement = (ISelectableElement) childElement;
-                               if (selectableElement != null
-                                               && selectableElement.getSelectionArbitrator() != null) {
+                               if (selectableElement.getSelectionArbitrator() != null) {
                                        formFactory.destroySelectionArbitrator(selectableElement
                                                        .getSelectionArbitrator());
                                }
@@ -427,7 +438,8 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
                        formFactory.removePropertyChangeListener(childElement);
 
                        // dispose of the controls
-                       for (Control control : childElement.getControls()) {
+                       for (Iterator<Control> controlIterator = childElement.getControls().iterator();controlIterator.hasNext();) {
+                           Control control = controlIterator.next();
                                // we added the layoutComposite of the parental element as the
                                // layout composite to this formElement
                                // but we do not want to destroy it.
@@ -447,10 +459,11 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * Getter for the field <code>parentElement</code>.
         * </p>
-        * 
+        *
         * @return a {@link eu.etaxonomy.taxeditor.ui.element.ICdmFormElement} object.
         */
-       public ICdmFormElement getParentElement() {
+       @Override
+    public ICdmFormElement getParentElement() {
                return parentElement;
        }
 
@@ -458,10 +471,11 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * Getter for the field <code>elements</code>.
         * </p>
-        * 
+        *
         * @return a {@link java.util.Set} object.
         */
-       public Set<ICdmFormElement> getElements() {
+       @Override
+    public Set<ICdmFormElement> getElements() {
                return elements;
        }
 
@@ -469,11 +483,12 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * getControls
         * </p>
-        * 
+        *
         * @return a {@link java.util.Set} object.
         */
-       public Set<Control> getControls() {
-               Set<Control> controls = new HashSet<Control>();
+       @Override
+    public Set<Control> getControls() {
+               Set<Control> controls = new HashSet<>();
 
                for (Control control : getChildren()) {
                        controls.add(control);
@@ -493,15 +508,17 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * getLayoutComposite
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.swt.widgets.Composite} object.
         */
-       public Composite getLayoutComposite() {
+       @Override
+    public Composite getLayoutComposite() {
                return (Composite) getClient();
        }
 
        /** {@inheritDoc} */
-       public boolean containsFormElement(ICdmFormElement formElement) {
+       @Override
+    public boolean containsFormElement(ICdmFormElement formElement) {
                if (formElement == this) {
                        return true;
                } else {
@@ -519,24 +536,21 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * Getter for the field <code>formFactory</code>.
         * </p>
-        * 
+        *
         * @return a {@link eu.etaxonomy.taxeditor.ui.element.CdmFormFactory} object.
         */
-       public CdmFormFactory getFormFactory() {
+       @Override
+    public CdmFormFactory getFormFactory() {
                return formFactory;
        }
 
        /*
         * (non-Javadoc)
-        * 
+        *
         * @see eu.etaxonomy.taxeditor.forms.ICdmFormElement#refresh()
         */
-       /**
-        * <p>
-        * refresh
-        * </p>
-        */
-       public void refresh() {
+       @Override
+    public void refresh() {
                // empty default implementation
 
        }
@@ -545,30 +559,59 @@ public abstract class AbstractFormSection<ENTITY> extends Section implements
         * <p>
         * getConversationHolder
         * </p>
-        * 
+        *
         * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
         *         object.
         */
-       public ConversationHolder getConversationHolder() {
-               if(getParentElement() instanceof RootElement || getParentElement() == null){
-               
-                       IEditorPart activeEditor = StoreUtil.getActiveEditor();
-                       if(activeEditor instanceof IConversationEnabled){
-                               ConversationHolder conversation = ((IConversationEnabled) StoreUtil.getActiveEditor()).getConversationHolder();
-                               return conversation;
-                       }
-               }else if(getParentElement() instanceof IConversationEnabled){
-                       return ((IConversationEnabled) getParentElement()).getConversationHolder();
-               }
-               StoreUtil.errorDialog("Could not get conversation for AbstractFormSection", 
-                               getClass(), "There is an error in the implementation. There should have been an active editor but it wasn't", 
+       @Override
+    public ConversationHolder getConversationHolder() {
+           if(AbstractUtility.getActivePart() instanceof IConversationEnabled){
+            return ((IConversationEnabled) AbstractUtility.getActivePart()).getConversationHolder();
+        }
+           if(getParentElement() instanceof RootElement || getParentElement() == null){
+
+               Object activeEditor = AbstractUtility.getActiveEditor();
+               if(activeEditor instanceof IConversationEnabled){
+                   ConversationHolder conversation = ((IConversationEnabled) AbstractUtility.getActiveEditor()).getConversationHolder();
+                   return conversation;
+               }
+           }else if(getParentElement() instanceof IConversationEnabled){
+               return ((IConversationEnabled) getParentElement()).getConversationHolder();
+           }
+               MessagingUtils.error(getClass(), "Could not get conversation for AbstractFormSection. There is an error in the implementation. There should have been an active editor but it wasn't",
                                new IllegalArgumentException());
                return null;
-       
+
        }
 
        /** {@inheritDoc} */
-       public void update(CdmDataChangeMap changeEvents) {
+       @Override
+    public void update(CdmDataChangeMap changeEvents) {
+
+       }
+
+    public class ExpandListener implements IExpansionListener{
+               @Override
+               public void expansionStateChanging(ExpansionEvent e) {
+               }
+               @Override
+               public void expansionStateChanged(ExpansionEvent e) {
+                       PreferencesUtil.getPreferenceStore().setValue(getPrefKey(), e.getState());
+               }
+    }
+
+    /**
+     * Adds a custom implementation of IExpansionListener to this section
+     * which stores the expansion state in the preferences
+     */
+       protected void addExpandListener() {
+               PreferencesUtil.getPreferenceStore().setDefault(getPrefKey(), isExpanded());
+               setExpanded(PreferencesUtil.getPreferenceStore().getBoolean(getPrefKey()));
+               addExpansionListener(new ExpandListener());
+       }
+
+       private String getPrefKey() {
+               return this.getClass().getCanonicalName()+";"+entity.getClass().getCanonicalName();
        }