Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
FigClassifierBox |
|
| 3.0869565217391304;3.087 |
1 | /* $Id: FigClassifierBox.java 17739 2010-01-09 08:39:10Z bobtarling $ | |
2 | ******************************************************************************* | |
3 | * Copyright (c) 2010 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 | * Bob Tarling | |
11 | * Michiel van der Wulp | |
12 | ******************************************************************************* | |
13 | * | |
14 | * Some portions of this file were previously release using the BSD License: | |
15 | */ | |
16 | // $Id: FigClassifierBox.java 17739 2010-01-09 08:39:10Z bobtarling $ | |
17 | // Copyright (c) 1996-2009 The Regents of the University of California. All | |
18 | // Rights Reserved. Permission to use, copy, modify, and distribute this | |
19 | // software and its documentation without fee, and without a written | |
20 | // agreement is hereby granted, provided that the above copyright notice | |
21 | // and this paragraph appear in all copies. This software program and | |
22 | // documentation are copyrighted by The Regents of the University of | |
23 | // California. The software program and documentation are supplied "AS | |
24 | // IS", without any accompanying services from The Regents. The Regents | |
25 | // does not warrant that the operation of the program will be | |
26 | // uninterrupted or error-free. The end-user understands that the program | |
27 | // was developed for research purposes and is advised not to rely | |
28 | // exclusively on the program for any reason. IN NO EVENT SHALL THE | |
29 | // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, | |
30 | // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, | |
31 | // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF | |
32 | // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF | |
33 | // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY | |
34 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
35 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE | |
36 | // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF | |
37 | // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, | |
38 | // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
39 | ||
40 | package org.argouml.uml.diagram.static_structure.ui; | |
41 | ||
42 | import java.awt.Rectangle; | |
43 | import java.awt.event.MouseEvent; | |
44 | import java.beans.PropertyChangeEvent; | |
45 | import java.util.HashSet; | |
46 | import java.util.Iterator; | |
47 | import java.util.List; | |
48 | import java.util.Set; | |
49 | import java.util.Vector; | |
50 | ||
51 | import javax.swing.Action; | |
52 | ||
53 | import org.argouml.model.AddAssociationEvent; | |
54 | import org.argouml.model.AssociationChangeEvent; | |
55 | import org.argouml.model.AttributeChangeEvent; | |
56 | import org.argouml.model.Model; | |
57 | import org.argouml.model.RemoveAssociationEvent; | |
58 | import org.argouml.model.UmlChangeEvent; | |
59 | import org.argouml.ui.ActionCreateContainedModelElement; | |
60 | import org.argouml.ui.ArgoJMenu; | |
61 | import org.argouml.uml.diagram.DiagramSettings; | |
62 | import org.argouml.uml.diagram.OperationsCompartmentContainer; | |
63 | import org.argouml.uml.diagram.ui.ActionAddNote; | |
64 | import org.argouml.uml.diagram.ui.ActionCompartmentDisplay; | |
65 | import org.argouml.uml.diagram.ui.ActionEdgesDisplay; | |
66 | import org.argouml.uml.diagram.ui.FigAttributesCompartment; | |
67 | import org.argouml.uml.diagram.ui.FigCompartment; | |
68 | import org.argouml.uml.diagram.ui.FigCompartmentBox; | |
69 | import org.argouml.uml.diagram.ui.FigOperationsCompartment; | |
70 | import org.tigris.gef.base.Editor; | |
71 | import org.tigris.gef.base.Globals; | |
72 | import org.tigris.gef.base.Selection; | |
73 | import org.tigris.gef.presentation.Fig; | |
74 | ||
75 | /** | |
76 | * Class to display graphics for any UML Classifier in a diagram.<p> | |
77 | * | |
78 | * This abstract Fig adds an Operations compartment. | |
79 | */ | |
80 | public abstract class FigClassifierBox extends FigCompartmentBox | |
81 | implements OperationsCompartmentContainer { | |
82 | ||
83 | /** | |
84 | * The Fig for the operations compartment (if any). | |
85 | */ | |
86 | private FigOperationsCompartment operationsFigCompartment; | |
87 | ||
88 | private Rectangle getDefaultBounds() { | |
89 | // this rectangle marks the operation section; all operations | |
90 | // are inside it | |
91 | 0 | Rectangle bounds = new Rectangle(DEFAULT_COMPARTMENT_BOUNDS); |
92 | // 2nd compartment, so adjust Y appropriately | |
93 | 0 | bounds.y = DEFAULT_COMPARTMENT_BOUNDS.y + ROWHEIGHT + 1; |
94 | 0 | return bounds; |
95 | } | |
96 | ||
97 | /** | |
98 | * Construct a Fig with owner, bounds, and settings. | |
99 | * | |
100 | * @param owner the model element that owns this fig | |
101 | * @param bounds the rectangle defining the bounds | |
102 | * @param settings the rendering settings | |
103 | */ | |
104 | public FigClassifierBox(Object owner, Rectangle bounds, | |
105 | DiagramSettings settings) { | |
106 | 0 | super(owner, bounds, settings); |
107 | 0 | operationsFigCompartment = new FigOperationsCompartment( |
108 | owner, | |
109 | getDefaultBounds(), | |
110 | getSettings()); | |
111 | 0 | } |
112 | ||
113 | /* | |
114 | * @see java.lang.Object#clone() | |
115 | */ | |
116 | public Object clone() { | |
117 | 0 | FigClassifierBox figClone = (FigClassifierBox) super.clone(); |
118 | 0 | Iterator thisIter = this.getFigs().iterator(); |
119 | 0 | while (thisIter.hasNext()) { |
120 | 0 | Fig thisFig = (Fig) thisIter.next(); |
121 | 0 | if (thisFig == operationsFigCompartment) { |
122 | 0 | figClone.operationsFigCompartment = (FigOperationsCompartment) thisFig; |
123 | 0 | return figClone; |
124 | } | |
125 | 0 | } |
126 | 0 | return figClone; |
127 | } | |
128 | ||
129 | /** | |
130 | * @deprecated by Bob Tarling in 0.29.3 use | |
131 | * updateCompartment(Model.getMetaTypes().getAttribute()) | |
132 | */ | |
133 | protected void updateOperations() { | |
134 | 0 | if (!isOperationsVisible()) { |
135 | 0 | return; |
136 | } | |
137 | 0 | operationsFigCompartment.populate(); |
138 | ||
139 | 0 | setBounds(getBounds()); |
140 | 0 | damage(); |
141 | 0 | } |
142 | ||
143 | /* | |
144 | * @see org.argouml.uml.diagram.ui.FigNodeModelElement#renderingChanged() | |
145 | */ | |
146 | public void renderingChanged() { | |
147 | 0 | super.renderingChanged(); |
148 | // TODO: We should be able to just call renderingChanged on the child | |
149 | // figs here instead of doing an updateOperations... | |
150 | 0 | updateOperations(); |
151 | ||
152 | // TODO: Taken from FigClassifierBoxWithAttribute to handle events | |
153 | // on an attribute. All this event handling should eventually be moved | |
154 | // to the compartment Fig for attributes | |
155 | 0 | if (isAttributesVisible()) { |
156 | // TODO: We shouldn't actually have to do all this work | |
157 | 0 | updateCompartment(Model.getMetaTypes().getAttribute()); |
158 | } | |
159 | 0 | } |
160 | ||
161 | /** | |
162 | * We are getting events we don't want. Filter them out. | |
163 | * TODO: Can we instruct the model event pump not to send these in the | |
164 | * first place? See defect 5095. | |
165 | * @param event the event | |
166 | */ | |
167 | public void propertyChange(PropertyChangeEvent event) { | |
168 | 0 | if (event.getPropertyName().equals("generalization") |
169 | && Model.getFacade().isAGeneralization(event.getOldValue())) { | |
170 | 0 | return; |
171 | 0 | } else if (event.getPropertyName().equals("association") |
172 | && Model.getFacade().isAAssociationEnd(event.getOldValue())) { | |
173 | 0 | return; |
174 | 0 | } else if (event.getPropertyName().equals("supplierDependency") |
175 | && Model.getFacade().isAUsage(event.getOldValue())) { | |
176 | 0 | return; |
177 | 0 | } else if (event.getPropertyName().equals("clientDependency") |
178 | && Model.getFacade().isAAbstraction(event.getOldValue())) { | |
179 | 0 | return; |
180 | } | |
181 | ||
182 | 0 | super.propertyChange(event); |
183 | 0 | } |
184 | ||
185 | protected void updateLayout(UmlChangeEvent event) { | |
186 | 0 | super.updateLayout(event); |
187 | 0 | if (event instanceof AssociationChangeEvent |
188 | && getOwner().equals(event.getSource())) { | |
189 | 0 | Object o = null; |
190 | 0 | if (event instanceof AddAssociationEvent) { |
191 | 0 | o = event.getNewValue(); |
192 | 0 | } else if (event instanceof RemoveAssociationEvent) { |
193 | 0 | o = event.getOldValue(); |
194 | } | |
195 | 0 | if (Model.getFacade().isAOperation(o) |
196 | || Model.getFacade().isAReception(o)) { | |
197 | 0 | updateOperations(); |
198 | } | |
199 | } | |
200 | ||
201 | // TODO: Taken from FigClassifierBoxWithAttribute to handle events | |
202 | // on an attribute. All this event handling should eventually be moved | |
203 | // to the compartment Fig for attributes | |
204 | 0 | if (Model.getFacade().isAAttribute(getOwner())) { |
205 | 0 | if (event instanceof AttributeChangeEvent) { |
206 | 0 | Object source = event.getSource(); |
207 | 0 | if (Model.getFacade().isAAttribute(source)) { |
208 | // TODO: We just need to get someone to re-render a single | |
209 | // line of text which represents the element here, but I'm | |
210 | // not sure how to do that. - tfm | |
211 | // TODO: Bob replies - we shouldn't be interested in this | |
212 | // event here. The FigFeature (or its notation) should be | |
213 | // listen for change and the FigFeature should be update | |
214 | // from that. | |
215 | 0 | updateCompartment(Model.getMetaTypes().getAttribute()); |
216 | } | |
217 | 0 | } else if (event instanceof AssociationChangeEvent |
218 | && getOwner().equals(event.getSource())) { | |
219 | 0 | Object o = null; |
220 | 0 | if (event instanceof AddAssociationEvent) { |
221 | 0 | o = event.getNewValue(); |
222 | 0 | } else if (event instanceof RemoveAssociationEvent) { |
223 | 0 | o = event.getOldValue(); |
224 | } | |
225 | 0 | if (Model.getFacade().isAAttribute(o)) { |
226 | // TODO: Bob says - we should not be listening here for | |
227 | // addition and removal of attributes. This should be done in | |
228 | // FigAttributesCompartment. | |
229 | 0 | updateCompartment(Model.getMetaTypes().getAttribute()); |
230 | } | |
231 | } | |
232 | } | |
233 | 0 | } |
234 | ||
235 | @Override | |
236 | protected void updateListeners(Object oldOwner, Object newOwner) { | |
237 | ||
238 | 0 | if (isAttributesVisible()) { |
239 | 0 | Set<Object[]> listeners = new HashSet<Object[]>(); |
240 | ||
241 | // Collect the set of model elements that we want to listen to | |
242 | 0 | if (newOwner != null) { |
243 | // TODO: Because we get called on each and every change event, when | |
244 | // the model is in a state of flux, we'll often get an | |
245 | // InvalidElementException before we finish this collection. The | |
246 | // only saving grace is that we're called SO many times that on the | |
247 | // last time, things should be stable again and we'll get a good set | |
248 | // of elements for the final update. We need a better mechanism. | |
249 | ||
250 | // add the listeners to the newOwner | |
251 | 0 | listeners.add(new Object[] {newOwner, null}); |
252 | ||
253 | // and its stereotypes | |
254 | // TODO: Aren't stereotypes handled elsewhere? | |
255 | for (Object stereotype | |
256 | 0 | : Model.getFacade().getStereotypes(newOwner)) { |
257 | 0 | listeners.add(new Object[] {stereotype, null}); |
258 | } | |
259 | ||
260 | // and its features | |
261 | 0 | for (Object feat : Model.getFacade().getFeatures(newOwner)) { |
262 | 0 | listeners.add(new Object[] {feat, null}); |
263 | // and the stereotypes of its features | |
264 | for (Object stereotype | |
265 | 0 | : Model.getFacade().getStereotypes(feat)) { |
266 | 0 | listeners.add(new Object[] {stereotype, null}); |
267 | } | |
268 | // and the parameter of its operations | |
269 | 0 | if (Model.getFacade().isAOperation(feat)) { |
270 | 0 | for (Object param : Model.getFacade().getParameters(feat)) { |
271 | 0 | listeners.add(new Object[] {param, null}); |
272 | } | |
273 | } | |
274 | } | |
275 | } | |
276 | ||
277 | // Update the listeners to match the desired set using the minimal | |
278 | // update facility | |
279 | 0 | updateElementListeners(listeners); |
280 | 0 | } else { |
281 | 0 | super.updateListeners(oldOwner, newOwner); |
282 | } | |
283 | 0 | } |
284 | ||
285 | /** | |
286 | * Updates a compartment box. Called from updateLayout if there is | |
287 | * a model event effecting the attributes/operations and from | |
288 | * renderingChanged in all cases. | |
289 | * TODO: The above statement means that the entire contents of the | |
290 | * compartments are being rebuilt whenever an add/remove | |
291 | * of an attribute, operation or a reception is detected. It would be | |
292 | * better to have compartments listen for add and remove events | |
293 | * and make minimum change rather than entirely rebuild. | |
294 | * Remark MVW: This is a bit exaggerated, since the populate() | |
295 | * method is already heavily optimized. | |
296 | */ | |
297 | protected void updateCompartment(Object metaType) { | |
298 | 0 | FigCompartment fc = getCompartment(metaType); |
299 | 0 | if (!fc.isVisible()) { |
300 | 0 | return; |
301 | } | |
302 | 0 | fc.populate(); |
303 | ||
304 | // TODO: make setBounds, calcBounds and updateBounds consistent | |
305 | 0 | setBounds(getBounds()); |
306 | 0 | } |
307 | ||
308 | /** | |
309 | * @return The graphics for the UML operations (if any). | |
310 | * @deprecated in 0.29.3 use | |
311 | * getCompartment(Model.getUmlFactory(Model.getMetaTypes(),getOperation())) | |
312 | * to determine if an operation compartment exists and return it. | |
313 | * The operationsCompartment should be created by the concrete class | |
314 | */ | |
315 | protected FigOperationsCompartment getOperationsFig() { | |
316 | 0 | return operationsFigCompartment; |
317 | } | |
318 | ||
319 | /** | |
320 | * @deprecated by Bob Tarling in 0.29.3 use | |
321 | * getCompartment(Model.getMetaTypes().getOperation()).getBounds() | |
322 | * @return the bounds | |
323 | */ | |
324 | public Rectangle getOperationsBounds() { | |
325 | 0 | return operationsFigCompartment.getBounds(); |
326 | } | |
327 | ||
328 | /** | |
329 | * @deprecated by Bob Tarling in 0.29.2 use | |
330 | * isCompartmentVisible(Model.getMetaTypes().getOperation()) | |
331 | * @return the visibility | |
332 | */ | |
333 | public boolean isOperationsVisible() { | |
334 | 0 | return operationsFigCompartment != null && operationsFigCompartment.isVisible(); |
335 | } | |
336 | ||
337 | /** | |
338 | * TODO: Should not be on this class as we don't know if we'll have | |
339 | * operations | |
340 | * @param isVisible true if the operation compartment is visible | |
341 | * @deprecated by Bob Tarling in 0.29.2 use setCompartmentVisible | |
342 | */ | |
343 | public void setOperationsVisible(boolean isVisible) { | |
344 | 0 | setCompartmentVisible(operationsFigCompartment, isVisible); |
345 | 0 | } |
346 | ||
347 | /** | |
348 | * @return The graphics for the UML attributes (if any). | |
349 | * @deprecated in 0.29.1 use | |
350 | * getCompartment(Model.getUmlFactory(Model.getMetaTypes(),getAttribute())) | |
351 | * to determine if an attribute compartment exists and return it. | |
352 | * The attributesCompartment should be created by the concrete class | |
353 | */ | |
354 | protected FigAttributesCompartment getAttributesFig() { | |
355 | 0 | FigCompartment fc = getCompartment(Model.getMetaTypes().getAttribute()); |
356 | 0 | return (FigAttributesCompartment) fc; |
357 | } | |
358 | ||
359 | /** | |
360 | * @deprecated by Bob Tarling in 0.29.2 use | |
361 | * getCompartment(Model.getMetaTypes().getAttribute()).getBounds() | |
362 | * @return the bounds | |
363 | */ | |
364 | @Deprecated | |
365 | public Rectangle getAttributesBounds() { | |
366 | 0 | return getAttributesFig().getBounds(); |
367 | } | |
368 | ||
369 | /** | |
370 | * @deprecated by Bob Tarling in 0.29.2 use | |
371 | * isCompartmentVisible(Model.getMetaTypes().getAttribute()) | |
372 | * @return the visibility | |
373 | */ | |
374 | @Deprecated | |
375 | public boolean isAttributesVisible() { | |
376 | 0 | FigCompartment fc = getCompartment(Model.getMetaTypes().getAttribute()); |
377 | 0 | return fc != null && fc.isVisible(); |
378 | } | |
379 | ||
380 | /** | |
381 | * TODO: Should not be on this class as we don't know if we'll have | |
382 | * attributes | |
383 | * @param isVisible true if the attribute compartment is visible | |
384 | * @deprecated by Bob Tarling in 0.29.2 use setCompartmentVisible | |
385 | */ | |
386 | public void setAttributesVisible(boolean isVisible) { | |
387 | 0 | final FigCompartment afc = |
388 | getCompartment(Model.getMetaTypes().getAttribute()); | |
389 | 0 | setCompartmentVisible(afc, isVisible); |
390 | 0 | } |
391 | ||
392 | /* | |
393 | * @see org.tigris.gef.presentation.Fig#translate(int, int) | |
394 | */ | |
395 | public void translate(int dx, int dy) { | |
396 | 0 | super.translate(dx, dy); |
397 | 0 | Editor ce = Globals.curEditor(); |
398 | 0 | if (ce != null) { |
399 | 0 | Selection sel = ce.getSelectionManager().findSelectionFor(this); |
400 | // TODO" What is the purpose of this? Why do we hide buttons here? | |
401 | // Presumably if so we should not assume SelectionClass | |
402 | 0 | if (sel instanceof SelectionClass) { |
403 | 0 | ((SelectionClass) sel).hideButtons(); |
404 | } | |
405 | } | |
406 | 0 | } |
407 | ||
408 | /** | |
409 | * Build a collection of menu items relevant for a right-click | |
410 | * popup menu on an Interface. | |
411 | * | |
412 | * @param me a mouse event | |
413 | * @return a collection of menu items | |
414 | */ | |
415 | public Vector getPopUpActions(MouseEvent me) { | |
416 | 0 | Vector popUpActions = super.getPopUpActions(me); |
417 | ||
418 | // Add ... | |
419 | 0 | ArgoJMenu addMenu = buildAddMenu(); |
420 | 0 | popUpActions.add( |
421 | popUpActions.size() - getPopupAddOffset(), | |
422 | addMenu); | |
423 | ||
424 | // Modifier ... | |
425 | 0 | popUpActions.add( |
426 | popUpActions.size() - getPopupAddOffset(), | |
427 | buildModifierPopUp()); | |
428 | ||
429 | // Visibility ... | |
430 | 0 | popUpActions.add( |
431 | popUpActions.size() - getPopupAddOffset(), | |
432 | buildVisibilityPopUp()); | |
433 | ||
434 | 0 | return popUpActions; |
435 | } | |
436 | ||
437 | protected ArgoJMenu buildShowPopUp() { | |
438 | 0 | ArgoJMenu showMenu = super.buildShowPopUp(); |
439 | ||
440 | 0 | Iterator i = ActionCompartmentDisplay.getActions().iterator(); |
441 | 0 | while (i.hasNext()) { |
442 | 0 | showMenu.add((Action) i.next()); |
443 | } | |
444 | 0 | return showMenu; |
445 | } | |
446 | ||
447 | protected ArgoJMenu buildAddMenu() { | |
448 | 0 | ArgoJMenu addMenu = new ArgoJMenu("menu.popup.add"); |
449 | ||
450 | 0 | List<FigCompartment> comps = getCompartments(); |
451 | 0 | for (FigCompartment comp : comps) { |
452 | 0 | final Object metaType = comp.getCompartmentType(); |
453 | 0 | Action addAction = new ActionCreateContainedModelElement(metaType, getOwner()); |
454 | 0 | addAction.setEnabled(isSingleTarget()); |
455 | 0 | addMenu.insert(addAction, 0); |
456 | 0 | } |
457 | 0 | addMenu.add(new ActionAddNote()); |
458 | 0 | addMenu.add(ActionEdgesDisplay.getShowEdges()); |
459 | 0 | addMenu.add(ActionEdgesDisplay.getHideEdges()); |
460 | 0 | return addMenu; |
461 | } | |
462 | ||
463 | /** | |
464 | * USED BY PGML.tee. | |
465 | * TODO We should loop round the compartments to build this string. That | |
466 | * way we have no attribute/operation knowledge at this level. | |
467 | * @return the class name and bounds together with compartment | |
468 | * visibility. | |
469 | */ | |
470 | public String classNameAndBounds() { | |
471 | 0 | String classNameAndBounds = super.classNameAndBounds() |
472 | + "operationsVisible=" + isOperationsVisible() + ";"; | |
473 | 0 | FigCompartment fc = getCompartment(Model.getMetaTypes().getAttribute()); |
474 | 0 | if (fc != null) { |
475 | 0 | classNameAndBounds += |
476 | "attributesVisible=" + fc.isVisible() + ";"; | |
477 | } | |
478 | 0 | return classNameAndBounds; |
479 | } | |
480 | ||
481 | ||
482 | protected Object buildModifierPopUp() { | |
483 | 0 | return buildModifierPopUp(ABSTRACT | LEAF | ROOT); |
484 | } | |
485 | } |