1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
package org.argouml.model.euml; |
13 | |
|
14 | |
import java.beans.PropertyChangeEvent; |
15 | |
import java.beans.PropertyChangeListener; |
16 | |
import java.util.ArrayList; |
17 | |
import java.util.EventObject; |
18 | |
import java.util.HashMap; |
19 | |
import java.util.HashSet; |
20 | |
import java.util.Iterator; |
21 | |
import java.util.LinkedHashMap; |
22 | |
import java.util.LinkedList; |
23 | |
import java.util.List; |
24 | |
import java.util.Map; |
25 | |
import java.util.Set; |
26 | |
|
27 | |
import org.apache.log4j.Logger; |
28 | |
import org.argouml.model.AbstractModelEventPump; |
29 | |
import org.argouml.model.AddAssociationEvent; |
30 | |
import org.argouml.model.AttributeChangeEvent; |
31 | |
import org.argouml.model.DeleteInstanceEvent; |
32 | |
import org.argouml.model.RemoveAssociationEvent; |
33 | |
import org.eclipse.emf.common.command.CommandStackListener; |
34 | |
import org.eclipse.emf.common.notify.Notification; |
35 | |
import org.eclipse.emf.common.notify.Notifier; |
36 | |
import org.eclipse.emf.common.notify.impl.NotificationImpl; |
37 | |
import org.eclipse.emf.ecore.ENamedElement; |
38 | |
import org.eclipse.emf.ecore.EObject; |
39 | |
import org.eclipse.emf.ecore.EReference; |
40 | |
import org.eclipse.uml2.uml.Property; |
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
class ModelEventPumpEUMLImpl extends AbstractModelEventPump { |
46 | |
|
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | 0 | final private List<Property> deleteEventIgnoreList = |
53 | |
new ArrayList<Property>(); |
54 | |
|
55 | |
|
56 | |
|
57 | |
|
58 | |
private class Listener { |
59 | |
|
60 | |
private PropertyChangeListener listener; |
61 | |
|
62 | |
private Set<String> props; |
63 | |
|
64 | 0 | Listener(PropertyChangeListener listener, String[] properties) { |
65 | 0 | this.listener = listener; |
66 | 0 | if (properties != null) { |
67 | 0 | setProperties(properties); |
68 | |
} |
69 | 0 | } |
70 | |
|
71 | |
void setProperties(String[] properties) { |
72 | 0 | if (properties == null) { |
73 | 0 | props = null; |
74 | |
} else { |
75 | 0 | if (props == null) { |
76 | 0 | props = new HashSet<String>(); |
77 | |
} |
78 | 0 | for (String s : properties) { |
79 | 0 | props.add(s); |
80 | |
} |
81 | |
} |
82 | 0 | } |
83 | |
|
84 | |
void removeProperties(String[] properties) { |
85 | 0 | if (props == null) { |
86 | 0 | return; |
87 | |
} |
88 | 0 | for (String s : properties) { |
89 | 0 | props.remove(s); |
90 | |
} |
91 | 0 | } |
92 | |
|
93 | |
PropertyChangeListener getListener() { |
94 | 0 | return listener; |
95 | |
} |
96 | |
|
97 | |
Set<String> getProperties() { |
98 | 0 | return props; |
99 | |
} |
100 | |
|
101 | |
} |
102 | |
|
103 | |
|
104 | |
|
105 | |
|
106 | |
private EUMLModelImplementation modelImpl; |
107 | |
|
108 | 0 | private RootContainerAdapter rootContainerAdapter = |
109 | |
new RootContainerAdapter(this); |
110 | |
|
111 | |
|
112 | 0 | private Map<Object, List<Listener>> registerForElements = |
113 | |
new HashMap<Object, List<Listener>>(); |
114 | |
|
115 | |
|
116 | 0 | private Map<Object, List<Listener>> registerForClasses = |
117 | |
new LinkedHashMap<Object, List<Listener>>(); |
118 | |
|
119 | |
private Object mutex; |
120 | |
|
121 | 0 | private static final Logger LOG = |
122 | |
Logger.getLogger(ModelEventPumpEUMLImpl.class); |
123 | |
|
124 | |
public static final int COMMAND_STACK_UPDATE = |
125 | |
Notification.EVENT_TYPE_COUNT + 1; |
126 | |
|
127 | |
|
128 | |
|
129 | |
|
130 | |
|
131 | |
|
132 | |
|
133 | 0 | public ModelEventPumpEUMLImpl(EUMLModelImplementation implementation) { |
134 | 0 | modelImpl = implementation; |
135 | 0 | mutex = this; |
136 | 0 | implementation.getEditingDomain().getCommandStack() |
137 | 0 | .addCommandStackListener(new CommandStackListener() { |
138 | |
|
139 | |
public void commandStackChanged(EventObject event) { |
140 | 0 | notifyChanged(new NotificationImpl( |
141 | |
COMMAND_STACK_UPDATE, false, false)); |
142 | 0 | } |
143 | |
|
144 | |
}); |
145 | 0 | } |
146 | |
|
147 | |
|
148 | |
|
149 | |
|
150 | |
|
151 | |
|
152 | |
public void setRootContainer(Notifier container) { |
153 | 0 | rootContainerAdapter.setRootContainer(container); |
154 | 0 | } |
155 | |
|
156 | |
public RootContainerAdapter getRootContainer() { |
157 | 0 | return rootContainerAdapter; |
158 | |
} |
159 | |
|
160 | |
public void addClassModelEventListener(PropertyChangeListener listener, |
161 | |
Object modelClass, String[] propertyNames) { |
162 | 0 | if (!(modelClass instanceof Class |
163 | |
&& EObject.class.isAssignableFrom((Class) modelClass))) { |
164 | 0 | throw new IllegalArgumentException( |
165 | |
"The model class must be instance of " |
166 | |
+ "java.lang.Class<EObject>"); |
167 | |
} |
168 | 0 | registerListener( |
169 | |
modelClass, listener, propertyNames, registerForClasses); |
170 | 0 | } |
171 | |
|
172 | |
public void addModelEventListener(PropertyChangeListener listener, |
173 | |
Object modelElement, String[] propertyNames) { |
174 | 0 | if (LOG.isDebugEnabled()) { |
175 | 0 | LOG.debug("Adding a listener to " |
176 | |
+ modelElement |
177 | |
+ " for " |
178 | |
+ propertyNames); |
179 | |
} |
180 | 0 | if (!(modelElement instanceof EObject)) { |
181 | 0 | throw new IllegalArgumentException( |
182 | |
"The modelelement must be instance " |
183 | |
+ "of EObject."); |
184 | |
} |
185 | 0 | registerListener( |
186 | |
modelElement, listener, propertyNames, registerForElements); |
187 | 0 | } |
188 | |
|
189 | |
public void addModelEventListener(PropertyChangeListener listener, |
190 | |
Object modelelement) { |
191 | 0 | addModelEventListener(listener, modelelement, (String[]) null); |
192 | 0 | } |
193 | |
|
194 | |
private void registerListener(Object notifier, |
195 | |
PropertyChangeListener listener, String[] propertyNames, |
196 | |
Map<Object, List<Listener>> register) { |
197 | 0 | if (notifier == null || listener == null) { |
198 | 0 | throw new NullPointerException( |
199 | |
"The model element/class and the " |
200 | |
+ "listener must be non-null."); |
201 | |
} |
202 | 0 | synchronized (mutex) { |
203 | 0 | List<Listener> list = register.get(notifier); |
204 | 0 | boolean found = false; |
205 | 0 | if (list == null) { |
206 | 0 | list = new ArrayList<Listener>(); |
207 | |
} else { |
208 | 0 | for (Listener l : list) { |
209 | 0 | if (l.getListener() == listener) { |
210 | 0 | l.setProperties(propertyNames); |
211 | 0 | found = true; |
212 | 0 | break; |
213 | |
} |
214 | |
} |
215 | |
} |
216 | 0 | if (!found) { |
217 | 0 | list.add(new Listener(listener, propertyNames)); |
218 | 0 | register.put(notifier, list); |
219 | |
} |
220 | 0 | } |
221 | 0 | } |
222 | |
|
223 | |
public void flushModelEvents() { |
224 | |
|
225 | 0 | } |
226 | |
|
227 | |
public void removeClassModelEventListener(PropertyChangeListener listener, |
228 | |
Object modelClass, String[] propertyNames) { |
229 | 0 | if (!(modelClass instanceof Class && EObject.class |
230 | |
.isAssignableFrom((Class) modelClass))) { |
231 | 0 | throw new IllegalArgumentException(); |
232 | |
} |
233 | 0 | unregisterListener( |
234 | |
modelClass, listener, propertyNames, registerForClasses); |
235 | 0 | } |
236 | |
|
237 | |
public void removeModelEventListener(PropertyChangeListener listener, |
238 | |
Object modelelement, String[] propertyNames) { |
239 | 0 | if (!(modelelement instanceof EObject)) { |
240 | 0 | throw new IllegalArgumentException(); |
241 | |
} |
242 | 0 | unregisterListener( |
243 | |
modelelement, listener, propertyNames, registerForElements); |
244 | 0 | } |
245 | |
|
246 | |
public void removeModelEventListener(PropertyChangeListener listener, |
247 | |
Object modelelement) { |
248 | 0 | removeModelEventListener(listener, modelelement, (String[]) null); |
249 | 0 | } |
250 | |
|
251 | |
private void unregisterListener(Object notifier, |
252 | |
PropertyChangeListener listener, String[] propertyNames, |
253 | |
Map<Object, List<Listener>> register) { |
254 | 0 | if (notifier == null || listener == null) { |
255 | 0 | throw new NullPointerException( |
256 | |
"The model element/class and the " |
257 | |
+ "listener must be non-null."); |
258 | |
} |
259 | 0 | synchronized (mutex) { |
260 | 0 | List<Listener> list = register.get(notifier); |
261 | 0 | if (list == null) { |
262 | 0 | return; |
263 | |
} |
264 | 0 | Iterator<Listener> iter = list.iterator(); |
265 | 0 | while (iter.hasNext()) { |
266 | 0 | Listener l = iter.next(); |
267 | 0 | if (l.getListener() == listener) { |
268 | 0 | if (propertyNames != null) { |
269 | 0 | l.removeProperties(propertyNames); |
270 | |
} else { |
271 | 0 | iter.remove(); |
272 | |
} |
273 | 0 | break; |
274 | |
} |
275 | 0 | } |
276 | 0 | } |
277 | 0 | } |
278 | |
|
279 | |
|
280 | |
|
281 | |
|
282 | |
|
283 | |
|
284 | |
public void notifyChanged(Notification notification) { |
285 | |
|
286 | 0 | if (notification.getEventType() == Notification.REMOVING_ADAPTER) { |
287 | 0 | return; |
288 | |
} |
289 | |
|
290 | 0 | final ENamedElement feature = (ENamedElement) notification.getFeature(); |
291 | |
|
292 | 0 | final String featureName = |
293 | |
feature == null ? "" : feature.getName(); |
294 | |
|
295 | |
final EReference oppositeRef; |
296 | 0 | if (feature instanceof EReference) { |
297 | 0 | oppositeRef = ((EReference) feature).getEOpposite(); |
298 | |
} else { |
299 | 0 | oppositeRef = null; |
300 | |
} |
301 | |
|
302 | 0 | fireEvent( |
303 | |
notification.getNotifier(), |
304 | |
notification.getOldValue(), |
305 | |
notification.getNewValue(), |
306 | |
notification.getEventType(), |
307 | |
featureName, |
308 | |
oppositeRef); |
309 | 0 | } |
310 | |
|
311 | |
|
312 | |
|
313 | |
|
314 | |
|
315 | |
|
316 | |
void fireEvent( |
317 | |
Object notifier, |
318 | |
Object oldValue, |
319 | |
Object newValue, |
320 | |
int eventType, |
321 | |
String featureName, |
322 | |
EReference oppositeRef) { |
323 | |
|
324 | 0 | LOG.debug("event - Property: " |
325 | |
+ featureName |
326 | |
+ " Old: " + oldValue |
327 | |
+ " New: " + newValue |
328 | |
+ " From: " + notifier); |
329 | |
|
330 | 0 | class EventAndListeners { |
331 | |
public EventAndListeners(PropertyChangeEvent e, |
332 | 0 | List<PropertyChangeListener> l) { |
333 | 0 | event = e; |
334 | 0 | listeners = l; |
335 | 0 | } |
336 | |
|
337 | |
private PropertyChangeEvent event; |
338 | |
|
339 | |
private List<PropertyChangeListener> listeners; |
340 | |
} |
341 | |
|
342 | 0 | List<EventAndListeners> events = new ArrayList<EventAndListeners>(); |
343 | |
|
344 | 0 | if (eventType == Notification.SET) { |
345 | 0 | String propName = |
346 | |
mapPropertyName(featureName); |
347 | 0 | events.add(new EventAndListeners(new AttributeChangeEvent( |
348 | |
notifier, propName, |
349 | |
oldValue, newValue, |
350 | |
null), getListeners( |
351 | |
notifier, propName))); |
352 | 0 | } else if (eventType == Notification.ADD |
353 | |
|| eventType == Notification.REMOVE) { |
354 | 0 | String propName = mapPropertyName(featureName); |
355 | 0 | if (eventType == Notification.ADD) { |
356 | 0 | events.add(new EventAndListeners(new AddAssociationEvent( |
357 | |
notifier, propName, null, |
358 | |
newValue, |
359 | |
newValue, null), getListeners( |
360 | |
notifier, propName))); |
361 | 0 | events.add(new EventAndListeners(new AttributeChangeEvent( |
362 | |
notifier, propName, null, |
363 | |
newValue, null), getListeners( |
364 | |
notifier, propName))); |
365 | |
} else { |
366 | 0 | if (isDeleteEventRequired(oldValue)) { |
367 | |
|
368 | |
|
369 | |
|
370 | |
|
371 | 0 | events.add(new EventAndListeners( |
372 | |
new DeleteInstanceEvent( |
373 | |
oldValue, |
374 | |
"remove", |
375 | |
null, null, null), |
376 | |
getListeners( |
377 | |
oldValue))); |
378 | |
} |
379 | 0 | events.add(new EventAndListeners( |
380 | |
new RemoveAssociationEvent( |
381 | |
notifier, propName, |
382 | |
oldValue, null, |
383 | |
oldValue, null), |
384 | |
getListeners( |
385 | |
notifier, propName))); |
386 | 0 | events.add(new EventAndListeners( |
387 | |
new AttributeChangeEvent( |
388 | |
notifier, propName, |
389 | |
oldValue, null, null), |
390 | |
getListeners( |
391 | |
notifier, propName))); |
392 | |
} |
393 | |
|
394 | 0 | if (oppositeRef != null) { |
395 | 0 | propName = mapPropertyName(oppositeRef.getName()); |
396 | 0 | if (eventType == Notification.ADD) { |
397 | 0 | events.add(new EventAndListeners( |
398 | |
new AddAssociationEvent( |
399 | |
newValue, |
400 | |
propName, null, |
401 | |
notifier, |
402 | |
notifier, null), |
403 | |
getListeners( |
404 | |
newValue, |
405 | |
propName))); |
406 | 0 | events.add(new EventAndListeners( |
407 | |
new AttributeChangeEvent( |
408 | |
newValue, |
409 | |
propName, null, |
410 | |
notifier, null), |
411 | |
getListeners( |
412 | |
newValue, |
413 | |
propName))); |
414 | |
} else { |
415 | 0 | events.add(new EventAndListeners( |
416 | |
new AddAssociationEvent( |
417 | |
oldValue, |
418 | |
propName, |
419 | |
notifier, null, |
420 | |
notifier, null), |
421 | |
getListeners( |
422 | |
oldValue, |
423 | |
propName))); |
424 | 0 | events.add(new EventAndListeners( |
425 | |
new AttributeChangeEvent( |
426 | |
oldValue, |
427 | |
propName, |
428 | |
notifier, null, null), |
429 | |
getListeners( |
430 | |
oldValue, |
431 | |
propName))); |
432 | |
} |
433 | |
} |
434 | |
} |
435 | |
|
436 | 0 | for (EventAndListeners e : events) { |
437 | 0 | if (e.listeners != null) { |
438 | 0 | for (PropertyChangeListener l : e.listeners) { |
439 | 0 | l.propertyChange(e.event); |
440 | |
} |
441 | |
} |
442 | |
} |
443 | 0 | } |
444 | |
|
445 | |
|
446 | |
|
447 | |
|
448 | |
|
449 | |
|
450 | |
|
451 | |
|
452 | |
|
453 | |
|
454 | |
|
455 | |
private boolean isDeleteEventRequired( |
456 | |
final Object element) { |
457 | 0 | if (element instanceof Property) { |
458 | 0 | synchronized (deleteEventIgnoreList) { |
459 | 0 | if (deleteEventIgnoreList.contains(element)) { |
460 | 0 | deleteEventIgnoreList.remove(element); |
461 | 0 | return false; |
462 | |
} |
463 | 0 | } |
464 | |
} |
465 | 0 | return true; |
466 | |
} |
467 | |
|
468 | |
|
469 | |
|
470 | |
|
471 | |
|
472 | |
|
473 | |
|
474 | |
void addElementForDeleteEventIgnore(Property property) { |
475 | 0 | synchronized (deleteEventIgnoreList) { |
476 | 0 | deleteEventIgnoreList.add(property); |
477 | 0 | } |
478 | 0 | } |
479 | |
|
480 | |
private List<PropertyChangeListener> getListeners(Object element) { |
481 | 0 | return getListeners(element, null); |
482 | |
} |
483 | |
|
484 | |
@SuppressWarnings("unchecked") |
485 | |
private List<PropertyChangeListener> getListeners(Object element, |
486 | |
String propName) { |
487 | 0 | List<PropertyChangeListener> returnedList = |
488 | |
new ArrayList<PropertyChangeListener>(); |
489 | |
|
490 | 0 | synchronized (mutex) { |
491 | 0 | addListeners(returnedList, element, propName, registerForElements); |
492 | 0 | for (Object o : registerForClasses.keySet()) { |
493 | 0 | if (o instanceof Class) { |
494 | 0 | Class type = (Class) o; |
495 | 0 | if (type.isAssignableFrom(element.getClass())) { |
496 | 0 | addListeners( |
497 | |
returnedList, o, propName, registerForClasses); |
498 | |
} |
499 | 0 | } |
500 | |
} |
501 | 0 | } |
502 | 0 | return returnedList.isEmpty() ? null : returnedList; |
503 | |
} |
504 | |
|
505 | |
private void addListeners(List<PropertyChangeListener> listeners, |
506 | |
Object element, String propName, |
507 | |
Map<Object, List<Listener>> register) { |
508 | 0 | List<Listener> list = register.get(element); |
509 | 0 | if (list != null) { |
510 | 0 | for (Listener l : list) { |
511 | 0 | if (propName == null || l.getProperties() == null |
512 | |
|| l.getProperties().contains(propName)) { |
513 | 0 | listeners.add(l.getListener()); |
514 | |
} |
515 | |
} |
516 | |
} |
517 | 0 | } |
518 | |
|
519 | |
public void startPumpingEvents() { |
520 | 0 | rootContainerAdapter.setDeliverEvents(true); |
521 | 0 | } |
522 | |
|
523 | |
public void stopPumpingEvents() { |
524 | 0 | rootContainerAdapter.setDeliverEvents(false); |
525 | 0 | } |
526 | |
|
527 | |
private String mapPropertyName(String name) { |
528 | |
|
529 | 0 | if (name.equals("ownedAttribute")) { |
530 | 0 | return "feature"; |
531 | |
} |
532 | 0 | return name; |
533 | |
} |
534 | |
|
535 | |
@SuppressWarnings("unchecked") |
536 | |
public List getDebugInfo() { |
537 | 0 | List info = new ArrayList(); |
538 | 0 | info.add("Event Listeners"); |
539 | 0 | for (Iterator it = registerForElements.entrySet().iterator(); |
540 | 0 | it.hasNext(); ) { |
541 | 0 | Map.Entry entry = (Map.Entry) it.next(); |
542 | |
|
543 | 0 | String item = entry.getKey().toString(); |
544 | 0 | List modelElementNode = newDebugNode(item); |
545 | 0 | info.add(modelElementNode); |
546 | 0 | List listenerList = (List) entry.getValue(); |
547 | |
|
548 | 0 | Map<String, List<String>> map = new HashMap<String, List<String>>(); |
549 | |
|
550 | 0 | for (Iterator listIt = listenerList.iterator(); listIt.hasNext();) { |
551 | 0 | Listener listener = (Listener) listIt.next(); |
552 | |
|
553 | 0 | if (listener.getProperties() != null) { |
554 | 0 | for (String eventName : listener.getProperties()) { |
555 | 0 | if (!map.containsKey(eventName)) { |
556 | 0 | map.put(eventName, new LinkedList<String>()); |
557 | |
} |
558 | 0 | map.get(eventName).add( |
559 | |
listener.getListener().getClass().getName()); |
560 | |
} |
561 | |
} else { |
562 | 0 | if (!map.containsKey("")) { |
563 | 0 | map.put("", new LinkedList<String>()); |
564 | |
} |
565 | 0 | map.get("") |
566 | |
.add(listener.getListener().getClass().getName()); |
567 | |
} |
568 | 0 | } |
569 | 0 | for (Map.Entry o : map.entrySet()) { |
570 | 0 | modelElementNode.add((String) o.getKey()); |
571 | 0 | modelElementNode.add((List<String>) o.getValue()); |
572 | |
} |
573 | 0 | } |
574 | 0 | return info; |
575 | |
} |
576 | |
|
577 | |
private List<String> newDebugNode(String name) { |
578 | 0 | List<String> list = new ArrayList<String>(); |
579 | 0 | list.add(name); |
580 | 0 | return list; |
581 | |
} |
582 | |
|
583 | |
} |