Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
NotationProvider |
|
| 2.388888888888889;2.389 |
1 | /* $Id: NotationProvider.java 18852 2010-11-20 19:27:11Z mvw $ | |
2 | ***************************************************************************** | |
3 | * Copyright (c) 2009-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 | * Michiel van der Wulp | |
11 | ***************************************************************************** | |
12 | * | |
13 | * Some portions of this file was previously release using the BSD License: | |
14 | */ | |
15 | ||
16 | // Copyright (c) 2005-2009 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.notation; | |
40 | ||
41 | import java.beans.PropertyChangeEvent; | |
42 | import java.beans.PropertyChangeListener; | |
43 | import java.util.ArrayList; | |
44 | import java.util.Collection; | |
45 | ||
46 | import org.apache.log4j.Logger; | |
47 | import org.argouml.model.AddAssociationEvent; | |
48 | import org.argouml.model.DeleteInstanceEvent; | |
49 | import org.argouml.model.Model; | |
50 | import org.argouml.model.RemoveAssociationEvent; | |
51 | ||
52 | /** | |
53 | * A class that implements this abstract class manages a text | |
54 | * shown on a diagram. This means it is able to generate | |
55 | * text that represents one or more UML objects. | |
56 | * And when the user has edited this text, the model may be adapted | |
57 | * by parsing the text. | |
58 | * Additionally, a help text for the parsing is provided, | |
59 | * so that the user knows the syntax. | |
60 | * | |
61 | * @author Michiel van der Wulp | |
62 | */ | |
63 | 0 | public abstract class NotationProvider implements PropertyChangeListener { |
64 | ||
65 | 0 | private static final Logger LOG = Logger.getLogger(NotationProvider.class); |
66 | private NotationRenderer renderer; | |
67 | ||
68 | /** | |
69 | * A collection of properties of listeners registered for this notation. | |
70 | * Each entry is a 2 element array containing the element and the property | |
71 | * name(s) for which a listener is registered. This facilitates easy removal | |
72 | * of a complex set of listeners. | |
73 | */ | |
74 | 0 | private final Collection<Object[]> listeners = new ArrayList<Object[]>(); |
75 | ||
76 | /** | |
77 | * @return a i18 key that represents a help string | |
78 | * giving an explanation to the user of the syntax | |
79 | */ | |
80 | public abstract String getParsingHelp(); | |
81 | ||
82 | /** | |
83 | * Parses the given text, and adapts the modelElement and | |
84 | * maybe related elements accordingly. | |
85 | * | |
86 | * @param modelElement the modelelement to adapt | |
87 | * @param text the string given by the user to be parsed | |
88 | * to adapt the model | |
89 | */ | |
90 | public abstract void parse(Object modelElement, String text); | |
91 | ||
92 | /** | |
93 | * Generate a string representation for the given model element. | |
94 | * | |
95 | * @param modelElement the base UML element | |
96 | * @param settings settings that control rendering of the text | |
97 | * @return the string written in the correct notation | |
98 | */ | |
99 | public abstract String toString(Object modelElement, | |
100 | NotationSettings settings); | |
101 | ||
102 | /** | |
103 | * Initialize the appropriate model change listeners | |
104 | * for the given modelelement to the given listener. | |
105 | * Overrule this when you need more than | |
106 | * listening to all events from the base modelelement. | |
107 | * | |
108 | * @param modelElement the modelelement that we provide | |
109 | * notation for | |
110 | */ | |
111 | public void initialiseListener(Object modelElement) { | |
112 | 0 | addElementListener(modelElement); |
113 | 0 | } |
114 | ||
115 | /** | |
116 | * Clean out the listeners registered before. | |
117 | * <p> | |
118 | * The default implementation is to remove all listeners | |
119 | * that were remembered by the utility functions below. | |
120 | */ | |
121 | public void cleanListener() { | |
122 | 0 | removeAllElementListeners(); |
123 | 0 | } |
124 | ||
125 | /** | |
126 | * Update the set of listeners based on the given event. <p> | |
127 | * | |
128 | * The default implementation just removes all listeners, and then | |
129 | * re-initializes completely - this is method 1. | |
130 | * A more efficient way would be to dissect | |
131 | * the propertyChangeEvent, and only adapt the listeners | |
132 | * that need to be adapted - this is method 2. <p> | |
133 | * | |
134 | * Method 2 is explained by the code below that is commented out. | |
135 | * Method 1 is the easiest to implement, since at every arrival of an event, | |
136 | * we just remove all old listeners, and then inspect the current model, | |
137 | * and add listeners where we need them. I.e. the advantage is | |
138 | * that we only need to traverse the model structure in one location, i.e. | |
139 | * the initialiseListener() method. | |
140 | * | |
141 | * @param modelElement the modelelement that we provide | |
142 | * notation for | |
143 | * @param pce the received event, that we base the changes on | |
144 | */ | |
145 | public void updateListener(Object modelElement, PropertyChangeEvent pce) { | |
146 | // e.g. for an operation: | |
147 | // if pce.getSource() == modelElement | |
148 | // && event.propertyName = "parameter" | |
149 | // if event instanceof AddAssociationEvent | |
150 | // Get the parameter instance from event.newValue | |
151 | // Call model to add listener on parameter on change | |
152 | // of "name", "type" | |
153 | // else if event instanceof RemoveAssociationEvent | |
154 | // Get the parameter instance from event.oldValue | |
155 | // Call model to remove listener on parameter on change | |
156 | // of "name", "type" | |
157 | // end if | |
158 | // end if | |
159 | 0 | if (Model.getUmlFactory().isRemoved(modelElement)) { |
160 | 0 | LOG.warn("Encountered deleted object during delete of " |
161 | + modelElement); | |
162 | 0 | return; |
163 | } | |
164 | 0 | cleanListener(); |
165 | 0 | initialiseListener(modelElement); |
166 | 0 | } |
167 | ||
168 | public void propertyChange(PropertyChangeEvent evt) { | |
169 | 0 | if (renderer != null) { |
170 | 0 | Object owner = renderer.getOwner(this); |
171 | 0 | if ((owner == evt.getSource()) |
172 | && (evt instanceof DeleteInstanceEvent)) { | |
173 | 0 | return; |
174 | } | |
175 | 0 | if (owner != null) { |
176 | 0 | if (Model.getUmlFactory().isRemoved(owner)) { |
177 | 0 | LOG.warn("Encountered deleted object during delete of " |
178 | + owner); | |
179 | 0 | return; |
180 | } | |
181 | 0 | renderer.notationRenderingChanged(this, |
182 | toString(owner, renderer.getNotationSettings(this))); | |
183 | 0 | if (evt instanceof AddAssociationEvent |
184 | || evt instanceof RemoveAssociationEvent) { | |
185 | 0 | initialiseListener(owner); |
186 | } | |
187 | } | |
188 | } | |
189 | 0 | } |
190 | ||
191 | /* | |
192 | * Add an element listener and remember the registration. | |
193 | * | |
194 | * @param element | |
195 | * element to listen for changes on | |
196 | * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String) | |
197 | */ | |
198 | protected final void addElementListener(PropertyChangeListener listener, | |
199 | Object element) { | |
200 | 0 | if (Model.getUmlFactory().isRemoved(element)) { |
201 | 0 | LOG.warn("Encountered deleted object during delete of " + element); |
202 | 0 | return; |
203 | } | |
204 | 0 | Object[] entry = new Object[] {element, null}; |
205 | 0 | if (!listeners.contains(entry)) { |
206 | 0 | listeners.add(entry); |
207 | 0 | Model.getPump().addModelEventListener(listener, element); |
208 | } else { | |
209 | 0 | LOG.warn("Attempted duplicate registration of event listener" |
210 | + " - Element: " + element + " Listener: " + listener); | |
211 | } | |
212 | 0 | } |
213 | ||
214 | /** | |
215 | * Utility function to add a listener for an array of property names | |
216 | * and remember the registration. | |
217 | * | |
218 | * @param element element to listen for changes on | |
219 | */ | |
220 | public final void addElementListener(Object element) { | |
221 | 0 | addElementListener(this, element); |
222 | 0 | } |
223 | ||
224 | /* | |
225 | * Utility function to add a listener for a given property name | |
226 | * and remember the registration. | |
227 | * | |
228 | * @param element | |
229 | * element to listen for changes on | |
230 | * @param property | |
231 | * name of property to listen for changes of | |
232 | * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, | |
233 | * Object, String) | |
234 | */ | |
235 | protected final void addElementListener(PropertyChangeListener listener, | |
236 | Object element, String property) { | |
237 | 0 | if (Model.getUmlFactory().isRemoved(element)) { |
238 | 0 | LOG.warn("Encountered deleted object during delete of " + element); |
239 | 0 | return; |
240 | } | |
241 | 0 | Object[] entry = new Object[] {element, property}; |
242 | 0 | if (!listeners.contains(entry)) { |
243 | 0 | listeners.add(entry); |
244 | 0 | Model.getPump().addModelEventListener(listener, element, property); |
245 | } else { | |
246 | 0 | LOG.debug("Attempted duplicate registration of event listener" |
247 | + " - Element: " + element + " Listener: " + listener); | |
248 | } | |
249 | 0 | } |
250 | ||
251 | /** | |
252 | * Utility function to add a listener for an array of property names | |
253 | * and remember the registration. | |
254 | * | |
255 | * @param element element to listen for changes on | |
256 | * @param property name of property to listen for changes of | |
257 | */ | |
258 | public final void addElementListener(Object element, String property) { | |
259 | 0 | addElementListener(this, element, property); |
260 | 0 | } |
261 | ||
262 | /* | |
263 | * Utility function to add a listener for an array of property names | |
264 | * and remember the registration. | |
265 | * | |
266 | * @param element | |
267 | * element to listen for changes on | |
268 | * @param property | |
269 | * array of property names (Strings) to listen for changes of | |
270 | * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, | |
271 | * Object, String) | |
272 | */ | |
273 | protected final void addElementListener(PropertyChangeListener listener, | |
274 | Object element, String[] property) { | |
275 | 0 | if (Model.getUmlFactory().isRemoved(element)) { |
276 | 0 | LOG.warn("Encountered deleted object during delete of " + element); |
277 | 0 | return; |
278 | } | |
279 | 0 | Object[] entry = new Object[] {element, property}; |
280 | 0 | if (!listeners.contains(entry)) { |
281 | 0 | listeners.add(entry); |
282 | 0 | Model.getPump().addModelEventListener(listener, element, property); |
283 | } else { | |
284 | 0 | LOG.debug("Attempted duplicate registration of event listener" |
285 | + " - Element: " + element + " Listener: " + listener); | |
286 | } | |
287 | 0 | } |
288 | ||
289 | /** | |
290 | * Utility function to add a listener for an array of property names | |
291 | * and remember the registration. | |
292 | * | |
293 | * @param element element to listen for changes on | |
294 | * @param property array of property names (Strings) | |
295 | * to listen for changes of | |
296 | */ | |
297 | public final void addElementListener(Object element, String[] property) { | |
298 | 0 | addElementListener(this, element, property); |
299 | 0 | } |
300 | ||
301 | /* | |
302 | * Utility function to remove an element listener | |
303 | * and adapt the remembered list of registration. | |
304 | * | |
305 | * @param element | |
306 | * element to listen for changes on | |
307 | * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String) | |
308 | */ | |
309 | protected final void removeElementListener(PropertyChangeListener listener, | |
310 | Object element) { | |
311 | 0 | listeners.remove(new Object[] {element, null}); |
312 | 0 | Model.getPump().removeModelEventListener(listener, element); |
313 | 0 | } |
314 | ||
315 | /** | |
316 | * Utility function to remove an element listener | |
317 | * and adapt the remembered list of registration. | |
318 | * | |
319 | * @param element element to listen for changes on | |
320 | */ | |
321 | public final void removeElementListener(Object element) { | |
322 | 0 | removeElementListener(this, element); |
323 | 0 | } |
324 | ||
325 | /* | |
326 | * Utility function to unregister all listeners | |
327 | * registered through addElementListener. | |
328 | * | |
329 | * @see #addElementListener(Object, String) | |
330 | */ | |
331 | protected final void removeAllElementListeners( | |
332 | PropertyChangeListener listener) { | |
333 | 0 | for (Object[] lis : listeners) { |
334 | 0 | Object property = lis[1]; |
335 | 0 | if (property == null) { |
336 | 0 | Model.getPump().removeModelEventListener(listener, lis[0]); |
337 | 0 | } else if (property instanceof String[]) { |
338 | 0 | Model.getPump().removeModelEventListener(listener, lis[0], |
339 | (String[]) property); | |
340 | 0 | } else if (property instanceof String) { |
341 | 0 | Model.getPump().removeModelEventListener(listener, lis[0], |
342 | (String) property); | |
343 | } else { | |
344 | 0 | throw new RuntimeException( |
345 | "Internal error in removeAllElementListeners"); | |
346 | } | |
347 | 0 | } |
348 | 0 | listeners.clear(); |
349 | 0 | } |
350 | ||
351 | /** | |
352 | * Utility function to unregister all listeners | |
353 | * registered through addElementListener. | |
354 | */ | |
355 | public final void removeAllElementListeners() { | |
356 | 0 | removeAllElementListeners(this); |
357 | 0 | } |
358 | ||
359 | /** | |
360 | * @param nr the NotationRenderer | |
361 | */ | |
362 | void setRenderer(NotationRenderer nr) { | |
363 | 0 | renderer = nr; |
364 | 0 | } |
365 | } |