001: /*******************************************************************************
002: * Copyright (c) 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: ******************************************************************************/package org.eclipse.ui.internal.navigator;
011:
012: import java.util.Arrays;
013: import java.util.Iterator;
014: import java.util.LinkedHashSet;
015: import java.util.Set;
016:
017: import org.eclipse.jface.viewers.TreePath;
018: import org.eclipse.ui.internal.navigator.extensions.NavigatorContentDescriptor;
019: import org.eclipse.ui.internal.navigator.extensions.NavigatorContentExtension;
020: import org.eclipse.ui.navigator.INavigatorContentDescriptor;
021: import org.eclipse.ui.navigator.INavigatorPipelineService;
022: import org.eclipse.ui.navigator.IPipelinedTreeContentProvider;
023: import org.eclipse.ui.navigator.PipelinedShapeModification;
024: import org.eclipse.ui.navigator.PipelinedViewerUpdate;
025:
026: /**
027: * @since 3.2
028: *
029: */
030: public class NavigatorPipelineService implements
031: INavigatorPipelineService {
032:
033: private NavigatorContentService contentService;
034:
035: /**
036: * Create a pipeline assistnat for the given content service.
037: *
038: * @param aContentService
039: * The content service that will drive this pipeline assistant.
040: */
041: public NavigatorPipelineService(
042: NavigatorContentService aContentService) {
043: contentService = aContentService;
044: }
045:
046: /**
047: * Intercept attempts to add elements directly to the viewer.
048: *
049: * <p>
050: * For content extensions that reshape the structure of children in a
051: * viewer, their overridden extensions may sometimes use optimized refreshes
052: * to add elements to the tree. These attempts must be intercepted and
053: * mapped to the correct set of model elements in the overridding extension.
054: * Clients may add, remove, or modify elements in the given set of added
055: * children. Clients should return a set for downstream extensions to
056: * massage further.
057: * </p>
058: * <p>
059: * <b>Clients should not call any of the add, remove, refresh, or update
060: * methods on the viewer from this method or any code invoked by the
061: * implementation of this method.</b>
062: * </p>
063: *
064: * @param anAddModification
065: * The shape modification which contains the current suggested
066: * parent and children. Clients may modify this parameter
067: * directly and return it as the new shape modification.
068: * @return The new shape modification to use. Clients should <b>never</b>
069: * return <b>null</b> from this method.
070: */
071: public PipelinedShapeModification interceptAdd(
072: PipelinedShapeModification anAddModification) {
073:
074: ContributorTrackingSet trackedSet = (ContributorTrackingSet) anAddModification
075: .getChildren();
076:
077: Set contentDescriptors = contentService
078: .findDescriptorsByTriggerPoint(anAddModification
079: .getParent());
080:
081: for (Iterator descriptorsItr = contentDescriptors.iterator(); descriptorsItr
082: .hasNext();) {
083: INavigatorContentDescriptor descriptor = (INavigatorContentDescriptor) descriptorsItr
084: .next();
085: pipelineInterceptAdd(anAddModification, trackedSet,
086: descriptor);
087: }
088:
089: // for consistency, we register the contribution from our best known match
090: registerContribution(anAddModification.getParent(),
091: anAddModification.getChildren().toArray());
092: return anAddModification;
093:
094: }
095:
096: /**
097: * @param parent The object to which data was contributed
098: * @param contributions Data contributed to the viewer
099: */
100: private void registerContribution(Object parent,
101: Object[] contributions) {
102:
103: // returns an array sorted by priority
104: Set possibleContributors = contentService
105: .findDescriptorsByTriggerPoint(parent);
106: Set possibleMatches = null;
107: for (int i = 0; i < contributions.length; i++) {
108: // returns an array sorted by priority
109: possibleMatches = contentService
110: .findDescriptorsWithPossibleChild(contributions[i]);
111: for (Iterator iterator = possibleMatches.iterator(); iterator
112: .hasNext();) {
113: NavigatorContentDescriptor descriptor = (NavigatorContentDescriptor) iterator
114: .next();
115:
116: // terminates once the highest priority match is found for this child
117: if (possibleContributors.contains(descriptor)) {
118: contentService.rememberContribution(descriptor,
119: contributions[i]);
120: break;
121: }
122:
123: }
124: }
125: }
126:
127: private void pipelineInterceptAdd(
128: PipelinedShapeModification anAddModification,
129: ContributorTrackingSet trackedSet,
130: INavigatorContentDescriptor descriptor) {
131: if (descriptor.hasOverridingExtensions()) {
132: Set overridingDescriptors = descriptor
133: .getOverriddingExtensions();
134: for (Iterator overridingDescriptorsItr = overridingDescriptors
135: .iterator(); overridingDescriptorsItr.hasNext();) {
136: INavigatorContentDescriptor overridingDescriptor = (INavigatorContentDescriptor) overridingDescriptorsItr
137: .next();
138: if (contentService.isVisible(overridingDescriptor
139: .getId())
140: && contentService.isActive(overridingDescriptor
141: .getId())) {
142: trackedSet
143: .setContributor((NavigatorContentDescriptor) overridingDescriptor);
144: NavigatorContentExtension extension = contentService
145: .getExtension(overridingDescriptor);
146: ((IPipelinedTreeContentProvider) extension
147: .internalGetContentProvider())
148: .interceptAdd(anAddModification);
149: trackedSet.setContributor(null);
150: pipelineInterceptAdd(anAddModification, trackedSet,
151: overridingDescriptor);
152: }
153: }
154: }
155: }
156:
157: /**
158: * Intercept attempts to remove elements directly from the viewer.
159: *
160: * <p>
161: * For content extensions that reshape the structure of children in a
162: * viewer, their overridden extensions may sometimes use optimized refreshes
163: * to remove elements to the tree. These attempts must be intercepted and
164: * mapped to the correct set of model elements in the overridding extension.
165: * Clients may add, remove, or modify elements in the given set of removed
166: * children. Clients should return a set for downstream extensions to
167: * massage further.
168: * </p>
169: * <p>
170: * <b>Clients should not call any of the add, remove, refresh, or update
171: * methods on the viewer from this method or any code invoked by the
172: * implementation of this method.</b>
173: * </p>
174: *
175: * @param aRemoveModification
176: * The shape modification which contains the current suggested
177: * parent and children. Clients may modify this parameter
178: * directly and return it as the new shape modification.
179: * @return The new shape modification to use. Clients should <b>never</b>
180: * return <b>null</b> from this method.
181: */
182: public PipelinedShapeModification interceptRemove(
183: PipelinedShapeModification aRemoveModification) {
184:
185: ContributorTrackingSet trackedSet = (ContributorTrackingSet) aRemoveModification
186: .getChildren();
187:
188: Set interestedExtensions = new LinkedHashSet();
189: for (Iterator iter = trackedSet.iterator(); iter.hasNext();) {
190: Object element = (Object) iter.next();
191: if (element instanceof TreePath) {
192: interestedExtensions
193: .addAll(contentService
194: .findOverrideableContentExtensionsForPossibleChild(((TreePath) element)
195: .getLastSegment()));
196: } else {
197: interestedExtensions = contentService
198: .findOverrideableContentExtensionsForPossibleChild(element);
199:
200: }
201: }
202: for (Iterator overridingExtensionsIter = interestedExtensions
203: .iterator(); overridingExtensionsIter.hasNext();)
204: pipelineInterceptRemove(
205: aRemoveModification,
206: trackedSet,
207: (NavigatorContentExtension) overridingExtensionsIter
208: .next());
209: return aRemoveModification;
210: }
211:
212: private void pipelineInterceptRemove(
213: PipelinedShapeModification aRemoveModification,
214: ContributorTrackingSet trackedSet,
215: NavigatorContentExtension overrideableExtension) {
216:
217: try {
218: NavigatorContentExtension overridingExtension = null;
219: Set overridingExtensions = new LinkedHashSet();
220: for (Iterator iter = trackedSet.iterator(); iter.hasNext();) {
221: Object element = (Object) iter.next();
222: if (element instanceof TreePath) {
223: overridingExtensions
224: .addAll(Arrays
225: .asList(overrideableExtension
226: .getOverridingExtensionsForPossibleChild(((TreePath) element)
227: .getLastSegment())));
228: } else {
229: overridingExtensions
230: .addAll(Arrays
231: .asList(overrideableExtension
232: .getOverridingExtensionsForPossibleChild(element)));
233: }
234: }
235:
236: for (Iterator extensionsItr = overridingExtensions
237: .iterator(); extensionsItr.hasNext();) {
238: overridingExtension = (NavigatorContentExtension) extensionsItr
239: .next();
240: trackedSet
241: .setContributor((NavigatorContentDescriptor) overridingExtension
242: .getDescriptor());
243: if (overridingExtension.getContentProvider() instanceof IPipelinedTreeContentProvider) {
244: ((IPipelinedTreeContentProvider) overridingExtension
245: .getContentProvider())
246: .interceptRemove(aRemoveModification);
247: }
248: trackedSet.setContributor(null);
249: if (overridingExtension.getDescriptor()
250: .hasOverridingExtensions())
251: pipelineInterceptRemove(aRemoveModification,
252: trackedSet, overridingExtension);
253:
254: }
255:
256: } catch (Throwable e) {
257: String msg = e.getMessage() != null ? e.getMessage() : e
258: .toString();
259: NavigatorPlugin.logError(0, msg, e);
260: }
261: }
262:
263: /**
264: * Intercept calls to viewer <code>refresh()</code> methods.
265: *
266: * <p>
267: * Clients may modify the given update to add or remove the elements to be
268: * refreshed. Clients may return the same instance that was passed in for
269: * the next downstream extension.
270: * </p>
271: *
272: * <p>
273: * <b>Clients should not call any of the add, remove, refresh, or update
274: * methods on the viewer from this method or any code invoked by the
275: * implementation of this method.</b>
276: * </p>
277: *
278: * @param aRefreshSynchronization
279: * The (current) refresh update to execute against the viewer.
280: * @return The (potentially reshaped) refresh to execute against the viewer.
281: */
282: public boolean interceptRefresh(
283: PipelinedViewerUpdate aRefreshSynchronization) {
284:
285: boolean pipelined = false;
286: Object refreshable = null;
287: Set overrideableExtensions = new LinkedHashSet();
288: for (Iterator iter = aRefreshSynchronization
289: .getRefreshTargets().iterator(); iter.hasNext();) {
290: refreshable = iter.next();
291: overrideableExtensions
292: .addAll(contentService
293: .findOverrideableContentExtensionsForPossibleChild(refreshable));
294: }
295: for (Iterator overrideableExtensionItr = overrideableExtensions
296: .iterator(); overrideableExtensionItr.hasNext();) {
297: pipelined |= pipelineInterceptRefresh(
298: (NavigatorContentExtension) overrideableExtensionItr
299: .next(), aRefreshSynchronization,
300: refreshable);
301: }
302:
303: return pipelined;
304:
305: }
306:
307: private boolean pipelineInterceptRefresh(
308: NavigatorContentExtension overrideableExtension,
309: PipelinedViewerUpdate aRefreshSynchronization,
310: Object refreshable) {
311:
312: boolean intercepted = false;
313:
314: NavigatorContentExtension[] overridingExtensionsForPossibleChild = overrideableExtension
315: .getOverridingExtensionsForPossibleChild(refreshable);
316: for (int i = 0; i < overridingExtensionsForPossibleChild.length; i++) {
317: try {
318: if (overridingExtensionsForPossibleChild[i]
319: .getContentProvider() instanceof IPipelinedTreeContentProvider) {
320:
321: intercepted |= ((IPipelinedTreeContentProvider) overridingExtensionsForPossibleChild[i]
322: .getContentProvider())
323: .interceptRefresh(aRefreshSynchronization);
324:
325: if (overridingExtensionsForPossibleChild[i]
326: .getDescriptor().hasOverridingExtensions())
327: intercepted |= pipelineInterceptRefresh(
328: overridingExtensionsForPossibleChild[i],
329: aRefreshSynchronization, refreshable);
330: }
331: } catch (Throwable e) {
332: String msg = e.getMessage() != null ? e.getMessage()
333: : e.toString();
334: NavigatorPlugin.logError(0, msg, e);
335: }
336: }
337:
338: return intercepted;
339: }
340:
341: /**
342: * Intercept calls to viewer <code>update()</code> methods.
343: *
344: * <p>
345: * Clients may modify the given update to add or remove the elements to be
346: * updated. Clients may also add or remove properties for the given targets
347: * to optimize the refresh. Clients may return the same instance that was
348: * passed in for the next downstream extension.
349: * </p>
350: *
351: * <p>
352: * <b>Clients should not call any of the add, remove, refresh, or update
353: * methods on the viewer from this method or any code invoked by the
354: * implementation of this method.</b>
355: * </p>
356: *
357: * @param anUpdateSynchronization
358: * The (current) update to execute against the viewer.
359: * @return The (potentially reshaped) update to execute against the viewer.
360: */
361: public boolean interceptUpdate(
362: PipelinedViewerUpdate anUpdateSynchronization) {
363:
364: boolean pipelined = false;
365: Object refreshable = null;
366:
367: Set overrideableExtensions = new LinkedHashSet();
368: for (Iterator iter = anUpdateSynchronization
369: .getRefreshTargets().iterator(); iter.hasNext();) {
370: refreshable = iter.next();
371: overrideableExtensions
372: .addAll(contentService
373: .findOverrideableContentExtensionsForPossibleChild(refreshable));
374: }
375: for (Iterator overrideableExtensionItr = overrideableExtensions
376: .iterator(); overrideableExtensionItr.hasNext();) {
377: pipelined |= pipelineInterceptUpdate(
378: (NavigatorContentExtension) overrideableExtensionItr
379: .next(), anUpdateSynchronization,
380: refreshable);
381: }
382:
383: return pipelined;
384:
385: }
386:
387: private boolean pipelineInterceptUpdate(
388: NavigatorContentExtension overrideableExtension,
389: PipelinedViewerUpdate anUpdateSynchronization,
390: Object refreshable) {
391:
392: boolean intercepted = false;
393: NavigatorContentExtension[] overridingExtensionsForPossibleChild = overrideableExtension
394: .getOverridingExtensionsForPossibleChild(refreshable);
395: for (int i = 0; i < overridingExtensionsForPossibleChild.length; i++) {
396: try {
397: if (overridingExtensionsForPossibleChild[i]
398: .getContentProvider() instanceof IPipelinedTreeContentProvider) {
399:
400: intercepted |= ((IPipelinedTreeContentProvider) overridingExtensionsForPossibleChild[i]
401: .getContentProvider())
402: .interceptUpdate(anUpdateSynchronization);
403:
404: if (overridingExtensionsForPossibleChild[i]
405: .getDescriptor().hasOverridingExtensions())
406: intercepted |= pipelineInterceptUpdate(
407: overridingExtensionsForPossibleChild[i],
408: anUpdateSynchronization, refreshable);
409: }
410: } catch (Throwable e) {
411: String msg = e.getMessage() != null ? e.getMessage()
412: : e.toString();
413: NavigatorPlugin.logError(0, msg, e);
414: }
415: }
416:
417: return intercepted;
418: }
419:
420: }
|