Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
UMLModelElementListModel2 |
|
| 2.71875;2.719 |
1 | /* $Id: UMLModelElementListModel2.java 18588 2010-07-28 21:30:25Z bobtarling $ | |
2 | ***************************************************************************** | |
3 | * Copyright (c) 2009 Contributors - see below | |
4 | * All rights reserved. This program and the accompanying materials | |
5 | * are made available under the terms of the Eclipse Public License v1.0 | |
6 | * which accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: | |
10 | * mvw | |
11 | ***************************************************************************** | |
12 | * | |
13 | * Some portions of this file was previously release using the BSD License: | |
14 | */ | |
15 | ||
16 | // Copyright (c) 2002-2008 The Regents of the University of California. All | |
17 | // Rights Reserved. Permission to use, copy, modify, and distribute this | |
18 | // software and its documentation without fee, and without a written | |
19 | // agreement is hereby granted, provided that the above copyright notice | |
20 | // and this paragraph appear in all copies. This software program and | |
21 | // documentation are copyrighted by The Regents of the University of | |
22 | // California. The software program and documentation are supplied "AS | |
23 | // IS", without any accompanying services from The Regents. The Regents | |
24 | // does not warrant that the operation of the program will be | |
25 | // uninterrupted or error-free. The end-user understands that the program | |
26 | // was developed for research purposes and is advised not to rely | |
27 | // exclusively on the program for any reason. IN NO EVENT SHALL THE | |
28 | // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, | |
29 | // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, | |
30 | // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF | |
31 | // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF | |
32 | // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY | |
33 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
34 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE | |
35 | // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF | |
36 | // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, | |
37 | // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
38 | ||
39 | package org.argouml.uml.ui; | |
40 | ||
41 | import java.beans.PropertyChangeEvent; | |
42 | import java.beans.PropertyChangeListener; | |
43 | import java.util.ArrayList; | |
44 | import java.util.Collection; | |
45 | import java.util.Iterator; | |
46 | ||
47 | import javax.swing.DefaultListModel; | |
48 | import javax.swing.JPopupMenu; | |
49 | ||
50 | import org.apache.log4j.Logger; | |
51 | import org.argouml.model.AddAssociationEvent; | |
52 | import org.argouml.model.AssociationChangeEvent; | |
53 | import org.argouml.model.AttributeChangeEvent; | |
54 | import org.argouml.model.InvalidElementException; | |
55 | import org.argouml.model.Model; | |
56 | import org.argouml.model.RemoveAssociationEvent; | |
57 | import org.argouml.ui.targetmanager.TargetEvent; | |
58 | import org.argouml.ui.targetmanager.TargetListener; | |
59 | import org.tigris.gef.base.Diagram; | |
60 | import org.tigris.gef.presentation.Fig; | |
61 | ||
62 | /** | |
63 | * The model for a list that contains ModelElements. The state of the Element is | |
64 | * still kept in the model subsystem itself. This list is only to be used as the | |
65 | * model for some GUI element like UMLLinkedList. | |
66 | * | |
67 | * @since Oct 2, 2002 | |
68 | * @author jaap.branderhorst@xs4all.nl | |
69 | * @deprecated in 0.31.2 by Bob Tarling This is replaced by the XML property | |
70 | * panels module | |
71 | */ | |
72 | @Deprecated | |
73 | public abstract class UMLModelElementListModel2 extends DefaultListModel | |
74 | implements TargetListener, PropertyChangeListener { | |
75 | ||
76 | 0 | private static final Logger LOG = |
77 | Logger.getLogger(UMLModelElementListModel2.class); | |
78 | ||
79 | 0 | private String eventName = null; |
80 | 0 | private Object listTarget = null; |
81 | ||
82 | /** | |
83 | * Flag to indicate wether list events should be fired | |
84 | */ | |
85 | 0 | private boolean fireListEvents = true; |
86 | ||
87 | /** | |
88 | * Flag to indicate wether the model is being build | |
89 | */ | |
90 | 0 | private boolean buildingModel = false; |
91 | ||
92 | /** | |
93 | * The type of model elements this list model is designed to hold. | |
94 | */ | |
95 | private Object metaType; | |
96 | ||
97 | /** | |
98 | * Indicates that drops onto this list should connect in the opposite | |
99 | * way to standard. | |
100 | */ | |
101 | private boolean reverseDropConnection; | |
102 | ||
103 | /** | |
104 | * Constructor to be used if the subclass does not depend on the | |
105 | * MELementListener methods and setTarget method implemented in this | |
106 | * class. | |
107 | */ | |
108 | public UMLModelElementListModel2() { | |
109 | 0 | super(); |
110 | 0 | } |
111 | ||
112 | /** | |
113 | * Constructor for UMLModelElementListModel2. | |
114 | * | |
115 | * @param name the name of the event to listen to, which triggers us | |
116 | * to update the list model from the UML data | |
117 | */ | |
118 | public UMLModelElementListModel2(String name) { | |
119 | 0 | super(); |
120 | 0 | eventName = name; |
121 | 0 | } |
122 | ||
123 | /** | |
124 | * Constructor for UMLModelElementListModel2. | |
125 | * | |
126 | * @param name the name of the event to listen to, which triggers us | |
127 | * to update the list model from the UML data | |
128 | * @param theMetaType the type of model element that the list model | |
129 | * is designed to contain. | |
130 | */ | |
131 | public UMLModelElementListModel2(String name, Object theMetaType) { | |
132 | 0 | super(); |
133 | 0 | this.metaType = theMetaType; |
134 | 0 | eventName = name; |
135 | 0 | } |
136 | ||
137 | /** | |
138 | * Constructor for UMLModelElementListModel2. | |
139 | * | |
140 | * @param name the name of the event to listen to, which triggers us | |
141 | * to update the list model from the UML data | |
142 | * @param theMetaType the type of model element that the list model | |
143 | * is designed to contain. | |
144 | * @param reverseTheDropConnection tells the JList to reverse the | |
145 | * connection made and drop during dnd. | |
146 | */ | |
147 | public UMLModelElementListModel2( | |
148 | String name, | |
149 | Object theMetaType, | |
150 | boolean reverseTheDropConnection) { | |
151 | 0 | super(); |
152 | 0 | this.metaType = theMetaType; |
153 | 0 | eventName = name; |
154 | 0 | this.reverseDropConnection = reverseTheDropConnection; |
155 | 0 | } |
156 | ||
157 | /** | |
158 | * Get the type of objects that this list model is designed to contain. | |
159 | * @return metaType the meta type. | |
160 | */ | |
161 | public Object getMetaType() { | |
162 | 0 | return metaType; |
163 | } | |
164 | ||
165 | public boolean isReverseDropConnection() { | |
166 | 0 | return reverseDropConnection; |
167 | } | |
168 | ||
169 | /** | |
170 | * @param building The buildingModel to set. | |
171 | */ | |
172 | protected void setBuildingModel(boolean building) { | |
173 | 0 | this.buildingModel = building; |
174 | 0 | } |
175 | ||
176 | /** | |
177 | * @param t the list target to set | |
178 | */ | |
179 | protected void setListTarget(Object t) { | |
180 | 0 | this.listTarget = t; |
181 | 0 | } |
182 | ||
183 | /* | |
184 | * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) | |
185 | * | |
186 | * TODO: This should be reviewed to see if it can be improved with a view | |
187 | * towards removing some of the overrriding methods used as workarounds for | |
188 | * differences between NSUML and MDR - tfm - 20060302 | |
189 | */ | |
190 | public void propertyChange(PropertyChangeEvent e) { | |
191 | 0 | if (e instanceof AttributeChangeEvent) { |
192 | try { | |
193 | 0 | if (isValidEvent(e)) { |
194 | 0 | rebuildModelList(); |
195 | } | |
196 | 0 | } catch (InvalidElementException iee) { |
197 | 0 | return; |
198 | 0 | } |
199 | 0 | } else if (e instanceof AddAssociationEvent) { |
200 | 0 | if (isValidEvent(e)) { |
201 | 0 | Object o = getChangedElement(e); |
202 | 0 | if (o instanceof Collection) { |
203 | 0 | ArrayList tempList = new ArrayList((Collection) o); |
204 | 0 | Iterator it = tempList.iterator(); |
205 | 0 | while (it.hasNext()) { |
206 | 0 | Object o2 = it.next(); |
207 | 0 | addElement(o2); |
208 | 0 | } |
209 | 0 | } else { |
210 | /* TODO: If this is an ordered list, then you have to | |
211 | add in the right location! */ | |
212 | 0 | addElement(o); |
213 | } | |
214 | 0 | } |
215 | 0 | } else if (e instanceof RemoveAssociationEvent) { |
216 | 0 | boolean valid = false; |
217 | 0 | if (!(getChangedElement(e) instanceof Collection)) { |
218 | 0 | valid = contains(getChangedElement(e)); |
219 | } else { | |
220 | 0 | Collection col = (Collection) getChangedElement(e); |
221 | 0 | Iterator it = col.iterator(); |
222 | 0 | valid = true; |
223 | 0 | while (it.hasNext()) { |
224 | 0 | Object o = it.next(); |
225 | 0 | if (!contains(o)) { |
226 | 0 | valid = false; |
227 | 0 | break; |
228 | } | |
229 | 0 | } |
230 | } | |
231 | 0 | if (valid) { |
232 | 0 | Object o = getChangedElement(e); |
233 | 0 | if (o instanceof Collection) { |
234 | 0 | Iterator it = ((Collection) o).iterator(); |
235 | 0 | while (it.hasNext()) { |
236 | 0 | Object o3 = it.next(); |
237 | 0 | removeElement(o3); |
238 | 0 | } |
239 | 0 | } else { |
240 | 0 | removeElement(o); |
241 | } | |
242 | } | |
243 | } | |
244 | 0 | } |
245 | ||
246 | /** | |
247 | * Delete and rebuild the model list from scratch. | |
248 | */ | |
249 | private void rebuildModelList() { | |
250 | 0 | removeAllElements(); |
251 | 0 | buildingModel = true; |
252 | try { | |
253 | 0 | buildModelList(); |
254 | 0 | } catch (InvalidElementException exception) { |
255 | /* | |
256 | * This can throw an exception if the target has been | |
257 | * deleted. We don't want to try locking the repository | |
258 | * because this is called from the event delivery thread and | |
259 | * could cause a deadlock. Instead catch the exception and | |
260 | * leave the model empty. | |
261 | */ | |
262 | 0 | LOG.debug("buildModelList threw exception for target " |
263 | + getTarget() + ": " | |
264 | + exception); | |
265 | } finally { | |
266 | 0 | buildingModel = false; |
267 | 0 | } |
268 | 0 | if (getSize() > 0) { |
269 | 0 | fireIntervalAdded(this, 0, getSize() - 1); |
270 | } | |
271 | 0 | } |
272 | ||
273 | /** | |
274 | * Builds the list of elements. Called from targetChanged every time the | |
275 | * target of the proppanel is changed. Usually the method setAllElements is | |
276 | * called with the result. | |
277 | */ | |
278 | protected abstract void buildModelList(); | |
279 | ||
280 | /** | |
281 | * Utility method to set the elements of this list to the contents of the | |
282 | * given collection. | |
283 | * @param col the given collection | |
284 | */ | |
285 | protected void setAllElements(Collection col) { | |
286 | 0 | if (!isEmpty()) |
287 | 0 | removeAllElements(); |
288 | 0 | addAll(col); |
289 | 0 | } |
290 | ||
291 | /** | |
292 | * Utility method to add a collection of elements to the model | |
293 | * @param col the given collection | |
294 | */ | |
295 | protected void addAll(Collection col) { | |
296 | 0 | if (col.size() == 0) return; |
297 | 0 | Iterator it = col.iterator(); |
298 | 0 | fireListEvents = false; |
299 | 0 | int intervalStart = getSize() == 0 ? 0 : getSize() - 1; |
300 | 0 | while (it.hasNext()) { |
301 | 0 | Object o = it.next(); |
302 | 0 | addElement(o); |
303 | 0 | } |
304 | 0 | fireListEvents = true; |
305 | 0 | fireIntervalAdded(this, intervalStart, getSize() - 1); |
306 | 0 | } |
307 | ||
308 | /** | |
309 | * Utility method to get the target. Sets the target if the target is null | |
310 | * via the method setTarget(). | |
311 | * @return MModelElement | |
312 | */ | |
313 | public Object getTarget() { | |
314 | 0 | return listTarget; |
315 | } | |
316 | ||
317 | /** | |
318 | * Utility method to get the changed element from some event e | |
319 | * @param e the event | |
320 | * @return Object the changed element | |
321 | */ | |
322 | protected Object getChangedElement(PropertyChangeEvent e) { | |
323 | 0 | if (e instanceof AssociationChangeEvent) { |
324 | 0 | return ((AssociationChangeEvent) e).getChangedValue(); |
325 | } | |
326 | 0 | if (e instanceof AttributeChangeEvent) { |
327 | 0 | return ((AttributeChangeEvent) e).getSource(); |
328 | } | |
329 | 0 | return e.getNewValue(); |
330 | } | |
331 | ||
332 | /* | |
333 | * @see javax.swing.DefaultListModel#contains(java.lang.Object) | |
334 | */ | |
335 | public boolean contains(Object elem) { | |
336 | 0 | if (super.contains(elem)) { |
337 | 0 | return true; |
338 | } | |
339 | 0 | if (elem instanceof Collection) { |
340 | 0 | Iterator it = ((Collection) elem).iterator(); |
341 | 0 | while (it.hasNext()) { |
342 | 0 | if (!super.contains(it.next())) { |
343 | 0 | return false; |
344 | } | |
345 | } | |
346 | 0 | return true; |
347 | } | |
348 | 0 | return false; |
349 | } | |
350 | ||
351 | /** | |
352 | * Sets the target. If the old target is a ModelElement, it also removes | |
353 | * the model from the element listener list of the target. If the new target | |
354 | * is instanceof ModelElement, the model is added as element listener to the | |
355 | * new target. <p> | |
356 | * | |
357 | * This function is called when the user changes the target. | |
358 | * Hence, this shall not result in any UML model changes. | |
359 | * Hence, we block firing list events completely by setting | |
360 | * buildingModel to true for the duration of this function. <p> | |
361 | * | |
362 | * This function looks a lot like the one in UMLComboBoxModel2. | |
363 | * | |
364 | * @param theNewTarget the new target | |
365 | */ | |
366 | public void setTarget(Object theNewTarget) { | |
367 | 0 | theNewTarget = theNewTarget instanceof Fig |
368 | ? ((Fig) theNewTarget).getOwner() : theNewTarget; | |
369 | 0 | if (Model.getFacade().isAUMLElement(theNewTarget) |
370 | || theNewTarget instanceof Diagram) { | |
371 | 0 | if (Model.getFacade().isAUMLElement(listTarget)) { |
372 | 0 | Model.getPump().removeModelEventListener(this, listTarget, |
373 | eventName); | |
374 | // Allow listening to other elements: | |
375 | 0 | removeOtherModelEventListeners(listTarget); |
376 | } | |
377 | ||
378 | 0 | if (Model.getFacade().isAUMLElement(theNewTarget)) { |
379 | 0 | listTarget = theNewTarget; |
380 | 0 | Model.getPump().addModelEventListener(this, listTarget, |
381 | eventName); | |
382 | // Allow listening to other elements: | |
383 | 0 | addOtherModelEventListeners(listTarget); |
384 | ||
385 | 0 | rebuildModelList(); |
386 | ||
387 | } else { | |
388 | 0 | listTarget = null; |
389 | 0 | removeAllElements(); |
390 | } | |
391 | } | |
392 | 0 | } |
393 | ||
394 | /** | |
395 | * This function allows subclasses to listen to more modelelements. | |
396 | * The given target is guaranteed to be a UML modelelement. | |
397 | * | |
398 | * @param oldTarget the UML modelelement | |
399 | */ | |
400 | protected void removeOtherModelEventListeners(Object oldTarget) { | |
401 | /* Do nothing by default. */ | |
402 | 0 | } |
403 | ||
404 | /** | |
405 | * This function allows subclasses to listen to more modelelements. | |
406 | * The given target is guaranteed to be a UML modelelement. | |
407 | * | |
408 | * @param newTarget the UML modelelement | |
409 | */ | |
410 | protected void addOtherModelEventListeners(Object newTarget) { | |
411 | /* Do nothing by default. */ | |
412 | 0 | } |
413 | ||
414 | /** | |
415 | * Returns true if the given element is valid, i.e. it may be added to the | |
416 | * list of elements. | |
417 | * | |
418 | * @param element the element to be tested | |
419 | * @return true if valid | |
420 | */ | |
421 | protected abstract boolean isValidElement(Object element); | |
422 | ||
423 | /** | |
424 | * Returns true if some event is valid. An event is valid if the | |
425 | * element changed in the event is valid. This is determined via a | |
426 | * call to isValidElement. This method can be overriden by | |
427 | * subclasses if they cannot determine if it is a valid event just | |
428 | * by checking the changed element. | |
429 | * | |
430 | * @param e the event | |
431 | * @return boolean true if valid | |
432 | */ | |
433 | protected boolean isValidEvent(PropertyChangeEvent e) { | |
434 | 0 | boolean valid = false; |
435 | 0 | if (!(getChangedElement(e) instanceof Collection)) { |
436 | // TODO: Considering all delete events to be valid like below | |
437 | // is going to cause lots of unecessary work and some problems | |
438 | 0 | if ((e.getNewValue() == null && e.getOldValue() != null) |
439 | // Don't test changed element if it was deleted | |
440 | || isValidElement(getChangedElement(e))) { | |
441 | 0 | valid = true; // we tried to remove a value |
442 | } | |
443 | } else { | |
444 | 0 | Collection col = (Collection) getChangedElement(e); |
445 | 0 | Iterator it = col.iterator(); |
446 | 0 | if (!col.isEmpty()) { |
447 | 0 | valid = true; |
448 | 0 | while (it.hasNext()) { |
449 | 0 | Object o = it.next(); |
450 | 0 | if (!isValidElement(o)) { |
451 | 0 | valid = false; |
452 | 0 | break; |
453 | } | |
454 | 0 | } |
455 | } else { | |
456 | 0 | if (e.getOldValue() instanceof Collection |
457 | && !((Collection) e.getOldValue()).isEmpty()) { | |
458 | 0 | valid = true; |
459 | } | |
460 | } | |
461 | } | |
462 | 0 | return valid; |
463 | } | |
464 | ||
465 | /* | |
466 | * @see javax.swing.DefaultListModel#addElement(java.lang.Object) | |
467 | */ | |
468 | public void addElement(Object obj) { | |
469 | 0 | if (obj != null && !contains(obj)) { |
470 | 0 | super.addElement(obj); |
471 | } | |
472 | 0 | } |
473 | ||
474 | /** | |
475 | * Returns the eventName. This method is only here for testing goals. | |
476 | * @return String | |
477 | */ | |
478 | String getEventName() { | |
479 | 0 | return eventName; |
480 | } | |
481 | ||
482 | /** | |
483 | * Sets the eventName. The eventName is the name of the | |
484 | * MElementEvent to which the list should listen. The list is | |
485 | * registred with UMLModelEventPump and only gets events that have | |
486 | * a name like eventName. This method should be called in the | |
487 | * constructor of every subclass. | |
488 | * | |
489 | * @param theEventName The eventName to set | |
490 | */ | |
491 | protected void setEventName(String theEventName) { | |
492 | 0 | eventName = theEventName; |
493 | 0 | } |
494 | ||
495 | /* | |
496 | * @see TargetListener#targetAdded(TargetEvent) | |
497 | */ | |
498 | public void targetAdded(TargetEvent e) { | |
499 | 0 | setTarget(e.getNewTarget()); |
500 | 0 | } |
501 | ||
502 | /* | |
503 | * @see TargetListener#targetRemoved(TargetEvent) | |
504 | */ | |
505 | public void targetRemoved(TargetEvent e) { | |
506 | 0 | setTarget(e.getNewTarget()); |
507 | 0 | } |
508 | ||
509 | /* | |
510 | * @see TargetListener#targetSet(TargetEvent) | |
511 | */ | |
512 | public void targetSet(TargetEvent e) { | |
513 | 0 | setTarget(e.getNewTarget()); |
514 | 0 | } |
515 | ||
516 | /* | |
517 | * @see javax.swing.AbstractListModel#fireContentsChanged( | |
518 | * Object, int, int) | |
519 | */ | |
520 | protected void fireContentsChanged(Object source, int index0, int index1) { | |
521 | 0 | if (fireListEvents && !buildingModel) |
522 | 0 | super.fireContentsChanged(source, index0, index1); |
523 | 0 | } |
524 | ||
525 | /* | |
526 | * @see javax.swing.AbstractListModel#fireIntervalAdded( | |
527 | * Object, int, int) | |
528 | */ | |
529 | protected void fireIntervalAdded(Object source, int index0, int index1) { | |
530 | 0 | if (fireListEvents && !buildingModel) |
531 | 0 | super.fireIntervalAdded(source, index0, index1); |
532 | 0 | } |
533 | ||
534 | /* | |
535 | * @see javax.swing.AbstractListModel#fireIntervalRemoved( | |
536 | * Object, int, int) | |
537 | */ | |
538 | protected void fireIntervalRemoved(Object source, int index0, int index1) { | |
539 | 0 | if (fireListEvents && !buildingModel) |
540 | 0 | super.fireIntervalRemoved(source, index0, index1); |
541 | 0 | } |
542 | ||
543 | /** | |
544 | * Override this if you want a popup menu. | |
545 | * See for an example UMLModelElementOrderedListModel2. | |
546 | * | |
547 | * @param popup the popup menu | |
548 | * @param index the selected item in the list at the moment | |
549 | * the mouse was clicked | |
550 | * @return true if a popup menu is created, and needs to be shown | |
551 | */ | |
552 | public boolean buildPopup(JPopupMenu popup, int index) { | |
553 | 0 | return false; |
554 | } | |
555 | ||
556 | protected boolean hasPopup() { | |
557 | 0 | return false; |
558 | } | |
559 | ||
560 | } |