/* $Id: UMLModelElementListModel2.java 18588 2010-07-28 21:30:25Z bobtarling $
*****************************************************************************
* Copyright (c) 2009 Contributors - see below
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* mvw
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 2002-2008 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.uml.ui;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.swing.DefaultListModel;
import javax.swing.JPopupMenu;
import org.apache.log4j.Logger;
import org.argouml.model.AddAssociationEvent;
import org.argouml.model.AssociationChangeEvent;
import org.argouml.model.AttributeChangeEvent;
import org.argouml.model.InvalidElementException;
import org.argouml.model.Model;
import org.argouml.model.RemoveAssociationEvent;
import org.argouml.ui.targetmanager.TargetEvent;
import org.argouml.ui.targetmanager.TargetListener;
import org.tigris.gef.base.Diagram;
import org.tigris.gef.presentation.Fig;
/**
* The model for a list that contains ModelElements. The state of the Element is
* still kept in the model subsystem itself. This list is only to be used as the
* model for some GUI element like UMLLinkedList.
*
* @since Oct 2, 2002
* @author jaap.branderhorst@xs4all.nl
* @deprecated in 0.31.2 by Bob Tarling This is replaced by the XML property
* panels module
*/
@Deprecated
public abstract class UMLModelElementListModel2 extends DefaultListModel
implements TargetListener, PropertyChangeListener {
private static final Logger LOG =
Logger.getLogger(UMLModelElementListModel2.class);
private String eventName = null;
private Object listTarget = null;
/**
* Flag to indicate wether list events should be fired
*/
private boolean fireListEvents = true;
/**
* Flag to indicate wether the model is being build
*/
private boolean buildingModel = false;
/**
* The type of model elements this list model is designed to hold.
*/
private Object metaType;
/**
* Indicates that drops onto this list should connect in the opposite
* way to standard.
*/
private boolean reverseDropConnection;
/**
* Constructor to be used if the subclass does not depend on the
* MELementListener methods and setTarget method implemented in this
* class.
*/
public UMLModelElementListModel2() {
super();
}
/**
* Constructor for UMLModelElementListModel2.
*
* @param name the name of the event to listen to, which triggers us
* to update the list model from the UML data
*/
public UMLModelElementListModel2(String name) {
super();
eventName = name;
}
/**
* Constructor for UMLModelElementListModel2.
*
* @param name the name of the event to listen to, which triggers us
* to update the list model from the UML data
* @param theMetaType the type of model element that the list model
* is designed to contain.
*/
public UMLModelElementListModel2(String name, Object theMetaType) {
super();
this.metaType = theMetaType;
eventName = name;
}
/**
* Constructor for UMLModelElementListModel2.
*
* @param name the name of the event to listen to, which triggers us
* to update the list model from the UML data
* @param theMetaType the type of model element that the list model
* is designed to contain.
* @param reverseTheDropConnection tells the JList to reverse the
* connection made and drop during dnd.
*/
public UMLModelElementListModel2(
String name,
Object theMetaType,
boolean reverseTheDropConnection) {
super();
this.metaType = theMetaType;
eventName = name;
this.reverseDropConnection = reverseTheDropConnection;
}
/**
* Get the type of objects that this list model is designed to contain.
* @return metaType the meta type.
*/
public Object getMetaType() {
return metaType;
}
public boolean isReverseDropConnection() {
return reverseDropConnection;
}
/**
* @param building The buildingModel to set.
*/
protected void setBuildingModel(boolean building) {
this.buildingModel = building;
}
/**
* @param t the list target to set
*/
protected void setListTarget(Object t) {
this.listTarget = t;
}
/*
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*
* TODO: This should be reviewed to see if it can be improved with a view
* towards removing some of the overrriding methods used as workarounds for
* differences between NSUML and MDR - tfm - 20060302
*/
public void propertyChange(PropertyChangeEvent e) {
if (e instanceof AttributeChangeEvent) {
try {
if (isValidEvent(e)) {
rebuildModelList();
}
} catch (InvalidElementException iee) {
return;
}
} else if (e instanceof AddAssociationEvent) {
if (isValidEvent(e)) {
Object o = getChangedElement(e);
if (o instanceof Collection) {
ArrayList tempList = new ArrayList((Collection) o);
Iterator it = tempList.iterator();
while (it.hasNext()) {
Object o2 = it.next();
addElement(o2);
}
} else {
/* TODO: If this is an ordered list, then you have to
add in the right location! */
addElement(o);
}
}
} else if (e instanceof RemoveAssociationEvent) {
boolean valid = false;
if (!(getChangedElement(e) instanceof Collection)) {
valid = contains(getChangedElement(e));
} else {
Collection col = (Collection) getChangedElement(e);
Iterator it = col.iterator();
valid = true;
while (it.hasNext()) {
Object o = it.next();
if (!contains(o)) {
valid = false;
break;
}
}
}
if (valid) {
Object o = getChangedElement(e);
if (o instanceof Collection) {
Iterator it = ((Collection) o).iterator();
while (it.hasNext()) {
Object o3 = it.next();
removeElement(o3);
}
} else {
removeElement(o);
}
}
}
}
/**
* Delete and rebuild the model list from scratch.
*/
private void rebuildModelList() {
removeAllElements();
buildingModel = true;
try {
buildModelList();
} catch (InvalidElementException exception) {
/*
* This can throw an exception if the target has been
* deleted. We don't want to try locking the repository
* because this is called from the event delivery thread and
* could cause a deadlock. Instead catch the exception and
* leave the model empty.
*/
LOG.debug("buildModelList threw exception for target "
+ getTarget() + ": "
+ exception);
} finally {
buildingModel = false;
}
if (getSize() > 0) {
fireIntervalAdded(this, 0, getSize() - 1);
}
}
/**
* Builds the list of elements. Called from targetChanged every time the
* target of the proppanel is changed. Usually the method setAllElements is
* called with the result.
*/
protected abstract void buildModelList();
/**
* Utility method to set the elements of this list to the contents of the
* given collection.
* @param col the given collection
*/
protected void setAllElements(Collection col) {
if (!isEmpty())
removeAllElements();
addAll(col);
}
/**
* Utility method to add a collection of elements to the model
* @param col the given collection
*/
protected void addAll(Collection col) {
if (col.size() == 0) return;
Iterator it = col.iterator();
fireListEvents = false;
int intervalStart = getSize() == 0 ? 0 : getSize() - 1;
while (it.hasNext()) {
Object o = it.next();
addElement(o);
}
fireListEvents = true;
fireIntervalAdded(this, intervalStart, getSize() - 1);
}
/**
* Utility method to get the target. Sets the target if the target is null
* via the method setTarget().
* @return MModelElement
*/
public Object getTarget() {
return listTarget;
}
/**
* Utility method to get the changed element from some event e
* @param e the event
* @return Object the changed element
*/
protected Object getChangedElement(PropertyChangeEvent e) {
if (e instanceof AssociationChangeEvent) {
return ((AssociationChangeEvent) e).getChangedValue();
}
if (e instanceof AttributeChangeEvent) {
return ((AttributeChangeEvent) e).getSource();
}
return e.getNewValue();
}
/*
* @see javax.swing.DefaultListModel#contains(java.lang.Object)
*/
public boolean contains(Object elem) {
if (super.contains(elem)) {
return true;
}
if (elem instanceof Collection) {
Iterator it = ((Collection) elem).iterator();
while (it.hasNext()) {
if (!super.contains(it.next())) {
return false;
}
}
return true;
}
return false;
}
/**
* Sets the target. If the old target is a ModelElement, it also removes
* the model from the element listener list of the target. If the new target
* is instanceof ModelElement, the model is added as element listener to the
* new target. <p>
*
* This function is called when the user changes the target.
* Hence, this shall not result in any UML model changes.
* Hence, we block firing list events completely by setting
* buildingModel to true for the duration of this function. <p>
*
* This function looks a lot like the one in UMLComboBoxModel2.
*
* @param theNewTarget the new target
*/
public void setTarget(Object theNewTarget) {
theNewTarget = theNewTarget instanceof Fig
? ((Fig) theNewTarget).getOwner() : theNewTarget;
if (Model.getFacade().isAUMLElement(theNewTarget)
|| theNewTarget instanceof Diagram) {
if (Model.getFacade().isAUMLElement(listTarget)) {
Model.getPump().removeModelEventListener(this, listTarget,
eventName);
// Allow listening to other elements:
removeOtherModelEventListeners(listTarget);
}
if (Model.getFacade().isAUMLElement(theNewTarget)) {
listTarget = theNewTarget;
Model.getPump().addModelEventListener(this, listTarget,
eventName);
// Allow listening to other elements:
addOtherModelEventListeners(listTarget);
rebuildModelList();
} else {
listTarget = null;
removeAllElements();
}
}
}
/**
* This function allows subclasses to listen to more modelelements.
* The given target is guaranteed to be a UML modelelement.
*
* @param oldTarget the UML modelelement
*/
protected void removeOtherModelEventListeners(Object oldTarget) {
/* Do nothing by default. */
}
/**
* This function allows subclasses to listen to more modelelements.
* The given target is guaranteed to be a UML modelelement.
*
* @param newTarget the UML modelelement
*/
protected void addOtherModelEventListeners(Object newTarget) {
/* Do nothing by default. */
}
/**
* Returns true if the given element is valid, i.e. it may be added to the
* list of elements.
*
* @param element the element to be tested
* @return true if valid
*/
protected abstract boolean isValidElement(Object element);
/**
* Returns true if some event is valid. An event is valid if the
* element changed in the event is valid. This is determined via a
* call to isValidElement. This method can be overriden by
* subclasses if they cannot determine if it is a valid event just
* by checking the changed element.
*
* @param e the event
* @return boolean true if valid
*/
protected boolean isValidEvent(PropertyChangeEvent e) {
boolean valid = false;
if (!(getChangedElement(e) instanceof Collection)) {
// TODO: Considering all delete events to be valid like below
// is going to cause lots of unecessary work and some problems
if ((e.getNewValue() == null && e.getOldValue() != null)
// Don't test changed element if it was deleted
|| isValidElement(getChangedElement(e))) {
valid = true; // we tried to remove a value
}
} else {
Collection col = (Collection) getChangedElement(e);
Iterator it = col.iterator();
if (!col.isEmpty()) {
valid = true;
while (it.hasNext()) {
Object o = it.next();
if (!isValidElement(o)) {
valid = false;
break;
}
}
} else {
if (e.getOldValue() instanceof Collection
&& !((Collection) e.getOldValue()).isEmpty()) {
valid = true;
}
}
}
return valid;
}
/*
* @see javax.swing.DefaultListModel#addElement(java.lang.Object)
*/
public void addElement(Object obj) {
if (obj != null && !contains(obj)) {
super.addElement(obj);
}
}
/**
* Returns the eventName. This method is only here for testing goals.
* @return String
*/
String getEventName() {
return eventName;
}
/**
* Sets the eventName. The eventName is the name of the
* MElementEvent to which the list should listen. The list is
* registred with UMLModelEventPump and only gets events that have
* a name like eventName. This method should be called in the
* constructor of every subclass.
*
* @param theEventName The eventName to set
*/
protected void setEventName(String theEventName) {
eventName = theEventName;
}
/*
* @see TargetListener#targetAdded(TargetEvent)
*/
public void targetAdded(TargetEvent e) {
setTarget(e.getNewTarget());
}
/*
* @see TargetListener#targetRemoved(TargetEvent)
*/
public void targetRemoved(TargetEvent e) {
setTarget(e.getNewTarget());
}
/*
* @see TargetListener#targetSet(TargetEvent)
*/
public void targetSet(TargetEvent e) {
setTarget(e.getNewTarget());
}
/*
* @see javax.swing.AbstractListModel#fireContentsChanged(
* Object, int, int)
*/
protected void fireContentsChanged(Object source, int index0, int index1) {
if (fireListEvents && !buildingModel)
super.fireContentsChanged(source, index0, index1);
}
/*
* @see javax.swing.AbstractListModel#fireIntervalAdded(
* Object, int, int)
*/
protected void fireIntervalAdded(Object source, int index0, int index1) {
if (fireListEvents && !buildingModel)
super.fireIntervalAdded(source, index0, index1);
}
/*
* @see javax.swing.AbstractListModel#fireIntervalRemoved(
* Object, int, int)
*/
protected void fireIntervalRemoved(Object source, int index0, int index1) {
if (fireListEvents && !buildingModel)
super.fireIntervalRemoved(source, index0, index1);
}
/**
* Override this if you want a popup menu.
* See for an example UMLModelElementOrderedListModel2.
*
* @param popup the popup menu
* @param index the selected item in the list at the moment
* the mouse was clicked
* @return true if a popup menu is created, and needs to be shown
*/
public boolean buildPopup(JPopupMenu popup, int index) {
return false;
}
protected boolean hasPopup() {
return false;
}
}