Skip to content

Package: AdapterFactoryContentProvider$ViewerRefresh

AdapterFactoryContentProvider$ViewerRefresh

nameinstructionbranchcomplexitylinemethod
AdapterFactoryContentProvider.ViewerRefresh(Viewer)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
AdapterFactoryContentProvider.ViewerRefresh(Viewer, IViewerNotification)
M: 13 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
addNotification(IViewerNotification)
M: 63 C: 0
0%
M: 16 C: 0
0%
M: 9 C: 0
0%
M: 18 C: 0
0%
M: 1 C: 0
0%
merge(IViewerNotification, IViewerNotification)
M: 106 C: 0
0%
M: 36 C: 0
0%
M: 19 C: 0
0%
M: 29 C: 0
0%
M: 1 C: 0
0%
refresh(IViewerNotification)
M: 110 C: 0
0%
M: 24 C: 0
0%
M: 13 C: 0
0%
M: 32 C: 0
0%
M: 1 C: 0
0%
run()
M: 42 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /**
2: * Copyright (c) 2002-2006 IBM Corporation and others.
3: *
4: * All rights reserved. This program and the accompanying materials
5: * are made available under the terms of the Eclipse Public License 2.0
6: * which accompanies this distribution, and is available at
7: * https://www.eclipse.org/legal/epl-2.0/
8: *
9: * SPDX-License-Identifier: EPL-2.0
10: *
11: * Contributors:
12: * IBM - Initial API and implementation
13: */
14: // REUSED CLASS
15: package org.eclipse.emf.edit.ui.provider;
16:
17: import java.util.ArrayList;
18: import java.util.Collections;
19: import java.util.Iterator;
20: import java.util.List;
21:
22: import org.eclipse.emf.common.notify.AdapterFactory;
23: import org.eclipse.emf.common.notify.Notification;
24: import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
25: import org.eclipse.emf.edit.domain.EditingDomain;
26: import org.eclipse.emf.edit.provider.IChangeNotifier;
27: import org.eclipse.emf.edit.provider.INotifyChangedListener;
28: import org.eclipse.emf.edit.provider.IStructuredItemContentProvider;
29: import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
30: import org.eclipse.emf.edit.provider.IViewerNotification;
31: import org.eclipse.emf.edit.provider.ViewerNotification;
32: import org.eclipse.jface.viewers.ISelection;
33: import org.eclipse.jface.viewers.IStructuredContentProvider;
34: import org.eclipse.jface.viewers.IStructuredSelection;
35: import org.eclipse.jface.viewers.ITreeContentProvider;
36: import org.eclipse.jface.viewers.StructuredSelection;
37: import org.eclipse.jface.viewers.StructuredViewer;
38: import org.eclipse.jface.viewers.Viewer;
39: import org.eclipse.swt.widgets.Display;
40:
41: /**
42: * This content provider wraps an AdapterFactory
43: * and it delegates its JFace provider interfaces to corresponding adapter-implemented item provider interfaces.
44: * All method calls to the various structured content provider interfaces
45: * are delegated to interfaces implemented by the adapters generated by the AdapterFactory.
46: * {@link org.eclipse.jface.viewers.IStructuredContentProvider} is delegated to {@link IStructuredItemContentProvider};
47: * {@link ITreeContentProvider} is delegated
48: * to {@link ITreeItemContentProvider}.
49: */
50: public class AdapterFactoryContentProvider
51:         implements
52:         ITreeContentProvider,
53:         INotifyChangedListener {
54:         private static final long serialVersionUID = 1L;
55:
56:         /**
57:          * This keeps track of the one factory we are using.
58:          * Use a {@link org.eclipse.emf.edit.provider.ComposedAdapterFactory} if adapters
59:          * from more the one factory are involved in the model.
60:          */
61:         protected AdapterFactory adapterFactory;
62:
63:         /**
64:          * This keeps track of the one viewer using this content provider.
65:          */
66:         protected Viewer viewer;
67:
68:         /**
69:          * This is used to queue viewer notifications and refresh viewers based on them.
70:          *
71:          * @since 2.2.0
72:          */
73:         protected ViewerRefresh viewerRefresh;
74:
75:         private static final Class<?> IStructuredItemContentProviderClass = IStructuredItemContentProvider.class;
76:         private static final Class<?> ITreeItemContentProviderClass = ITreeItemContentProvider.class;
77:
78:         /**
79:          * This constructs an instance that wraps this factory.
80:          * The factory should yield adapters that implement the various IItemContentProvider interfaces.
81:          * If the adapter factory is an {@link IChangeNotifier},
82:          * a listener is added to it,
83:          * so it's important to call {@link #dispose()}.
84:          */
85:         public AdapterFactoryContentProvider(AdapterFactory adapterFactory) {
86:                 this.adapterFactory = adapterFactory;
87:
88:                 if (adapterFactory instanceof IChangeNotifier) {
89:                         ((IChangeNotifier) adapterFactory).addListener(this);
90:                 }
91:         }
92:
93:         /**
94:          * This sets the wrapped factory.
95:          * If the adapter factory is an {@link IChangeNotifier},
96:          * a listener is added to it,
97:          * so it's important to call {@link #dispose()}.
98:          */
99:         public void setAdapterFactory(AdapterFactory adapterFactory) {
100:                 if (this.adapterFactory instanceof IChangeNotifier) {
101:                         ((IChangeNotifier) this.adapterFactory).removeListener(this);
102:                 }
103:
104:                 if (adapterFactory instanceof IChangeNotifier) {
105:                         ((IChangeNotifier) adapterFactory).addListener(this);
106:                 }
107:
108:                 this.adapterFactory = adapterFactory;
109:         }
110:
111:         /**
112:          * This returns the wrapped factory.
113:          */
114:         public AdapterFactory getAdapterFactory() {
115:                 return adapterFactory;
116:         }
117:
118:         /**
119:          * The given Viewer will start (oldInput == null) or stop (newInput == null) listening for domain events.
120:          */
121:         @Override
122:         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
123:                 // If there was no old input, then we must be providing content for this part for the first time...
124:                 //
125:                 this.viewer = viewer;
126:         }
127:
128:         /**
129:          * This implements {@link org.eclipse.jface.viewers.IStructuredContentProvider}.getElements to
130:          * forward the call to an object that implements
131:          * {@link org.eclipse.emf.edit.provider.IStructuredItemContentProvider#getElements
132:          * IStructuredItemContentProvider.getElements}.
133:          */
134:         @Override
135:         public Object[] getElements(Object object) {
136:                 // Get the adapter from the factory.
137:                 //
138:                 final IStructuredItemContentProvider structuredItemContentProvider = (IStructuredItemContentProvider) adapterFactory
139:                         .adapt(object, IStructuredItemContentProviderClass);
140:
141:                 // Either delegate the call or return nothing.
142:                 //
143:                 return (structuredItemContentProvider != null ? structuredItemContentProvider.getElements(object)
144:                         : Collections.EMPTY_LIST).toArray();
145:         }
146:
147:         /**
148:          * This implements {@link org.eclipse.jface.viewers.ITreeContentProvider}.getChildren to forward the call to an
149:          * object that implements {@link org.eclipse.emf.edit.provider.ITreeItemContentProvider#getChildren
150:          * ITreeItemContentProvider.getChildren}.
151:          */
152:         @Override
153:         public Object[] getChildren(Object object) {
154:                 // Get the adapter from the factory.
155:                 //
156:                 final ITreeItemContentProvider treeItemContentProvider = (ITreeItemContentProvider) adapterFactory.adapt(object,
157:                         ITreeItemContentProviderClass);
158:
159:                 // Either delegate the call or return nothing.
160:                 //
161:                 return (treeItemContentProvider != null ? treeItemContentProvider.getChildren(object) : Collections.EMPTY_LIST)
162:                         .toArray();
163:         }
164:
165:         /**
166:          * This implements {@link org.eclipse.jface.viewers.ITreeContentProvider}.hasChildren to forward the call to an
167:          * object that implements {@link org.eclipse.emf.edit.provider.ITreeItemContentProvider#hasChildren
168:          * ITreeItemContentProvider.hasChildren}.
169:          */
170:         @Override
171:         public boolean hasChildren(Object object) {
172:                 // Get the adapter from the factory.
173:                 //
174:                 final ITreeItemContentProvider treeItemContentProvider = (ITreeItemContentProvider) adapterFactory.adapt(object,
175:                         ITreeItemContentProviderClass);
176:
177:                 // Either delegate the call or return nothing.
178:                 //
179:                 return treeItemContentProvider != null &&
180:                         treeItemContentProvider.hasChildren(object);
181:         }
182:
183:         /**
184:          * This implements {@link org.eclipse.jface.viewers.ITreeContentProvider}.getParent to forward the call to an object
185:          * that implements {@link org.eclipse.emf.edit.provider.ITreeItemContentProvider#getParent
186:          * ITreeItemContentProvider.getParent}.
187:          */
188:         @Override
189:         public Object getParent(Object object) {
190:                 // Get the adapter from the factory.
191:                 //
192:                 final ITreeItemContentProvider treeItemContentProvider = (ITreeItemContentProvider) adapterFactory.adapt(object,
193:                         ITreeItemContentProviderClass);
194:
195:                 // Either delegate the call or return nothing.
196:                 //
197:                 return treeItemContentProvider != null ? treeItemContentProvider.getParent(object) : null;
198:         }
199:
200:         /**
201:          * This discards the content provider and removes this as a listener to the {@link #adapterFactory}.
202:          */
203:         @Override
204:         public void dispose() {
205:                 if (adapterFactory instanceof IChangeNotifier) {
206:                         ((IChangeNotifier) adapterFactory).removeListener(this);
207:                 }
208:                 viewer = null;
209:         }
210:
211:         @Override
212:         public void notifyChanged(Notification notification) {
213:                 if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed()) {
214:                         // If the notification is an IViewerNotification, it specifies how ViewerRefresh should behave. Otherwise
215:                         // fall
216:                         // back to NotifyChangedToViewerRefresh, which determines how to refresh the viewer directly from the model
217:                         // notification.
218:                         //
219:                         if (notification instanceof IViewerNotification) {
220:                                 if (viewerRefresh == null) {
221:                                         viewerRefresh = new ViewerRefresh(viewer);
222:                                 }
223:
224:                                 if (viewerRefresh.addNotification((IViewerNotification) notification)) {
225:                                         viewer.getControl().getDisplay().asyncExec(viewerRefresh);
226:                                 }
227:                         } else {
228:                                 NotifyChangedToViewerRefresh.handleNotifyChanged(
229:                                         viewer,
230:                                         notification.getNotifier(),
231:                                         notification.getEventType(),
232:                                         notification.getFeature(),
233:                                         notification.getOldValue(),
234:                                         notification.getNewValue(),
235:                                         notification.getPosition());
236:                         }
237:                 }
238:         }
239:
240:         /**
241:          * A runnable class that efficiently updates a {@link org.eclipse.jface.viewers.Viewer} via standard APIs, based on
242:          * queued {@link org.eclipse.emf.edit.provider.IViewerNotification}s from the model's item providers.
243:          */
244:         public static class ViewerRefresh implements Runnable {
245:                 Viewer viewer;
246:                 List<IViewerNotification> notifications;
247:                 boolean compatibility;
248:
249:                 /**
250:                  * @since 2.2.0
251:                  */
252:                 public ViewerRefresh(Viewer viewer) {
253:                         this.viewer = viewer;
254:                 }
255:
256:                 /**
257:                  * @deprecated in 2.2.0
258:                  */
259:                 @Deprecated
260:                 public ViewerRefresh(Viewer viewer, IViewerNotification notification) {
261:                         this.viewer = viewer;
262:                         addNotification(notification);
263:                         compatibility = true;
264:                 }
265:
266:                 /**
267:                  * Adds a viewer notification to the queue that will be processed by this <code>ViewerRefresh</code>.
268:                  * Duplicative notifications will not be queued.
269:                  *
270:                  * @param notification the notification to add to the queue
271:                  * @return whether the queue has been made non-empty, which would indicate that the <code>ViewerRefresh</code>
272:                  * needs to be {@link Display#asyncExec scheduled} on the event queue
273:                  * @since 2.2.0
274:                  */
275:                 public synchronized boolean addNotification(IViewerNotification notification) {
276:•                        if (notifications == null) {
277:                                 notifications = new ArrayList<IViewerNotification>();
278:                         }
279:
280:•                        if (notifications.isEmpty()) {
281:                                 notifications.add(notification);
282:                                 return true;
283:                         }
284:
285:•                        if (viewer instanceof StructuredViewer) {
286:•                                for (final Iterator<IViewerNotification> i = notifications.iterator(); i.hasNext()
287:•                                        && notification != null;) {
288:                                         final IViewerNotification old = i.next();
289:                                         final IViewerNotification merged = merge(old, notification);
290:•                                        if (merged == old) {
291:                                                 notification = null;
292:•                                        } else if (merged != null) {
293:                                                 notification = merged;
294:                                                 i.remove();
295:                                         }
296:                                 }
297:•                                if (notification != null) {
298:                                         notifications.add(notification);
299:                                 }
300:                         }
301:                         return false;
302:                 }
303:
304:                 /**
305:                  * Compares two notifications and, if duplicative, returns a single notification that does the work of
306:                  * both. Note: this gives priority to a content refresh on the whole viewer over a content refresh or
307:                  * label update on a specific element; however, it doesn't use parent-child relationships to determine
308:                  * if refreshes on non-equal elements are duplicative.
309:                  *
310:                  * @return a single notification that is equivalent to the two parameters, or null if they are non-duplicative
311:                  * @since 2.2.0
312:                  */
313:                 protected IViewerNotification merge(IViewerNotification n1, IViewerNotification n2) {
314:                         // This implements the following order of preference:
315:                         // 1. full refresh and update
316:                         // 2. full refresh (add update if necessary)
317:                         // 3. refresh element with update
318:                         // 4. refresh element (if necessary)
319:                         // 5. update element
320:                         //
321:•                        if (n1.getElement() == null && n1.isLabelUpdate()) {
322:                                 return n1;
323:•                        } else if (n2.getElement() == null && n2.isLabelUpdate()) {
324:                                 return n2;
325:•                        } else if (n1.getElement() == null) {
326:•                                if (n2.isLabelUpdate()) {
327:                                         n1 = new ViewerNotification(n1);
328:                                 }
329:                                 return n1;
330:•                        } else if (n2.getElement() == null) {
331:•                                if (n1.isLabelUpdate()) {
332:                                         n2 = new ViewerNotification(n2);
333:                                 }
334:                                 return n2;
335:•                        } else if (n1.getElement() == n2.getElement()) {
336:•                                if (n1.isContentRefresh() && n1.isLabelUpdate()) {
337:                                         return n1;
338:•                                } else if (n2.isContentRefresh() && n2.isLabelUpdate()) {
339:                                         return n2;
340:•                                } else if (n1.isContentRefresh()) {
341:•                                        if (n2.isLabelUpdate()) {
342:                                                 n1 = new ViewerNotification(n1, n1.getElement(), true, true);
343:                                         }
344:                                         return n1;
345:•                                } else if (n2.isContentRefresh()) {
346:•                                        if (n1.isLabelUpdate()) {
347:                                                 n2 = new ViewerNotification(n2, n2.getElement(), true, true);
348:                                         }
349:                                         return n2;
350:•                                } else if (n1.isLabelUpdate()) {
351:                                         return n1;
352:                                 } else // n2.isLabelUpdate()
353:                                 {
354:                                         return n2;
355:                                 }
356:                         }
357:                         return null;
358:                 }
359:
360:                 @Override
361:                 public void run() {
362:•                        if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed()) {
363:                                 List<IViewerNotification> current;
364:
365:                                 synchronized (this) {
366:                                         current = notifications;
367:                                         notifications = null;
368:                                 }
369:
370:•                                if (current != null) {
371:•                                        for (final IViewerNotification viewerNotification : current) {
372:                                                 refresh(viewerNotification);
373:                                         }
374:                                 }
375:                         }
376:                 }
377:
378:                 /**
379:                  * @since 2.2.0
380:                  */
381:                 protected void refresh(IViewerNotification notification) {
382:                         // Previously, we never updated the viewer on a resolve. Now we post and merge it as appropriate.
383:                         //
384:•                        if (compatibility && notification.getEventType() == Notification.RESOLVE) {
385:                                 return;
386:                         }
387:
388:                         final Object element = notification.getElement();
389:
390:•                        if (viewer instanceof StructuredViewer) {
391:                                 final StructuredViewer structuredViewer = (StructuredViewer) viewer;
392:
393:                                 final ISelection selection = structuredViewer.getSelection();
394:                                 final boolean isStaleSelection = AdapterFactoryEditingDomain.isStale(selection);
395:•                                if (isStaleSelection) {
396:                                         viewer.setSelection(StructuredSelection.EMPTY);
397:                                 }
398:
399:•                                if (element != null) {
400:•                                        if (notification.isContentRefresh()) {
401:                                                 structuredViewer.refresh(element, notification.isLabelUpdate());
402:•                                        } else if (notification.isLabelUpdate()) {
403:                                                 structuredViewer.update(element, null);
404:                                         }
405:                                 } else {
406:                                         structuredViewer.refresh(notification.isLabelUpdate());
407:                                 }
408:
409:•                                if (isStaleSelection) {
410:                                         final Object object = structuredViewer.getInput();
411:                                         EditingDomain editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(object);
412:•                                        if (editingDomain == null) {
413:•                                                for (final Object child : ((IStructuredContentProvider) structuredViewer.getContentProvider())
414:                                                         .getElements(object)) {
415:                                                         editingDomain = AdapterFactoryEditingDomain.getEditingDomainFor(child);
416:•                                                        if (editingDomain != null) {
417:                                                                 break;
418:                                                         }
419:                                                 }
420:                                         }
421:•                                        if (editingDomain instanceof AdapterFactoryEditingDomain) {
422:                                                 structuredViewer
423:                                                         .setSelection(new StructuredSelection(((AdapterFactoryEditingDomain) editingDomain)
424:                                                                 .resolve(((IStructuredSelection) selection).toList())), true);
425:                                         }
426:                                 }
427:                         } else {
428:                                 viewer.refresh();
429:                         }
430:                 }
431:         }
432: }