// $Id: TargetManager.java 16651 2009-01-18 19:54:02Z tfmorris $
// Copyright (c) 2002-2009 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
package org.argouml.ui.targetmanager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import javax.management.ListenerNotFoundException;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import javax.swing.event.EventListenerList;
import org.apache.log4j.Logger;
import org.argouml.kernel.Project;
import org.argouml.kernel.ProjectManager;
import org.argouml.model.Model;
import org.tigris.gef.base.Diagram;
import org.tigris.gef.presentation.Fig;
/**
* The manager of the target of ArgoUML.
* The target of ArgoUML is the element currently selected by the user.
* This can either be an instance of a meta-class (an
* Interface or a Class for example) but it can also be a diagram
* or anything that is shown
* on a diagram.<p>
*
* There can be multiple targets in case
* someone selected multiple items in the explorer or on the diagram.
* This can be done by shift-clicking or Ctrl-clicking items,
* or by drawing a box on the diagram around the items to select.<p>
*
* In case multiple targets are selected, the target manager will add each
* target to the beginning of the list of targets. This way,
* the first item of the list is the last selected item.
* Most functions in ArgoUML work on all selected items.
* However, a few (intentionally) only work on one target,
* such as the properties panels.
* These functions have 2 ways of retrieving the target they should work on:
* <ul>
* <li>1. Use the functions that return one target only,
* such as getTarget(), getModelTarget(), getFigTarget().
* <li>2. Use the first item in the list returned by
* getTargets(), getModelTargets(). </ul><p>
*
* Remark: There is currently no function getFigs(),
* returning a list of selected figs.
* But you can obtain such a list from GEF. <p>
*
* The purpose of the targetmanager is to have a central spot where we
* manage the list of current targets.<p>
*
* Via an event mechanism this manager makes sure that all objects interested
* in knowing wether the selection changed are acknowledged. <p>
*
* Note in particular that null is an invalid target.<p>
*
* Thanks to the architecture of ArgoUML of Modelelements and Figs,
* one rule has been decided upon (by mvw@tigris.org):<ul>
* <li>The list of targets shall not contain any Fig that has an owner.
* Instead, the owner is enlisted.
* </ul>
*
* @author jaap.branderhorst@xs4all.nl
*/
public final class TargetManager {
/**
* The manager of the history of targets. Every time the user (or
* the program) selects a new target, this is recorded in the
* history. Via navigateBack and navigateForward, the user can
* browse through the history just like in an ordinary internet
* browser.
* @author jaap.branderhorst@xs4all.nl
*/
private final class HistoryManager implements TargetListener {
private static final int MAX_SIZE = 100;
/**
* The history with targets.
*/
private List history = new ArrayList();
/**
* Flag to indicate if the current settarget was instantiated by a
* navigateBack action.
*/
private boolean navigateBackward;
/**
* The pointer to the current target in the history.
*/
private int currentTarget = -1;
/**
* The listener to UML model changes.
* Deleted model elements are removed
* from the history list.
*/
private Remover umlListener = new HistoryRemover();
/**
* Default constructor that registrates the history manager as target
* listener with the target manager.
*
*/
private HistoryManager() {
addTargetListener(this);
}
/**
* Puts some target into the history (if needed). Updates both
* the history as the pointer to indicate the target.
* @param target The target to put into the history
*/
private void putInHistory(Object target) {
if (currentTarget > -1) {
// only targets we didn't have allready count
Object theModelTarget =
target instanceof Fig ? ((Fig) target).getOwner() : target;
Object oldTarget =
((WeakReference) history.get(currentTarget)).get();
oldTarget =
oldTarget instanceof Fig
? ((Fig) oldTarget).getOwner()
: oldTarget;
if (oldTarget == theModelTarget) {
return;
}
}
if (target != null && !navigateBackward) {
if (currentTarget + 1 == history.size()) {
umlListener.addListener(target);
history.add(new WeakReference(target));
currentTarget++;
resize();
} else {
WeakReference ref =
currentTarget > -1
? (WeakReference) history.get(currentTarget)
: null;
if (currentTarget == -1 || !ref.get().equals(target)) {
int size = history.size();
for (int i = currentTarget + 1; i < size; i++) {
umlListener.removeListener(
history.remove(currentTarget + 1));
}
history.add(new WeakReference(target));
umlListener.addListener(target);
currentTarget++;
}
}
}
}
/**
* Resizes the history if it's grown too big.
*
*/
private void resize() {
int size = history.size();
if (size > MAX_SIZE) {
int oversize = size - MAX_SIZE;
int halfsize = size / 2;
if (currentTarget > halfsize && oversize < halfsize) {
for (int i = 0; i < oversize; i++) {
umlListener.removeListener(
history.remove(0));
}
currentTarget -= oversize;
}
}
}
/**
* Navigate one target forward in history. Throws an
* illegalstateException if not possible.
*
*/
private void navigateForward() {
if (currentTarget >= history.size() - 1) {
throw new IllegalStateException(
"NavigateForward is not allowed "
+ "since the targetpointer is pointing at "
+ "the upper boundary "
+ "of the history");
}
setTarget(((WeakReference) history.get(++currentTarget)).get());
}
/**
* Navigate one step back in history. Throws an illegalstateexception if
* not possible.
*
*/
private void navigateBackward() {
if (currentTarget == 0) {
throw new IllegalStateException(
"NavigateBackward is not allowed "
+ "since the targetpointer is pointing at "
+ "the lower boundary "
+ "of the history");
}
navigateBackward = true;
// If nothing selected, go to last selected target
if (targets.size() == 0) {
setTarget(((WeakReference) history.get(currentTarget)).get());
} else {
setTarget(((WeakReference) history.get(--currentTarget)).get());
}
navigateBackward = false;
}
/**
* Checks if it's possible to navigate back.
*
* @return true if it's possible to navigate back.
*/
private boolean navigateBackPossible() {
return currentTarget > 0;
}
/**
* Checks if it's possible to navigate forward.
*
* @return true if it's possible to navigate forward
*/
private boolean navigateForwardPossible() {
return currentTarget < history.size() - 1;
}
/**
* Listener for additions of targets to the selected targets.
* On addition of targets we put them in the history.
* @see org.argouml.ui.targetmanager.TargetListener#targetAdded(
* org.argouml.ui.targetmanager.TargetEvent)
*/
public void targetAdded(TargetEvent e) {
Object[] addedTargets = e.getAddedTargets();
// we put the targets 'backwards' in the history
// since the first target in the addedTargets array is
// the first one selected.
for (int i = addedTargets.length - 1; i >= 0; i--) {
putInHistory(addedTargets[i]);
}
}
/**
* Listener for the removal of targets from the selection.
* On removal of a target from the selection we do nothing
* with respect to the history of targets.
* @see org.argouml.ui.targetmanager.TargetListener#targetRemoved(org.argouml.ui.targetmanager.TargetEvent)
*/
public void targetRemoved(TargetEvent e) {
}
/**
* Listener for the selection of a whole bunch of targets
* in one go (or just one). Puts all the new
* targets in the history starting with the 'newest' target.
* @see org.argouml.ui.targetmanager.TargetListener#targetSet(org.argouml.ui.targetmanager.TargetEvent)
*/
public void targetSet(TargetEvent e) {
Object[] newTargets = e.getNewTargets();
for (int i = newTargets.length - 1; i >= 0; i--) {
putInHistory(newTargets[i]);
}
}
/**
* Cleans the history in total.
*
*/
private void clean() {
umlListener.removeAllListeners(history);
history = new ArrayList();
currentTarget = -1;
}
private void removeHistoryTarget(Object o) {
if (o instanceof Diagram) {
Iterator it = ((Diagram) o).getEdges().iterator();
while (it.hasNext()) {
removeHistoryTarget(it.next());
}
it = ((Diagram) o).getNodes().iterator();
while (it.hasNext()) {
removeHistoryTarget(it.next());
}
}
ListIterator it = history.listIterator();
while (it.hasNext()) {
WeakReference ref = (WeakReference) it.next();
Object historyObject = ref.get();
if (Model.getFacade().isAModelElement(o)) {
historyObject =
historyObject instanceof Fig
? ((Fig) historyObject).getOwner()
: historyObject;
}
if (o == historyObject) {
if (history.indexOf(ref) <= currentTarget) {
currentTarget--;
}
it.remove();
}
// cannot break here since an object can be multiple
// times in history
}
}
}
/**
* The log4j logger to log messages to.
*/
private static final Logger LOG = Logger.getLogger(TargetManager.class);
/**
* The singleton instance.
*/
private static TargetManager instance = new TargetManager();
/**
* The targets.
*/
private List targets = new ArrayList();
/**
* Cache for the modeltarget. See getModelTarget.
*/
private Object modelTarget;
/**
* Cache for the figTarget. See getFigTarget.
*/
private Fig figTarget;
/**
* The list with targetlisteners.
*/
private EventListenerList listenerList = new EventListenerList();
/**
* The history manager of argouml. Via the historymanager browser behaviour
* is emulated.
*/
private HistoryManager historyManager = new HistoryManager();
/**
* The listener to UML model changes.
* Deleted model elements are removed
* from the target list.
*/
private Remover umlListener = new TargetRemover();
/**
* Flag to indicate that there is a setTarget method running.
*/
private boolean inTransaction = false;
/**
* Singleton retrieval method.
* @return the targetmanager
*/
public static TargetManager getInstance() {
return instance;
}
/**
* Singleton constructor should remain private.
*/
private TargetManager() {
}
/**
* Sets the targets to the single given object. If there are targets at the
* moment of calling this method, these will be removed as targets. To
* all interested targetlisteners, a TargetEvent will be fired. If the
* new target o equals the current target, no events will be fired, nor will
* the target be (re)set.
* @param o The new target, null clears all targets.
*/
public synchronized void setTarget(Object o) {
if (isInTargetTransaction()) {
return;
}
if ((targets.size() == 0 && o == null)
|| (targets.size() == 1 && targets.get(0).equals(o))) {
return;
}
startTargetTransaction();
Object[] oldTargets = targets.toArray();
umlListener.removeAllListeners(targets);
targets.clear();
if (o != null) {
Object newTarget;
if (o instanceof Diagram) { // Needed for Argo startup :-(
newTarget = o;
} else {
newTarget = getOwner(o);
}
targets.add(newTarget);
umlListener.addListener(newTarget);
}
internalOnSetTarget(TargetEvent.TARGET_SET, oldTargets);
endTargetTransaction();
}
private void internalOnSetTarget(String eventName, Object[] oldTargets) {
TargetEvent event =
new TargetEvent(this, eventName, oldTargets, targets.toArray());
if (targets.size() > 0) {
figTarget = determineFigTarget(targets.get(0));
modelTarget = determineModelTarget(targets.get(0));
} else {
figTarget = null;
modelTarget = null;
}
if (TargetEvent.TARGET_SET.equals(eventName)) {
fireTargetSet(event);
return;
} else if (TargetEvent.TARGET_ADDED.equals(eventName)) {
fireTargetAdded(event);
return;
} else if (TargetEvent.TARGET_REMOVED.equals(eventName)) {
fireTargetRemoved(event);
return;
}
LOG.error("Unknown eventName: " + eventName);
}
/**
* Returns the current primary target, the first selected object.
*
* The value will be that of the new primary target during a targetSet/
* targetAdded/targetRemoved notification, since they are just that,
* notifications that the target(s) has just changed.
*
* @return The current target, or null if no target is selected
*/
public synchronized Object getTarget() {
return targets.size() > 0 ? targets.get(0) : null;
}
/**
* Sets the given collection to the current targets. If the collection
* equals the current targets, then does nothing. When setting
* the targets, a TargetEvent will be fired to each interested listener.
* Note that the first element returned by an Iterator on targetList
* will be taken to be the primary target (see getTarget()), and that
* an event will be fired also in case that that element would not equal
* the element returned by getTarget().
* Note also that any nulls within the Collection will be ignored.
*
* @param targetsCollection The new targets list.
*/
public synchronized void setTargets(Collection targetsCollection) {
Iterator ntarg;
if (isInTargetTransaction()) {
return;
}
Collection targetsList = new ArrayList();
if (targetsCollection != null) {
targetsList.addAll(targetsCollection);
}
/* Remove duplicates and take care of getOwner()
* and remove nulls: */
List modifiedList = new ArrayList();
Iterator it = targetsList.iterator();
while (it.hasNext()) {
Object o = it.next();
o = getOwner(o);
if ((o != null) && !modifiedList.contains(o)) {
modifiedList.add(o);
}
}
targetsList = modifiedList;
Object[] oldTargets = null;
// check if there are new elements in the list
// if the old and new list are of the same size
// set the oldTargets to the correct selection
if (targetsList.size() == targets.size()) {
boolean first = true;
ntarg = targetsList.iterator();
while (ntarg.hasNext()) {
Object targ = ntarg.next();
if (targ == null) {
continue;
}
if (!targets.contains(targ)
|| (first && targ != getTarget())) {
oldTargets = targets.toArray();
break;
}
first = false;
}
} else {
oldTargets = targets.toArray();
}
if (oldTargets == null) {
return;
}
startTargetTransaction();
umlListener.removeAllListeners(targets);
targets.clear();
// implement set-like behaviour. The same element
// may not be added more then once.
ntarg = targetsList.iterator();
while (ntarg.hasNext()) {
Object targ = ntarg.next();
if (targets.contains(targ)) {
continue;
}
targets.add(targ);
umlListener.addListener(targ);
}
internalOnSetTarget(TargetEvent.TARGET_SET, oldTargets);
endTargetTransaction();
}
/**
* Adds a target to the targets list. If the target is already in
* the targets list then does nothing. Otherwise the
* target will be added and an appropriate TargetEvent will be
* fired to all interested listeners. Since null can never be a target,
* adding null will never do anything.
* @param target the target to be added.
*/
public synchronized void addTarget(Object target) {
if (target instanceof TargetListener) {
LOG.warn("addTarget method received a TargetListener, "
+ "perhaps addTargetListener was intended! - " + target);
}
if (isInTargetTransaction()) {
return;
}
Object newTarget = getOwner(target);
if (target == null
|| targets.contains(target)
|| targets.contains(newTarget)) {
return;
}
startTargetTransaction();
Object[] oldTargets = targets.toArray();
targets.add(0, newTarget);
umlListener.addListener(newTarget);
internalOnSetTarget(TargetEvent.TARGET_ADDED, oldTargets);
endTargetTransaction();
}
/**
* Removes the target from the targets list. Does nothing if the target
* does not exist in the targets list. Fires an appropriate TargetEvent to
* all interested listeners. Since null can never be a target, removing
* null will never do anything.
* @param target The target to remove.
*/
public synchronized void removeTarget(Object target) {
if (isInTargetTransaction()) {
return;
}
if (target == null /*|| !targets.contains(target)*/) {
return;
}
startTargetTransaction();
Object[] oldTargets = targets.toArray();
Collection c = getOwnerAndAllFigs(target);
// targets.remove(target);
targets.removeAll(c);
umlListener.removeAllListeners(c);
if (targets.size() != oldTargets.length) {
internalOnSetTarget(TargetEvent.TARGET_REMOVED, oldTargets);
}
endTargetTransaction();
}
private Collection getOwnerAndAllFigs(Object o) {
Collection c = new ArrayList();
c.add(o);
if (o instanceof Fig) {
if (((Fig) o).getOwner() != null) {
o = ((Fig) o).getOwner();
c.add(o);
}
}
if (!(o instanceof Fig)) {
Project p = ProjectManager.getManager().getCurrentProject();
Collection col = p.findAllPresentationsFor(o);
if (col != null && !col.isEmpty()) {
c.addAll(col);
}
}
return c;
}
/**
* @param o the object
* @return the owner of the fig, or if it didn't exist, the object itself
*/
public Object getOwner(Object o) {
if (o instanceof Fig) {
if (((Fig) o).getOwner() != null) {
o = ((Fig) o).getOwner();
}
}
return o;
}
/**
* Returns a list of all targets. Returns an empty list
* if there are no targets. If there are several targets then the first
* Object by an Iterator on the returned List or the zero'th Object
* in an array on this List is guaranteed to be the object returned
* by {@link #getSingleTarget()}.
*
* The value will be that of the new target(s) during a targetSet/
* targetAdded/targetRemoved notification, since they are just that,
* notifications that the target(s) has just changed.
*
* @return A list with all targets.
*/
public synchronized List getTargets() {
return Collections.unmodifiableList(targets);
}
/**
* If there is only one target, then it is returned.
* Otherwise null.
*
* @return the one and only target
*/
public synchronized Object getSingleTarget() {
return targets.size() == 1 ? targets.get(0) : null;
}
/**
* @return the target from the model
*/
public synchronized Collection getModelTargets() {
ArrayList t = new ArrayList();
Iterator iter = getTargets().iterator();
while (iter.hasNext()) {
t.add(determineModelTarget(iter.next()));
}
return t;
}
/**
* If there is only one model target, then it is returned.
* Otherwise null.
*
* @return the single model target
*/
public synchronized Object getSingleModelTarget() {
int i = 0;
Iterator iter = getTargets().iterator();
while (iter.hasNext()) {
if (determineModelTarget(iter.next()) != null) {
i++;
}
if (i > 1) {
break;
}
}
if (i == 1) {
return modelTarget;
}
return null;
}
/**
* Adds a listener.
* @param listener the listener to add
*/
public void addTargetListener(TargetListener listener) {
listenerList.add(TargetListener.class, listener);
}
/**
* Removes a listener.
* @param listener the listener to remove
*/
public void removeTargetListener(TargetListener listener) {
listenerList.remove(TargetListener.class, listener);
}
private void fireTargetSet(TargetEvent targetEvent) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
try {
if (listeners[i] == TargetListener.class) {
// Lazily create the event:
((TargetListener) listeners[i + 1]).targetSet(targetEvent);
}
} catch (RuntimeException e) {
LOG.error("While calling targetSet for "
+ targetEvent
+ " in "
+ listeners[i + 1]
+ " an error is thrown.",
e);
e.printStackTrace();
}
}
}
private void fireTargetAdded(TargetEvent targetEvent) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
try {
if (listeners[i] == TargetListener.class) {
// Lazily create the event:
((TargetListener) listeners[i + 1])
.targetAdded(targetEvent);
}
} catch (RuntimeException e) {
LOG.error("While calling targetAdded for "
+ targetEvent
+ " in "
+ listeners[i + 1]
+ " an error is thrown.",
e);
e.printStackTrace();
}
}
}
private void fireTargetRemoved(TargetEvent targetEvent) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
try {
if (listeners[i] == TargetListener.class) {
// Lazily create the event:
((TargetListener) listeners[i + 1])
.targetRemoved(targetEvent);
}
} catch (RuntimeException e) {
LOG.warn("While calling targetRemoved for "
+ targetEvent
+ " in "
+ listeners[i + 1]
+ " an error is thrown.",
e);
}
}
}
private void startTargetTransaction() {
inTransaction = true;
}
private boolean isInTargetTransaction() {
return inTransaction;
}
private void endTargetTransaction() {
inTransaction = false;
}
/**
* Convenience method to return the target as fig. If the current
* target (retrieved by getTarget) is either a fig itself or the
* owner of a fig this fig will be returned. Otherwise null will
* be returned.
* @return the target in it's 'fig-form'
*/
public Fig getFigTarget() {
return figTarget;
}
/**
* Calculates the most probable 'fig-form' of some target. Beware:
* The result does NOT depend on the current diagram!
*
* @param target the target to calculate the 'fig-form' for.
* @return The fig-form.
*/
private Fig determineFigTarget(Object target) {
if (!(target instanceof Fig)) {
Project p = ProjectManager.getManager().getCurrentProject();
Collection col = p.findFigsForMember(target);
if (col == null || col.isEmpty()) {
target = null;
} else {
target = col.iterator().next();
}
}
return target instanceof Fig ? (Fig) target : null;
}
/**
* Returns the target in it's 'modelform'. If the target retrieved
* by getTarget is an UMLDiagram or a UML element the target will
* be returned. If the target is a fig but owned by a modelelement
* that modelelement will be returned. Otherwise null will be
* returned.
*
* @return the target in it's 'modelform'.
*/
public Object getModelTarget() {
return modelTarget;
}
/**
* Calculates the modeltarget.
* @param target The target to calculate the modeltarget for
* @return The modeltarget
*/
private Object determineModelTarget(Object target) {
if (target instanceof Fig) {
Object owner = ((Fig) target).getOwner();
if (Model.getFacade().isAUMLElement(owner)) {
target = owner;
}
}
return target instanceof Diagram
|| Model.getFacade().isAUMLElement(target) ? target : null;
}
/**
* Navigates the target pointer one target forward. This implements together
* with navigateBackward browser like functionality.
* @throws IllegalStateException If the target pointer is at the end of the
* history.
*/
public void navigateForward() throws IllegalStateException {
historyManager.navigateForward();
LOG.debug("Navigate forward");
}
/**
* Navigates the target pointer one target backward. This
* implements together with navigateForward browser like
* functionality
* @throws IllegalStateException If the target pointer is at the
* beginning of the history.
*/
public void navigateBackward() throws IllegalStateException {
historyManager.navigateBackward();
LOG.debug("Navigate backward");
}
/**
* Checks if it's possible to navigate forward.
* @return true if it is possible to navigate forward.
*/
public boolean navigateForwardPossible() {
return historyManager.navigateForwardPossible();
}
/**
* Checks if it's possible to navigate backward.
*
* @return true if it's possible to navigate backward
*/
public boolean navigateBackPossible() {
return historyManager.navigateBackPossible();
}
/**
* Cleans the history. Needed for the JUnit tests and when instantiating a
* new project.
*/
public void cleanHistory() {
historyManager.clean();
}
/**
* @param o the object to be removed
*/
public void removeHistoryElement(Object o) {
historyManager.removeHistoryTarget(o);
}
/**
* The listener to removals of UML model elements,
* diagrams and CommentEdges.
* Deleted elements are removed
* from the target list and/or from the history.
*
* @author michiel
*/
private abstract class Remover implements PropertyChangeListener,
NotificationListener
{
protected Remover() {
// Listen for the removal of diagrams from project
ProjectManager.getManager().addPropertyChangeListener(this);
}
private void addListener(Object o) {
if (Model.getFacade().isAModelElement(o)) {
Model.getPump().addModelEventListener(this, o, "remove");
} else if (o instanceof Diagram) {
// Figs on a diagram without an owning model element
((Diagram) o).addPropertyChangeListener(this);
} else if (o instanceof NotificationEmitter) {
// CommentEdge - the owner of a FigEdgeNote
((NotificationEmitter) o).addNotificationListener(
this, null, o);
}
}
private void removeListener(Object o) {
if (Model.getFacade().isAModelElement(o)) {
Model.getPump().removeModelEventListener(this, o, "remove");
} else if (o instanceof Diagram) {
((Diagram) o).removePropertyChangeListener(this);
} else if (o instanceof NotificationEmitter) {
try {
((NotificationEmitter) o).removeNotificationListener(this);
} catch (ListenerNotFoundException e) {
LOG.error("Notification Listener for "
+ "CommentEdge not found", e);
}
}
}
private void removeAllListeners(Collection c) {
Iterator i = c.iterator();
while (i.hasNext()) {
removeListener(i.next());
}
}
/*
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent evt) {
if ("remove".equals(evt.getPropertyName())) {
remove(evt.getSource());
}
}
/*
* @see javax.management.NotificationListener#handleNotification(javax.management.Notification, java.lang.Object)
*/
public void handleNotification(Notification notification,
Object handback) {
if ("remove".equals(notification.getType())) {
remove(notification.getSource());
}
}
protected abstract void remove(Object obj);
}
private class TargetRemover extends Remover {
protected void remove(Object obj) {
removeTarget(obj);
}
}
private class HistoryRemover extends Remover {
protected void remove(Object obj) {
historyManager.removeHistoryTarget(obj);
}
}
}