001: /*******************************************************************************
002: * Copyright (c) 2003, 2007 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.ArrayList;
013: import java.util.Collections;
014: import java.util.Iterator;
015: import java.util.LinkedHashSet;
016: import java.util.LinkedList;
017: import java.util.List;
018: import java.util.Set;
019:
020: import org.eclipse.jface.viewers.ITreeContentProvider;
021: import org.eclipse.jface.viewers.ITreePathContentProvider;
022: import org.eclipse.jface.viewers.TreePath;
023: import org.eclipse.jface.viewers.Viewer;
024: import org.eclipse.osgi.util.NLS;
025: import org.eclipse.ui.internal.navigator.extensions.NavigatorContentDescriptor;
026: import org.eclipse.ui.internal.navigator.extensions.NavigatorContentExtension;
027: import org.eclipse.ui.internal.navigator.extensions.NavigatorViewerDescriptor;
028: import org.eclipse.ui.internal.navigator.extensions.OverridePolicy;
029: import org.eclipse.ui.navigator.CommonViewer;
030: import org.eclipse.ui.navigator.INavigatorContentDescriptor;
031: import org.eclipse.ui.navigator.INavigatorViewerDescriptor;
032: import org.eclipse.ui.navigator.IPipelinedTreeContentProvider;
033:
034: /**
035: * <p>
036: * Provides relevant content based on the associated
037: * {@link org.eclipse.ui.internal.navigator.NavigatorContentService} for
038: * a TreeViewer .
039: * </p>
040: * <p>
041: * Except for the dependency on
042: * {@link org.eclipse.ui.internal.navigator.NavigatorContentService}, this
043: * class has no dependencies on the rest of the Common Navigator framework. Tree
044: * viewers that would like to use the extensions defined by the Common
045: * Navigator, without using the actual view part or other pieces of
046: * functionality (filters, sorting, etc) may choose to use this class, in effect
047: * using an extensible, aggregating, delegate content provider.
048: * </p>
049: *
050: * @see org.eclipse.ui.internal.navigator.NavigatorContentService
051: * @see org.eclipse.ui.internal.navigator.NavigatorContentServiceLabelProvider
052: *
053: * <p>
054: * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
055: * part of a work in progress. There is a guarantee neither that this API will
056: * work nor that it will remain the same. Please do not use this API without
057: * consulting with the Platform/UI team.
058: * </p>
059: *
060: * @since 3.2
061: *
062: */
063: public class NavigatorContentServiceContentProvider implements
064: ITreeContentProvider, ITreePathContentProvider {
065:
066: private static final Object[] NO_CHILDREN = new Object[0];
067:
068: private final NavigatorContentService contentService;
069:
070: private final boolean isContentServiceSelfManaged;
071:
072: private final boolean enforceHasChildren;
073:
074: private Viewer viewer;
075:
076: /**
077: * <p>
078: * Creates a cached {@link NavigatorContentService} from the given
079: * viewer Id.
080: * </p>
081: *
082: * @param aViewerId
083: * The associated viewer id that this
084: * NavigatorContentServiceContentProvider will provide content
085: * for
086: */
087: public NavigatorContentServiceContentProvider(String aViewerId) {
088: super ();
089: contentService = new NavigatorContentService(aViewerId);
090: INavigatorViewerDescriptor vDesc = contentService
091: .getViewerDescriptor();
092: enforceHasChildren = vDesc
093: .getBooleanConfigProperty(NavigatorViewerDescriptor.PROP_ENFORCE_HAS_CHILDREN);
094: isContentServiceSelfManaged = true;
095: }
096:
097: /**
098: * <p>
099: * Uses the supplied content service to acquire the available extensions.
100: * </p>
101: *
102: * @param aContentService
103: * The associated NavigatorContentService that should be used to
104: * acquire information.
105: */
106: public NavigatorContentServiceContentProvider(
107: NavigatorContentService aContentService) {
108: super ();
109: contentService = aContentService;
110: isContentServiceSelfManaged = false;
111: INavigatorViewerDescriptor vDesc = contentService
112: .getViewerDescriptor();
113: enforceHasChildren = vDesc
114: .getBooleanConfigProperty(NavigatorViewerDescriptor.PROP_ENFORCE_HAS_CHILDREN);
115: }
116:
117: /**
118: *
119: * <p>
120: * Return the root objects for the supplied anInputElement. anInputElement
121: * is the root thing that the viewer visualizes.
122: * </p>
123: * <p>
124: * This method will call out to its {@link NavigatorContentService} for
125: * extensions that are enabled on the supplied anInputElement or enabled on
126: * the viewerId supplied when the {@link NavigatorContentService} was
127: * created (either by this class or its client). The extensions will then be
128: * queried for relevant content. The children returned from each extension
129: * will be aggregated and returned as is -- there is no additional sorting
130: * or filtering at this level.
131: * </p>
132: * <p>
133: * The results of this method will be displayed in the root of the
134: * TreeViewer.
135: * </p>
136: * {@inheritDoc}
137: *
138: * @param anInputElement
139: * The relevant element that a client would like children for -
140: * the input element of the TreeViewer
141: * @return A non-null array of objects that are logical children of
142: * anInputElement
143: * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
144: */
145: public synchronized Object[] getElements(Object anInputElement) {
146: Set rootContentExtensions = contentService
147: .findRootContentExtensions(anInputElement);
148: if (rootContentExtensions.size() == 0) {
149: return NO_CHILDREN;
150: }
151: ContributorTrackingSet finalElementsSet = new ContributorTrackingSet(
152: contentService);
153: ContributorTrackingSet localSet = new ContributorTrackingSet(
154: contentService);
155:
156: Object[] contributedChildren = null;
157: NavigatorContentExtension foundExtension;
158: NavigatorContentExtension[] overridingExtensions;
159: for (Iterator itr = rootContentExtensions.iterator(); itr
160: .hasNext();) {
161: foundExtension = (NavigatorContentExtension) itr.next();
162: try {
163:
164: if (!isOverridingExtensionInSet(foundExtension
165: .getDescriptor(), rootContentExtensions)) {
166:
167: contributedChildren = foundExtension
168: .internalGetContentProvider().getElements(
169: anInputElement);
170:
171: localSet.setContents(contributedChildren);
172:
173: overridingExtensions = foundExtension
174: .getOverridingExtensionsForTriggerPoint(anInputElement);
175:
176: if (overridingExtensions.length > 0) {
177: localSet = pipelineElements(anInputElement,
178: overridingExtensions, localSet);
179: }
180: finalElementsSet.addAll(localSet);
181: }
182: } catch (RuntimeException re) {
183: NavigatorPlugin
184: .logError(
185: 0,
186: NLS
187: .bind(
188: CommonNavigatorMessages.Could_not_provide_children_for_element,
189: new Object[] { foundExtension
190: .getDescriptor()
191: .getId() }), re);
192: } catch (Error e) {
193: NavigatorPlugin
194: .logError(
195: 0,
196: NLS
197: .bind(
198: CommonNavigatorMessages.Could_not_provide_children_for_element,
199: new Object[] { foundExtension
200: .getDescriptor()
201: .getId() }), e);
202:
203: }
204: }
205: return finalElementsSet.toArray();
206: }
207:
208: /**
209: * <p>
210: * Return the children of the supplied aParentElement
211: * </p>
212: *
213: * <p>
214: * This method will call out to its {@link NavigatorContentService} for
215: * extensions that are enabled on the supplied aParentElement. The
216: * extensions will then be queried for children for aParentElement. The
217: * children returned from each extension will be aggregated and returned as
218: * is -- there is no additional sorting or filtering at this level.
219: * </p>
220: * <p>
221: * The results of this method will be displayed as children of the supplied
222: * element in the TreeViewer.
223: * </p>
224: * {@inheritDoc}
225: *
226: * @param aParentElement
227: * An element that requires children content in the viewer (e.g.
228: * an end-user expanded a node)
229: * @return A non-null array of objects that are logical children of
230: * aParentElement
231: * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
232: */
233: public synchronized Object[] getChildren(Object aParentElement) {
234: return internalGetChildren(aParentElement);
235: }
236:
237: private Object[] internalGetChildren(Object aParentElementOrPath) {
238: Object aParentElement = internalAsElement(aParentElementOrPath);
239: Set enabledExtensions = contentService
240: .findContentExtensionsByTriggerPoint(aParentElement);
241: if (enabledExtensions.size() == 0) {
242: return NO_CHILDREN;
243: }
244: ContributorTrackingSet finalChildrenSet = new ContributorTrackingSet(
245: contentService);
246: ContributorTrackingSet localSet = new ContributorTrackingSet(
247: contentService);
248:
249: Object[] contributedChildren = null;
250: NavigatorContentExtension foundExtension;
251: NavigatorContentExtension[] overridingExtensions;
252: for (Iterator itr = enabledExtensions.iterator(); itr.hasNext();) {
253: foundExtension = (NavigatorContentExtension) itr.next();
254: try {
255:
256: if (!isOverridingExtensionInSet(foundExtension
257: .getDescriptor(), enabledExtensions)) {
258:
259: contributedChildren = foundExtension
260: .internalGetContentProvider().getChildren(
261: aParentElementOrPath);
262:
263: overridingExtensions = foundExtension
264: .getOverridingExtensionsForTriggerPoint(aParentElement);
265:
266: localSet.setContents(contributedChildren);
267:
268: if (overridingExtensions.length > 0) {
269: // TODO: could pass tree path through pipeline
270: localSet = pipelineChildren(aParentElement,
271: overridingExtensions, localSet);
272: }
273: finalChildrenSet.addAll(localSet);
274: }
275: } catch (RuntimeException re) {
276: NavigatorPlugin
277: .logError(
278: 0,
279: NLS
280: .bind(
281: CommonNavigatorMessages.Could_not_provide_children_for_element,
282: new Object[] { foundExtension
283: .getDescriptor()
284: .getId() }), re);
285: } catch (Error e) {
286: NavigatorPlugin
287: .logError(
288: 0,
289: NLS
290: .bind(
291: CommonNavigatorMessages.Could_not_provide_children_for_element,
292: new Object[] { foundExtension
293: .getDescriptor()
294: .getId() }), e);
295:
296: }
297: }
298: return finalChildrenSet.toArray();
299: }
300:
301: /**
302: * Query each of <code>theOverridingExtensions</code> for children, and
303: * then pipe them through the Pipeline content provider.
304: *
305: * @param aParentOrPath
306: * The parent element in the tree
307: * @param theOverridingExtensions
308: * The set of overriding extensions that should participate in
309: * the pipeline chain
310: * @param theCurrentChildren
311: * The current children to return to the viewer (should be
312: * modifiable)
313: * @return The set of children to return to the viewer
314: */
315: private ContributorTrackingSet pipelineChildren(
316: Object aParentOrPath,
317: NavigatorContentExtension[] theOverridingExtensions,
318: ContributorTrackingSet theCurrentChildren) {
319: IPipelinedTreeContentProvider pipelinedContentProvider;
320: NavigatorContentExtension[] overridingExtensions;
321: ContributorTrackingSet pipelinedChildren = theCurrentChildren;
322: for (int i = 0; i < theOverridingExtensions.length; i++) {
323:
324: if (theOverridingExtensions[i].getContentProvider() instanceof IPipelinedTreeContentProvider) {
325: pipelinedContentProvider = (IPipelinedTreeContentProvider) theOverridingExtensions[i]
326: .getContentProvider();
327:
328: pipelinedChildren
329: .setContributor((NavigatorContentDescriptor) theOverridingExtensions[i]
330: .getDescriptor());
331: pipelinedContentProvider.getPipelinedChildren(
332: aParentOrPath, pipelinedChildren);
333:
334: pipelinedChildren.setContributor(null);
335:
336: overridingExtensions = theOverridingExtensions[i]
337: .getOverridingExtensionsForTriggerPoint(aParentOrPath);
338: if (overridingExtensions.length > 0) {
339: pipelinedChildren = pipelineChildren(aParentOrPath,
340: overridingExtensions, pipelinedChildren);
341: }
342: }
343: }
344:
345: return pipelinedChildren;
346:
347: }
348:
349: /**
350: * Query each of <code>theOverridingExtensions</code> for elements, and
351: * then pipe them through the Pipeline content provider.
352: *
353: * @param anInputElement
354: * The input element in the tree
355: * @param theOverridingExtensions
356: * The set of overriding extensions that should participate in
357: * the pipeline chain
358: * @param theCurrentElements
359: * The current elements to return to the viewer (should be
360: * modifiable)
361: * @return The set of elements to return to the viewer
362: */
363: private ContributorTrackingSet pipelineElements(
364: Object anInputElement,
365: NavigatorContentExtension[] theOverridingExtensions,
366: ContributorTrackingSet theCurrentElements) {
367: IPipelinedTreeContentProvider pipelinedContentProvider;
368: NavigatorContentExtension[] overridingExtensions;
369: ContributorTrackingSet pipelinedElements = theCurrentElements;
370: for (int i = 0; i < theOverridingExtensions.length; i++) {
371:
372: if (theOverridingExtensions[i].getContentProvider() instanceof IPipelinedTreeContentProvider) {
373: pipelinedContentProvider = (IPipelinedTreeContentProvider) theOverridingExtensions[i]
374: .getContentProvider();
375:
376: pipelinedElements
377: .setContributor((NavigatorContentDescriptor) theOverridingExtensions[i]
378: .getDescriptor());
379: pipelinedContentProvider.getPipelinedElements(
380: anInputElement, pipelinedElements);
381: pipelinedElements.setContributor(null);
382:
383: overridingExtensions = theOverridingExtensions[i]
384: .getOverridingExtensionsForTriggerPoint(anInputElement);
385: if (overridingExtensions.length > 0) {
386: pipelinedElements = pipelineElements(
387: anInputElement, overridingExtensions,
388: pipelinedElements);
389: }
390: }
391: }
392: return pipelinedElements;
393: }
394:
395: /**
396: * Currently this method only checks one level deep. If the suppressed
397: * extension of the given descriptor is contained lower in the tree, then
398: * the extension could still be invoked twice.
399: *
400: * @param aDescriptor
401: * The descriptor which may be overriding other extensions.
402: * @param theEnabledExtensions
403: * The other available extensions.
404: * @return True if the results should be pipelined through the downstream
405: * extensions.
406: */
407: private boolean isOverridingExtensionInSet(
408: INavigatorContentDescriptor aDescriptor,
409: Set theEnabledExtensions) {
410:
411: if (aDescriptor.getSuppressedExtensionId() != null /*
412: * The descriptor is
413: * an override
414: * descriptor
415: */
416: && aDescriptor.getOverridePolicy() == OverridePolicy.InvokeAlwaysRegardlessOfSuppressedExt) {
417: /*
418: * if the policy is set as such, it can lead to this extension being
419: * invoked twice; once as a first class extension, and once an
420: * overriding extension.
421: */
422: if (theEnabledExtensions
423: .contains(contentService.getExtension(aDescriptor
424: .getOverriddenDescriptor()))) {
425: return true;
426: }
427: }
428: return false;
429: }
430:
431: /**
432: * Currently this method only checks one level deep. If the suppressed
433: * extension of the given descriptor is contained lower in the tree, then
434: * the extension could still be invoked twice.
435: *
436: * @param aDescriptor
437: * The descriptor which may be overriding other extensions.
438: * @param theEnabledDescriptors
439: * The other available descriptors.
440: * @return True if the results should be pipelined through the downstream
441: * extensions.
442: */
443: private boolean isOverridingDescriptorInSet(
444: INavigatorContentDescriptor aDescriptor,
445: Set theEnabledDescriptors) {
446:
447: if (aDescriptor.getSuppressedExtensionId() != null /*
448: * The descriptor is
449: * an override
450: * descriptor
451: */
452: && aDescriptor.getOverridePolicy() == OverridePolicy.InvokeAlwaysRegardlessOfSuppressedExt) {
453: /*
454: * if the policy is set as such, it can lead to this extension being
455: * invoked twice; once as a first class extension, and once an
456: * overriding extension.
457: */
458: if (theEnabledDescriptors.contains(aDescriptor
459: .getOverriddenDescriptor())) {
460: return true;
461: }
462: }
463: return false;
464: }
465:
466: /**
467: * <p>
468: * Returns the logical parent of anElement.
469: * </p>
470: * <p>
471: * This method requires that any extension that would like an opportunity to
472: * supply a parent for anElement expressly indicate that in the action
473: * expression <enables> statement of the
474: * <b>org.eclipse.ui.navigator.navigatorContent </b> extension point.
475: * </p>
476: * {@inheritDoc}
477: *
478: * @param anElement
479: * An element that requires its logical parent - generally as a
480: * result of setSelection(expand=true) on the viewer
481: * @return The logical parent if available or null if the parent cannot be
482: * determined
483: * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
484: */
485: public synchronized Object getParent(Object anElement) {
486: Set extensions = contentService
487: .findContentExtensionsWithPossibleChild(anElement);
488:
489: Object parent;
490: NavigatorContentExtension foundExtension;
491: NavigatorContentExtension[] overridingExtensions;
492: for (Iterator itr = extensions.iterator(); itr.hasNext();) {
493: foundExtension = (NavigatorContentExtension) itr.next();
494: try {
495:
496: if (!isOverridingExtensionInSet(foundExtension
497: .getDescriptor(), extensions)) {
498:
499: parent = foundExtension
500: .internalGetContentProvider().getParent(
501: anElement);
502:
503: overridingExtensions = foundExtension
504: .getOverridingExtensionsForPossibleChild(anElement);
505:
506: if (overridingExtensions.length > 0) {
507: parent = pipelineParent(anElement,
508: overridingExtensions, parent);
509: }
510:
511: if (parent != null) {
512: return parent;
513: }
514: }
515: } catch (RuntimeException re) {
516: NavigatorPlugin
517: .logError(
518: 0,
519: NLS
520: .bind(
521: CommonNavigatorMessages.Could_not_provide_children_for_element,
522: new Object[] { foundExtension
523: .getDescriptor()
524: .getId() }), re);
525: } catch (Error e) {
526: NavigatorPlugin
527: .logError(
528: 0,
529: NLS
530: .bind(
531: CommonNavigatorMessages.Could_not_provide_children_for_element,
532: new Object[] { foundExtension
533: .getDescriptor()
534: .getId() }), e);
535:
536: }
537: }
538:
539: return null;
540: }
541:
542: /**
543: * Query each of <code>theOverridingExtensions</code> for elements, and
544: * then pipe them through the Pipeline content provider.
545: *
546: * @param anInputElement
547: * The input element in the tree
548: * @param theOverridingExtensions
549: * The set of overriding extensions that should participate in
550: * the pipeline chain
551: * @param theCurrentParent
552: * The current elements to return to the viewer (should be
553: * modifiable)
554: * @return The set of elements to return to the viewer
555: */
556: private Object pipelineParent(Object anInputElement,
557: NavigatorContentExtension[] theOverridingExtensions,
558: Object theCurrentParent) {
559: IPipelinedTreeContentProvider pipelinedContentProvider;
560: NavigatorContentExtension[] overridingExtensions;
561: Object aSuggestedParent = null;
562: for (int i = 0; i < theOverridingExtensions.length; i++) {
563:
564: if (theOverridingExtensions[i].getContentProvider() instanceof IPipelinedTreeContentProvider) {
565: pipelinedContentProvider = (IPipelinedTreeContentProvider) theOverridingExtensions[i]
566: .getContentProvider();
567:
568: aSuggestedParent = pipelinedContentProvider
569: .getPipelinedParent(anInputElement,
570: aSuggestedParent);
571:
572: overridingExtensions = theOverridingExtensions[i]
573: .getOverridingExtensionsForTriggerPoint(anInputElement);
574: if (overridingExtensions.length > 0) {
575: aSuggestedParent = pipelineParent(anInputElement,
576: overridingExtensions, aSuggestedParent);
577: }
578: }
579: }
580: return aSuggestedParent != null ? aSuggestedParent
581: : theCurrentParent;
582: }
583:
584: /**
585: * <p>
586: * Used to determine of anElement should be displayed with a '+' or not.
587: * </p>
588: * {@inheritDoc}
589: *
590: * @param anElement
591: * The element in question
592: * @return True if anElement has logical children as returned by this
593: * content provider.
594: * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
595: */
596: public synchronized boolean hasChildren(Object anElement) {
597: Set resultInstances = contentService
598: .findContentExtensionsByTriggerPoint(anElement);
599:
600: NavigatorContentExtension ext;
601: for (Iterator itr = resultInstances.iterator(); itr.hasNext();) {
602: ext = (NavigatorContentExtension) itr.next();
603: if (!ext.isLoaded() && !enforceHasChildren) {
604: return true;
605: } else if (ext.internalGetContentProvider().hasChildren(
606: anElement)) {
607: return true;
608: }
609: }
610:
611: return false;
612: }
613:
614: /**
615: * <p>
616: * Handles any necessary clean up of the {@link NavigatorContentService}
617: * </p>
618: *
619: * <p>
620: * <b>If a client uses this class outside of the framework of
621: * {@link CommonViewer}, the client must ensure that this method is called
622: * when finished. </b>
623: * </p>
624: *
625: * @see org.eclipse.jface.viewers.IContentProvider#dispose()
626: */
627: public synchronized void dispose() {
628: if (isContentServiceSelfManaged) {
629: contentService.dispose();
630: }
631: }
632:
633: /**
634: * <p>
635: * Indicates that the current content provider is now representing a
636: * different input element. The input element is the root thing that the
637: * viewer displays.
638: * </p>
639: * <p>
640: * This method should handle any cleanup associated with the old input
641: * element and any initiailization associated with the new input element.
642: * </p>
643: * {@inheritDoc}
644: *
645: * @param aViewer
646: * The viewer that the current content provider is associated
647: * with
648: * @param anOldInput
649: * The original input element that the viewer was visualizing
650: * @param aNewInput
651: * The new input element that the viewer will visualize.
652: * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
653: * java.lang.Object, java.lang.Object)
654: *
655: */
656: public synchronized void inputChanged(Viewer aViewer,
657: Object anOldInput, Object aNewInput) {
658: viewer = aViewer;
659: contentService.updateService(aViewer, anOldInput, aNewInput);
660: }
661:
662: /* (non-Javadoc)
663: * @see org.eclipse.jface.viewers.ITreePathContentProvider#getChildren(org.eclipse.jface.viewers.TreePath)
664: */
665: public Object[] getChildren(TreePath parentPath) {
666: return internalGetChildren(parentPath);
667: }
668:
669: /* (non-Javadoc)
670: * @see org.eclipse.jface.viewers.ITreePathContentProvider#hasChildren(org.eclipse.jface.viewers.TreePath)
671: */
672: public boolean hasChildren(TreePath path) {
673: Object anElement = internalAsElement(path);
674: Set resultInstances = contentService
675: .findContentExtensionsByTriggerPoint(anElement);
676:
677: NavigatorContentExtension ext;
678: for (Iterator itr = resultInstances.iterator(); itr.hasNext();) {
679: ext = (NavigatorContentExtension) itr.next();
680: if (!ext.isLoaded() && !enforceHasChildren)
681: return true;
682: ITreeContentProvider cp = ext.internalGetContentProvider();
683: if (cp instanceof ITreePathContentProvider) {
684: ITreePathContentProvider tpcp = (ITreePathContentProvider) cp;
685: if (tpcp.hasChildren(path)) {
686: return true;
687: }
688: } else if (cp.hasChildren(anElement))
689: return true;
690: }
691:
692: return false;
693: }
694:
695: /* (non-Javadoc)
696: * @see org.eclipse.jface.viewers.ITreePathContentProvider#getParents(java.lang.Object)
697: */
698: public TreePath[] getParents(Object anElement) {
699:
700: List paths = new ArrayList();
701: TreePathCompiler compiler = new TreePathCompiler(anElement);
702: Set compilers = findPaths(compiler);
703: for (Iterator iter = compilers.iterator(); iter.hasNext();) {
704: TreePathCompiler c = (TreePathCompiler) iter.next();
705: paths.add(c.createParentPath());
706:
707: }
708: return (TreePath[]) paths.toArray(new TreePath[paths.size()]);
709:
710: }
711:
712: /**
713: * Get the element from an element or tree path argument.
714: * @param parentElementOrPath the element or tree path
715: * @return the element
716: */
717: private Object internalAsElement(Object parentElementOrPath) {
718: if (parentElementOrPath instanceof TreePath) {
719: TreePath tp = (TreePath) parentElementOrPath;
720: if (tp.getSegmentCount() > 0) {
721: return tp.getLastSegment();
722: }
723: // If the path is empty, the parent element is the root
724: return viewer.getInput();
725: }
726: return parentElementOrPath;
727: }
728:
729: class CyclicPathException extends Exception {
730:
731: private static final long serialVersionUID = 2111962579612444989L;
732:
733: protected CyclicPathException(TreePathCompiler compiler,
734: Object invalidSegment, boolean asChild) {
735: super ("Cannot add " + invalidSegment + //$NON-NLS-1$
736: " to the list of segments in " + compiler + //$NON-NLS-1$
737: (asChild ? " as a child." : " as a parent.")); //$NON-NLS-1$ //$NON-NLS-2$
738: }
739: }
740:
741: class TreePathCompiler {
742:
743: private final LinkedList segments = new LinkedList();
744:
745: protected TreePathCompiler(Object segment) {
746: segments.add(segment);
747: }
748:
749: protected TreePathCompiler(TreePathCompiler aCompiler) {
750: segments.addAll(aCompiler.segments);
751: }
752:
753: protected TreePathCompiler(TreePath aPath) {
754: for (int i = 0; i < aPath.getSegmentCount(); i++) {
755: segments.addLast(aPath.getSegment(i));
756: }
757: }
758:
759: protected void addParent(Object segment)
760: throws CyclicPathException {
761: if (segments.contains(segment)) {
762: throw new CyclicPathException(this , segment, false);
763: }
764: segments.addFirst(segment);
765: }
766:
767: protected void addChild(Object segment)
768: throws CyclicPathException {
769: if (segments.contains(segment)) {
770: throw new CyclicPathException(this , segment, false);
771: }
772: segments.addLast(segment);
773: }
774:
775: /**
776: * Create the full tree path.
777: *
778: * @return A TreePath with all segments from the compiler.
779: */
780: public TreePath createPath() {
781: return new TreePath(segments.toArray());
782: }
783:
784: /**
785: * Create parent tree path.
786: *
787: * @return A TreePath with all segments but the last from the compiler
788: */
789: public TreePath createParentPath() {
790: LinkedList parentSegments = new LinkedList(segments);
791: parentSegments.removeLast();
792: return new TreePath(parentSegments.toArray());
793: }
794:
795: public Object getLastSegment() {
796: return segments.getLast();
797: }
798:
799: public Object getFirstSegment() {
800: return segments.getFirst();
801: }
802:
803: /* (non-Javadoc)
804: * @see java.lang.Object#toString()
805: */
806: public String toString() {
807:
808: StringBuffer buffer = new StringBuffer();
809: for (Iterator iter = segments.iterator(); iter.hasNext();) {
810: Object segment = iter.next();
811: buffer.append(segment).append("::"); //$NON-NLS-1$
812: }
813: return buffer.toString();
814: }
815:
816: }
817:
818: private Set findPaths(TreePathCompiler aPathCompiler) {
819:
820: Set/* <Object> */parents = findParents(aPathCompiler
821: .getFirstSegment());
822: Set/* <TreePathCompiler> */parentPaths = new LinkedHashSet();
823: Set/* <TreePathCompiler> */foundPaths = Collections.EMPTY_SET;
824: if (parents.size() > 0) {
825: for (Iterator parentIter = parents.iterator(); parentIter
826: .hasNext();) {
827: Object parent = (Object) parentIter.next();
828: TreePathCompiler c = new TreePathCompiler(aPathCompiler);
829: try {
830: c.addParent(parent);
831: foundPaths = findPaths(c);
832: } catch (CyclicPathException cpe) {
833: String msg = cpe.getMessage() != null ? cpe
834: .getMessage() : cpe.toString();
835: NavigatorPlugin.logError(0, msg, cpe);
836: }
837: if (foundPaths.isEmpty())
838: parentPaths.add(c);
839: else
840: parentPaths.addAll(foundPaths);
841: }
842: }
843: return parentPaths;
844:
845: }
846:
847: private Set findParents(Object anElement) {
848:
849: Set descriptors = contentService
850: .findDescriptorsWithPossibleChild(anElement, false);
851: Set parents = new LinkedHashSet();
852: NavigatorContentDescriptor foundDescriptor;
853: NavigatorContentExtension foundExtension;
854: Object parent = null;
855: for (Iterator itr = descriptors.iterator(); itr.hasNext();) {
856: foundDescriptor = (NavigatorContentDescriptor) itr.next();
857: foundExtension = contentService
858: .getExtension(foundDescriptor);
859: try {
860:
861: if (!isOverridingDescriptorInSet(foundExtension
862: .getDescriptor(), descriptors)) {
863:
864: /* internalGetContentProvider returns the real delegate */
865: if (foundExtension.getContentProvider() instanceof ITreePathContentProvider) {
866: /*
867: * but we use the safe version to automatically handle
868: * errors
869: */
870: TreePath[] parentTreePaths = ((ITreePathContentProvider) foundExtension
871: .internalGetContentProvider())
872: .getParents(anElement);
873:
874: for (int i = 0; i < parentTreePaths.length; i++) {
875:
876: parent = parentTreePaths[i]
877: .getLastSegment();
878: if ((parent = findParent(foundExtension,
879: anElement, parent)) != null)
880: parents.add(parent);
881: }
882:
883: } else {
884:
885: parent = foundExtension
886: .internalGetContentProvider()
887: .getParent(anElement);
888: if ((parent = findParent(foundExtension,
889: anElement, parent)) != null)
890: parents.add(parent);
891: }
892: }
893:
894: } catch (RuntimeException re) {
895: NavigatorPlugin
896: .logError(
897: 0,
898: NLS
899: .bind(
900: CommonNavigatorMessages.Could_not_provide_children_for_element,
901: new Object[] { foundExtension
902: .getDescriptor()
903: .getId() }), re);
904: } catch (Error e) {
905: NavigatorPlugin
906: .logError(
907: 0,
908: NLS
909: .bind(
910: CommonNavigatorMessages.Could_not_provide_children_for_element,
911: new Object[] { foundExtension
912: .getDescriptor()
913: .getId() }), e);
914:
915: }
916: }
917:
918: return parents;
919:
920: }
921:
922: private Object findParent(NavigatorContentExtension anExtension,
923: Object anElement, Object aSuggestedParent) {
924:
925: /* the last valid (non-null) parent for the anElement */
926: Object lastValidParent = aSuggestedParent;
927: /* used to keep track of new suggestions */
928: Object suggestedOverriddenParent = null;
929: IPipelinedTreeContentProvider piplineContentProvider;
930: NavigatorContentExtension[] overridingExtensions = anExtension
931: .getOverridingExtensionsForPossibleChild(anElement);
932: for (int i = 0; i < overridingExtensions.length; i++) {
933: if (overridingExtensions[i].getContentProvider() instanceof IPipelinedTreeContentProvider) {
934: piplineContentProvider = (IPipelinedTreeContentProvider) overridingExtensions[i]
935: .getContentProvider();
936: suggestedOverriddenParent = piplineContentProvider
937: .getPipelinedParent(anElement, lastValidParent);
938:
939: if (suggestedOverriddenParent != null)
940: lastValidParent = suggestedOverriddenParent;
941:
942: // should never return null
943: lastValidParent = findParent(overridingExtensions[i],
944: anElement, lastValidParent);
945: }
946:
947: }
948: return lastValidParent;
949: }
950:
951: }
|