1 /******************************************************************************* 2 * Copyright (c) 2003, 2015 IBM Corporation and others. 3 * 4 * 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 Corporation - initial API and implementation 13 * Anton Leherbauer, Wind River 14 * bug 261031 [CommonNavigator] IPipelinedContentProvider getParent() returning the suggested parent is not ignored 15 * William Chen, chenwmw@gmail.com 16 * bug 343721 getParent of NavigatorContentServiceContentProvider does not return expected node. 17 *******************************************************************************/ 18 package org.eclipse.ui.internal.navigator; 19 20 import java.util.ArrayList; 21 import java.util.Collections; 22 import java.util.Iterator; 23 import java.util.LinkedHashSet; 24 import java.util.LinkedList; 25 import java.util.List; 26 import java.util.Set; 27 28 import org.eclipse.core.runtime.SafeRunner; 29 import org.eclipse.jface.viewers.ITreeContentProvider; 30 import org.eclipse.jface.viewers.ITreePathContentProvider; 31 import org.eclipse.jface.viewers.TreePath; 32 import org.eclipse.jface.viewers.Viewer; 33 import org.eclipse.osgi.util.NLS; 34 import org.eclipse.ui.internal.navigator.extensions.NavigatorContentDescriptor; 35 import org.eclipse.ui.internal.navigator.extensions.NavigatorContentExtension; 36 import org.eclipse.ui.internal.navigator.extensions.NavigatorViewerDescriptor; 37 import org.eclipse.ui.internal.navigator.extensions.SafeDelegateTreeContentProvider; 38 import org.eclipse.ui.navigator.CommonViewer; 39 import org.eclipse.ui.navigator.INavigatorContentDescriptor; 40 import org.eclipse.ui.navigator.INavigatorViewerDescriptor; 41 import org.eclipse.ui.navigator.IPipelinedTreeContentProvider; 42 import org.eclipse.ui.navigator.OverridePolicy; 43 44 /** 45 * <p> 46 * Provides relevant content based on the associated 47 * {@link org.eclipse.ui.internal.navigator.NavigatorContentService} for a 48 * TreeViewer . 49 * </p> 50 * <p> 51 * Except for the dependency on 52 * {@link org.eclipse.ui.internal.navigator.NavigatorContentService}, this class 53 * has no dependencies on the rest of the Common Navigator framework. Tree 54 * viewers that would like to use the extensions defined by the Common 55 * Navigator, without using the actual view part or other pieces of 56 * functionality (filters, sorting, etc) may choose to use this class, in effect 57 * using an extensible, aggregating, delegate content provider. 58 * </p> 59 * 60 * @see org.eclipse.ui.internal.navigator.NavigatorContentService 61 * @see org.eclipse.ui.internal.navigator.NavigatorContentServiceLabelProvider 62 * 63 * @since 3.2 64 * 65 */ 66 public class NavigatorContentServiceContentProvider implements ITreeContentProvider, ITreePathContentProvider { 67 68 private static final Object[] NO_CHILDREN = new Object[0]; 69 70 private final NavigatorContentService contentService; 71 72 private boolean disposeContentService; 73 74 private final boolean enforceHasChildren; 75 76 private Viewer viewer; 77 78 /** 79 * <p> 80 * Creates a cached {@link NavigatorContentService} from the given 81 * viewer Id. 82 * </p> 83 * 84 * @param aViewerId 85 * The associated viewer id that this 86 * NavigatorContentServiceContentProvider will provide content 87 * for 88 */ NavigatorContentServiceContentProvider(String aViewerId)89 public NavigatorContentServiceContentProvider(String aViewerId) { 90 this(new NavigatorContentService(aViewerId)); 91 disposeContentService = true; 92 } 93 94 /** 95 * <p> 96 * Uses the supplied content service to acquire the available extensions. 97 * </p> 98 * 99 * @param aContentService 100 * The associated NavigatorContentService that should be used to 101 * acquire information. 102 */ NavigatorContentServiceContentProvider(NavigatorContentService aContentService)103 public NavigatorContentServiceContentProvider(NavigatorContentService aContentService) { 104 super(); 105 contentService = aContentService; 106 INavigatorViewerDescriptor vDesc = contentService.getViewerDescriptor(); 107 enforceHasChildren = vDesc.getBooleanConfigProperty(NavigatorViewerDescriptor.PROP_ENFORCE_HAS_CHILDREN); 108 } 109 110 @Override inputChanged(Viewer aViewer, Object anOldInput, Object aNewInput)111 public void inputChanged(Viewer aViewer, Object anOldInput, Object aNewInput) { 112 viewer = aViewer; 113 contentService.updateService(aViewer, anOldInput, aNewInput); 114 } 115 116 @Override getElements(Object anInputElement)117 public Object[] getElements(Object anInputElement) { 118 Set rootContentExtensions = contentService.findRootContentExtensions(anInputElement); 119 return internalGetChildren(anInputElement, anInputElement, rootContentExtensions, ELEMENTS); 120 } 121 122 @Override getChildren(Object aParentElement)123 public Object[] getChildren(Object aParentElement) { 124 Set enabledExtensions = contentService.findContentExtensionsByTriggerPoint(aParentElement); 125 return internalGetChildren(aParentElement, aParentElement, enabledExtensions, !ELEMENTS); 126 } 127 128 @Override getChildren(TreePath parentPath)129 public Object[] getChildren(TreePath parentPath) { 130 Object aParentElement = internalAsElement(parentPath); 131 Set enabledExtensions = contentService.findContentExtensionsByTriggerPoint(aParentElement); 132 return internalGetChildren(aParentElement, parentPath, enabledExtensions, !ELEMENTS); 133 } 134 135 private static final boolean ELEMENTS = true; 136 internalGetChildren(final Object aParentElement, final Object aParentElementOrPath, final Set enabledExtensions, final boolean elements)137 private Object[] internalGetChildren(final Object aParentElement, 138 final Object aParentElementOrPath, final Set enabledExtensions, final boolean elements) { 139 if (enabledExtensions.isEmpty()) { 140 return NO_CHILDREN; 141 } 142 final Set finalSet = new LinkedHashSet(); 143 final ContributorTrackingSet localSet = new ContributorTrackingSet(contentService); 144 145 for (final Iterator itr = enabledExtensions.iterator(); itr.hasNext();) { 146 SafeRunner.run(new NavigatorSafeRunnable() { 147 NavigatorContentExtension foundExtension = (NavigatorContentExtension) itr.next(); 148 Object[] contributedChildren = null; 149 NavigatorContentExtension[] overridingExtensions; 150 151 @Override 152 public void run() throws Exception { 153 if (!isOverridingExtensionInSet(foundExtension.getDescriptor(), 154 enabledExtensions)) { 155 if (elements) 156 contributedChildren = foundExtension.internalGetContentProvider() 157 .getElements(aParentElementOrPath); 158 else 159 contributedChildren = foundExtension.internalGetContentProvider() 160 .getChildren(aParentElementOrPath); 161 overridingExtensions = foundExtension 162 .getOverridingExtensionsForTriggerPoint(aParentElement); 163 INavigatorContentDescriptor foundDescriptor = foundExtension 164 .getDescriptor(); 165 localSet.setContributor(foundDescriptor, foundDescriptor); 166 localSet.setContents(contributedChildren); 167 168 if (overridingExtensions.length > 0) { 169 pipelineChildren(aParentElement, overridingExtensions, foundDescriptor, 170 localSet, elements); 171 } 172 finalSet.addAll(localSet); 173 } 174 } 175 176 @Override 177 public void handleException(Throwable e) { 178 NavigatorPlugin.logError(0, NLS.bind( 179 CommonNavigatorMessages.Exception_Invoking_Extension, new Object[] { 180 foundExtension.getDescriptor().getId(), aParentElement }), e); 181 } 182 }); 183 } 184 185 return finalSet.toArray(); 186 } 187 188 /** 189 * Query each of <code>theOverridingExtensions</code> for children, and then 190 * pipe them through the Pipeline content provider. 191 * 192 * @param aParent 193 * The parent element in the tree 194 * @param theOverridingExtensions 195 * The set of overriding extensions that should participate in 196 * the pipeline chain 197 * @param pipelinedChildren 198 * The current children to return to the viewer (should be 199 * modifiable) 200 */ pipelineChildren(Object aParent, NavigatorContentExtension[] theOverridingExtensions, INavigatorContentDescriptor firstClassDescriptor, ContributorTrackingSet pipelinedChildren, boolean elements)201 private void pipelineChildren(Object aParent, NavigatorContentExtension[] theOverridingExtensions, 202 INavigatorContentDescriptor firstClassDescriptor, ContributorTrackingSet pipelinedChildren, boolean elements) { 203 IPipelinedTreeContentProvider pipelinedContentProvider; 204 NavigatorContentExtension[] overridingExtensions; 205 for (NavigatorContentExtension overridingExtension : theOverridingExtensions) { 206 207 if (overridingExtension.internalGetContentProvider().isPipelined()) { 208 pipelinedContentProvider = overridingExtension.internalGetContentProvider(); 209 pipelinedChildren.setContributor(overridingExtension.getDescriptor(), firstClassDescriptor); 210 if (elements) { 211 pipelinedContentProvider.getPipelinedElements(aParent, pipelinedChildren); 212 } else { 213 pipelinedContentProvider.getPipelinedChildren(aParent, pipelinedChildren); 214 } 215 overridingExtensions = overridingExtension.getOverridingExtensionsForTriggerPoint(aParent); 216 if (overridingExtensions.length > 0) { 217 pipelineChildren(aParent, overridingExtensions, firstClassDescriptor, pipelinedChildren, elements); 218 } 219 } 220 } 221 } 222 223 224 /** 225 * Currently this method only checks one level deep. If the suppressed 226 * extension of the given descriptor is contained lower in the tree, then 227 * the extension could still be invoked twice. 228 * 229 * @param aDescriptor 230 * The descriptor which may be overriding other extensions. 231 * @param theEnabledExtensions 232 * The other available extensions. 233 * @return True if the results should be pipelined through the downstream 234 * extensions. 235 */ isOverridingExtensionInSet(INavigatorContentDescriptor aDescriptor, Set theEnabledExtensions)236 private boolean isOverridingExtensionInSet(INavigatorContentDescriptor aDescriptor, Set theEnabledExtensions) { 237 238 if (aDescriptor.getSuppressedExtensionId() != null /* 239 * The descriptor is 240 * an override 241 * descriptor 242 */ 243 && aDescriptor.getOverridePolicy() == OverridePolicy.InvokeAlwaysRegardlessOfSuppressedExt) { 244 /* 245 * if the policy is set as such, it can lead to this extension being 246 * invoked twice; once as a first class extension, and once an 247 * overriding extension. 248 */ 249 if (theEnabledExtensions.contains(contentService.getExtension(aDescriptor.getOverriddenDescriptor()))) { 250 return true; 251 } 252 } 253 return false; 254 } 255 256 /** 257 * Currently this method only checks one level deep. If the suppressed 258 * extension of the given descriptor is contained lower in the tree, then 259 * the extension could still be invoked twice. 260 * 261 * @param aDescriptor 262 * The descriptor which may be overriding other extensions. 263 * @param theEnabledDescriptors 264 * The other available descriptors. 265 * @return True if the results should be pipelined through the downstream 266 * extensions. 267 */ isOverridingDescriptorInSet(INavigatorContentDescriptor aDescriptor, Set theEnabledDescriptors)268 private boolean isOverridingDescriptorInSet(INavigatorContentDescriptor aDescriptor, Set theEnabledDescriptors) { 269 270 if (aDescriptor.getSuppressedExtensionId() != null /* 271 * The descriptor is 272 * an override 273 * descriptor 274 */ 275 && aDescriptor.getOverridePolicy() == OverridePolicy.InvokeAlwaysRegardlessOfSuppressedExt) { 276 /* 277 * if the policy is set as such, it can lead to this extension being 278 * invoked twice; once as a first class extension, and once an 279 * overriding extension. 280 */ 281 if (theEnabledDescriptors.contains(aDescriptor.getOverriddenDescriptor())) { 282 return true; 283 } 284 } 285 return false; 286 } 287 288 @Override getParent(final Object anElement)289 public Object getParent(final Object anElement) { 290 final Set extensions = contentService.findContentExtensionsWithPossibleChild(anElement); 291 final Object[] parent = new Object[1]; 292 293 for (Iterator itr = extensions.iterator(); itr.hasNext();) { 294 final NavigatorContentExtension foundExtension = (NavigatorContentExtension) itr.next(); 295 296 SafeRunner.run(new NavigatorSafeRunnable() { 297 NavigatorContentExtension[] overridingExtensions; 298 299 @Override 300 public void run() throws Exception { 301 if (!isOverridingExtensionInSet(foundExtension.getDescriptor(), extensions)) { 302 parent[0] = foundExtension.internalGetContentProvider() 303 .getParent(anElement); 304 overridingExtensions = foundExtension 305 .getOverridingExtensionsForPossibleChild(anElement); 306 if (overridingExtensions.length > 0) { 307 parent[0] = pipelineParent(anElement, overridingExtensions, parent); 308 } 309 } 310 } 311 312 @Override 313 public void handleException(Throwable e) { 314 NavigatorPlugin.logError(0, NLS.bind( 315 CommonNavigatorMessages.Exception_Invoking_Extension, new Object[] { 316 foundExtension.getDescriptor().getId(), anElement }), e); 317 } 318 }); 319 320 if (parent[0] != null) { 321 return parent[0]; 322 } 323 } 324 return parent[0]; 325 } 326 327 @Override getParents(Object anElement)328 public TreePath[] getParents(Object anElement) { 329 List paths = new ArrayList(); 330 TreePathCompiler compiler = new TreePathCompiler(anElement); 331 Set compilers = findPaths(compiler); 332 for (Iterator iter = compilers.iterator(); iter.hasNext();) { 333 TreePathCompiler c = (TreePathCompiler) iter.next(); 334 paths.add(c.createParentPath()); 335 } 336 return (TreePath[]) paths.toArray(new TreePath[paths.size()]); 337 338 } 339 /** 340 * Query each of <code>theOverridingExtensions</code> for elements, and then 341 * pipe them through the Pipeline content provider. 342 * 343 * @param anInputElement 344 * The input element in the tree 345 * @param theOverridingExtensions 346 * The set of overriding extensions that should participate in 347 * the pipeline chain 348 * @param theCurrentParent 349 * The current elements to return to the viewer (should be 350 * modifiable) 351 * @return The set of elements to return to the viewer 352 */ pipelineParent(Object anInputElement, NavigatorContentExtension[] theOverridingExtensions, Object theCurrentParent)353 private Object pipelineParent(Object anInputElement, NavigatorContentExtension[] theOverridingExtensions, 354 Object theCurrentParent) { 355 IPipelinedTreeContentProvider pipelinedContentProvider; 356 NavigatorContentExtension[] overridingExtensions; 357 Object aSuggestedParent = null; 358 for (NavigatorContentExtension theOverridingExtension : theOverridingExtensions) { 359 360 if (theOverridingExtension.internalGetContentProvider().isPipelined()) { 361 pipelinedContentProvider = theOverridingExtension 362 .internalGetContentProvider(); 363 364 aSuggestedParent = pipelinedContentProvider.getPipelinedParent(anInputElement, aSuggestedParent); 365 366 overridingExtensions = theOverridingExtension 367 .getOverridingExtensionsForTriggerPoint(anInputElement); 368 if (overridingExtensions.length > 0) { 369 aSuggestedParent = pipelineParent(anInputElement, overridingExtensions, aSuggestedParent); 370 } 371 } 372 } 373 return aSuggestedParent != null ? aSuggestedParent : theCurrentParent; 374 } 375 376 /** 377 * Calculate hasChildren for both an element or a path. 378 * 379 * If any of the first class NCEs don't implement the IPipelinedTreeContentProviderHasChildren 380 * and they return true, then we have to use that as the value, as we are obliged to take 381 * the union of the non-pipelined calls. This may result in a false positive hasChildren 382 * indication if the pipeline mechanism does not actually contribute children later. 383 * For pipelined calls, we simply ask the pipelined content provider about the children 384 * and they can override this as they would in the case where they are providing the objects. 385 */ 386 @Override hasChildren(final Object anElementOrPath)387 public boolean hasChildren(final Object anElementOrPath) { 388 final Object anElement = internalAsElement(anElementOrPath); 389 final Set enabledExtensions = contentService.findContentExtensionsByTriggerPoint(anElement); 390 final boolean suggestedHasChildren[] = new boolean[1]; 391 392 for (final Iterator itr = enabledExtensions.iterator(); itr.hasNext();) { 393 SafeRunner.run(new NavigatorSafeRunnable() { 394 NavigatorContentExtension ext; 395 396 @Override 397 public void run() throws Exception { 398 ext = (NavigatorContentExtension) itr.next(); 399 400 if (!ext.isLoaded() && !enforceHasChildren) { 401 suggestedHasChildren[0] = true; 402 return; 403 } 404 405 NavigatorContentExtension[] overridingExtensions; 406 if (!isOverridingExtensionInSet(ext.getDescriptor(), enabledExtensions)) { 407 SafeDelegateTreeContentProvider cp = ext.internalGetContentProvider(); 408 suggestedHasChildren[0] |= callNormalHasChildren(anElementOrPath, 409 anElement, cp); 410 overridingExtensions = ext 411 .getOverridingExtensionsForTriggerPoint(anElement); 412 413 if (overridingExtensions.length > 0) { 414 suggestedHasChildren[0] = pipelineHasChildren(anElementOrPath, 415 anElement, overridingExtensions, suggestedHasChildren[0]); 416 } 417 if (suggestedHasChildren[0]) { 418 return; 419 420 } 421 } 422 } 423 424 @Override 425 public void handleException(Throwable e) { 426 NavigatorPlugin.logError(0, NLS.bind( 427 CommonNavigatorMessages.Exception_Invoking_Extension, new Object[] { 428 ext.getDescriptor().getId(), anElementOrPath }), e); 429 } 430 }); 431 } 432 return suggestedHasChildren[0]; 433 } 434 435 @Override hasChildren(TreePath path)436 public boolean hasChildren(TreePath path) { 437 return hasChildren((Object)path); 438 } 439 callNormalHasChildren(Object anElementOrPath, Object anElement, SafeDelegateTreeContentProvider cp)440 private boolean callNormalHasChildren(Object anElementOrPath, Object anElement, SafeDelegateTreeContentProvider cp) { 441 if (cp.isTreePath() && anElementOrPath instanceof TreePath) { 442 ITreePathContentProvider tpcp = cp; 443 return tpcp.hasChildren((TreePath) anElementOrPath); 444 } 445 return ((ITreeContentProvider) cp).hasChildren(anElement); 446 } 447 pipelineHasChildren(Object anElementOrPath, Object anElement, NavigatorContentExtension[] theOverridingExtensions, boolean suggestedHasChildren)448 private boolean pipelineHasChildren(Object anElementOrPath, Object anElement, 449 NavigatorContentExtension[] theOverridingExtensions, boolean suggestedHasChildren) { 450 NavigatorContentExtension[] overridingExtensions; 451 for (NavigatorContentExtension theOverridingExtension : theOverridingExtensions) { 452 453 SafeDelegateTreeContentProvider cp = theOverridingExtension.internalGetContentProvider(); 454 if (cp.isPipelinedHasChildren()) { 455 suggestedHasChildren = cp.hasPipelinedChildren( 456 anElement, suggestedHasChildren); 457 overridingExtensions = theOverridingExtension 458 .getOverridingExtensionsForTriggerPoint(anElement); 459 if (overridingExtensions.length > 0) { 460 suggestedHasChildren = pipelineHasChildren(anElementOrPath, anElement, 461 overridingExtensions, suggestedHasChildren); 462 } 463 } else { 464 suggestedHasChildren |= callNormalHasChildren(anElementOrPath, anElement, cp); 465 } 466 } 467 return suggestedHasChildren; 468 } 469 470 /** 471 * <p> 472 * Handles any necessary clean up of the {@link NavigatorContentService} 473 * </p> 474 * 475 * <p> 476 * <b>If a client uses this class outside of the framework of 477 * {@link CommonViewer}, the client must ensure that this method is called 478 * when finished. </b> 479 * </p> 480 * 481 * @see org.eclipse.jface.viewers.IContentProvider#dispose() 482 */ 483 @Override dispose()484 public void dispose() { 485 if (disposeContentService) { 486 contentService.dispose(); 487 } 488 } 489 490 /** 491 * Get the element from an element or tree path argument. 492 * 493 * @param parentElementOrPath 494 * the element or tree path 495 * @return the element 496 */ internalAsElement(Object parentElementOrPath)497 private Object internalAsElement(Object parentElementOrPath) { 498 if (parentElementOrPath instanceof TreePath) { 499 TreePath tp = (TreePath) parentElementOrPath; 500 if (tp.getSegmentCount() > 0) { 501 return tp.getLastSegment(); 502 } 503 // If the path is empty, the parent element is the root 504 return viewer.getInput(); 505 } 506 return parentElementOrPath; 507 } 508 509 static class CyclicPathException extends Exception { 510 511 private static final long serialVersionUID = 2111962579612444989L; 512 CyclicPathException(TreePathCompiler compiler, Object invalidSegment, boolean asChild)513 protected CyclicPathException(TreePathCompiler compiler, Object invalidSegment, boolean asChild) { 514 super("Cannot add " + invalidSegment + //$NON-NLS-1$ 515 " to the list of segments in " + compiler + //$NON-NLS-1$ 516 (asChild ? " as a child." : " as a parent.")); //$NON-NLS-1$ //$NON-NLS-2$ 517 } 518 } 519 520 class TreePathCompiler { 521 522 private final LinkedList segments = new LinkedList(); 523 TreePathCompiler(Object segment)524 protected TreePathCompiler(Object segment) { 525 segments.add(segment); 526 } 527 TreePathCompiler(TreePathCompiler aCompiler)528 protected TreePathCompiler(TreePathCompiler aCompiler) { 529 segments.addAll(aCompiler.segments); 530 } 531 TreePathCompiler(TreePath aPath)532 protected TreePathCompiler(TreePath aPath) { 533 for (int i = 0; i < aPath.getSegmentCount(); i++) { 534 segments.addLast(aPath.getSegment(i)); 535 } 536 } 537 addParent(Object segment)538 protected void addParent(Object segment) throws CyclicPathException { 539 if (segments.contains(segment)) { 540 throw new CyclicPathException(this, segment, false); 541 } 542 segments.addFirst(segment); 543 } 544 addChild(Object segment)545 protected void addChild(Object segment) throws CyclicPathException { 546 if (segments.contains(segment)) { 547 throw new CyclicPathException(this, segment, false); 548 } 549 segments.addLast(segment); 550 } 551 552 /** 553 * Create the full tree path. 554 * 555 * @return A TreePath with all segments from the compiler. 556 */ createPath()557 public TreePath createPath() { 558 return new TreePath(segments.toArray()); 559 } 560 561 /** 562 * Create parent tree path. 563 * 564 * @return A TreePath with all segments but the last from the compiler 565 */ createParentPath()566 public TreePath createParentPath() { 567 LinkedList parentSegments = new LinkedList(segments); 568 parentSegments.removeLast(); 569 return new TreePath(parentSegments.toArray()); 570 } 571 getLastSegment()572 public Object getLastSegment() { 573 return segments.getLast(); 574 } 575 getFirstSegment()576 public Object getFirstSegment() { 577 return segments.getFirst(); 578 } 579 580 @Override toString()581 public String toString() { 582 583 StringBuilder buffer = new StringBuilder(); 584 for (Iterator iter = segments.iterator(); iter.hasNext();) { 585 Object segment = iter.next(); 586 buffer.append(segment).append("::"); //$NON-NLS-1$ 587 } 588 return buffer.toString(); 589 } 590 591 } 592 findPaths(TreePathCompiler aPathCompiler)593 private Set findPaths(TreePathCompiler aPathCompiler) { 594 595 Set/* <Object> */parents = findParents(aPathCompiler.getFirstSegment()); 596 Set/* <TreePathCompiler> */parentPaths = new LinkedHashSet(); 597 Set/* <TreePathCompiler> */foundPaths = Collections.EMPTY_SET; 598 if (parents.size() > 0) { 599 for (Iterator parentIter = parents.iterator(); parentIter.hasNext();) { 600 Object parent = parentIter.next(); 601 TreePathCompiler c = new TreePathCompiler(aPathCompiler); 602 try { 603 c.addParent(parent); 604 foundPaths = findPaths(c); 605 } catch (CyclicPathException cpe) { 606 String msg = cpe.getMessage() != null ? cpe.getMessage() : cpe.toString(); 607 NavigatorPlugin.logError(0, msg, cpe); 608 } 609 if (foundPaths.isEmpty()) 610 parentPaths.add(c); 611 else 612 parentPaths.addAll(foundPaths); 613 } 614 } 615 return parentPaths; 616 617 } 618 findParents(final Object anElement)619 private Set findParents(final Object anElement) { 620 final Set descriptors = contentService.findDescriptorsWithPossibleChild(anElement, false); 621 final Set parents = new LinkedHashSet(); 622 623 for (final Iterator itr = descriptors.iterator(); itr.hasNext();) { 624 SafeRunner.run(new NavigatorSafeRunnable() { 625 NavigatorContentDescriptor foundDescriptor; 626 NavigatorContentExtension foundExtension; 627 Object parent = null; 628 629 @Override 630 public void run() throws Exception { 631 foundDescriptor = (NavigatorContentDescriptor) itr.next(); 632 foundExtension = contentService.getExtension(foundDescriptor); 633 634 if (!isOverridingDescriptorInSet(foundExtension.getDescriptor(), descriptors)) { 635 if (foundExtension.internalGetContentProvider().isTreePath()) { 636 TreePath[] parentTreePaths = ((ITreePathContentProvider) foundExtension 637 .internalGetContentProvider()).getParents(anElement); 638 639 for (TreePath parentTreePath : parentTreePaths) { 640 641 parent = parentTreePath.getLastSegment(); 642 if ((parent = findParent(foundExtension, anElement, parent)) != null) 643 parents.add(parent); 644 } 645 646 } else { 647 parent = foundExtension.internalGetContentProvider().getParent( 648 anElement); 649 if ((parent = findParent(foundExtension, anElement, parent)) != null) 650 parents.add(parent); 651 } 652 } 653 } 654 655 @Override 656 public void handleException(Throwable e) { 657 NavigatorPlugin.logError(0, NLS.bind( 658 CommonNavigatorMessages.Exception_Invoking_Extension, new Object[] { 659 foundExtension.getDescriptor().getId(), anElement }), e); 660 } 661 }); 662 } 663 664 return parents; 665 666 } 667 findParent(NavigatorContentExtension anExtension, Object anElement, Object aSuggestedParent)668 private Object findParent(NavigatorContentExtension anExtension, Object anElement, Object aSuggestedParent) { 669 670 /* the last valid (non-null) parent for the anElement */ 671 Object lastValidParent = aSuggestedParent; 672 /* used to keep track of new suggestions */ 673 Object suggestedOverriddenParent = null; 674 IPipelinedTreeContentProvider piplineContentProvider; 675 NavigatorContentExtension[] overridingExtensions = anExtension 676 .getOverridingExtensionsForPossibleChild(anElement); 677 for (NavigatorContentExtension overridingExtension : overridingExtensions) { 678 if (overridingExtension.internalGetContentProvider().isPipelined()) { 679 piplineContentProvider = overridingExtension.internalGetContentProvider(); 680 suggestedOverriddenParent = piplineContentProvider.getPipelinedParent(anElement, lastValidParent); 681 682 if (suggestedOverriddenParent != null && !suggestedOverriddenParent.equals(aSuggestedParent)) 683 lastValidParent = suggestedOverriddenParent; 684 685 // should never return null 686 lastValidParent = findParent(overridingExtension, anElement, lastValidParent); 687 } 688 689 } 690 return lastValidParent; 691 } 692 693 } 694