001: /*******************************************************************************
002: * Copyright (c) 2000, 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.jdt.internal.ui.browsing;
011:
012: import java.util.ArrayList;
013: import java.util.Arrays;
014: import java.util.Collection;
015: import java.util.Iterator;
016: import java.util.List;
017:
018: import org.eclipse.core.resources.IResource;
019:
020: import org.eclipse.swt.widgets.Control;
021: import org.eclipse.swt.widgets.Display;
022:
023: import org.eclipse.jface.viewers.AbstractTreeViewer;
024: import org.eclipse.jface.viewers.IBasicPropertyConstants;
025: import org.eclipse.jface.viewers.ListViewer;
026: import org.eclipse.jface.viewers.StructuredViewer;
027: import org.eclipse.jface.viewers.TableViewer;
028: import org.eclipse.jface.viewers.Viewer;
029:
030: import org.eclipse.jdt.core.ElementChangedEvent;
031: import org.eclipse.jdt.core.IClassFile;
032: import org.eclipse.jdt.core.ICompilationUnit;
033: import org.eclipse.jdt.core.IElementChangedListener;
034: import org.eclipse.jdt.core.IImportContainer;
035: import org.eclipse.jdt.core.IJavaElement;
036: import org.eclipse.jdt.core.IJavaElementDelta;
037: import org.eclipse.jdt.core.IJavaProject;
038: import org.eclipse.jdt.core.IPackageDeclaration;
039: import org.eclipse.jdt.core.IPackageFragment;
040: import org.eclipse.jdt.core.IPackageFragmentRoot;
041: import org.eclipse.jdt.core.IParent;
042: import org.eclipse.jdt.core.ISourceReference;
043: import org.eclipse.jdt.core.IType;
044: import org.eclipse.jdt.core.IWorkingCopy;
045: import org.eclipse.jdt.core.JavaCore;
046: import org.eclipse.jdt.core.JavaModelException;
047:
048: import org.eclipse.jdt.ui.StandardJavaElementContentProvider;
049:
050: import org.eclipse.jdt.internal.ui.JavaPlugin;
051:
052: class JavaBrowsingContentProvider extends
053: StandardJavaElementContentProvider implements
054: IElementChangedListener {
055:
056: private StructuredViewer fViewer;
057: private Object fInput;
058: private JavaBrowsingPart fBrowsingPart;
059: private int fReadsInDisplayThread;
060:
061: public JavaBrowsingContentProvider(boolean provideMembers,
062: JavaBrowsingPart browsingPart) {
063: super (provideMembers);
064: fBrowsingPart = browsingPart;
065: fViewer = fBrowsingPart.getViewer();
066: JavaCore.addElementChangedListener(this );
067: }
068:
069: public boolean hasChildren(Object element) {
070: startReadInDisplayThread();
071: try {
072: return super .hasChildren(element);
073: } finally {
074: finishedReadInDisplayThread();
075: }
076: }
077:
078: public Object[] getChildren(Object element) {
079: if (!exists(element))
080: return NO_CHILDREN;
081:
082: startReadInDisplayThread();
083: try {
084: if (element instanceof Collection) {
085: Collection elements = (Collection) element;
086: if (elements.isEmpty())
087: return NO_CHILDREN;
088: Object[] result = new Object[0];
089: Iterator iter = ((Collection) element).iterator();
090: while (iter.hasNext()) {
091: Object[] children = getChildren(iter.next());
092: if (children != NO_CHILDREN)
093: result = concatenate(result, children);
094: }
095: return result;
096: }
097: if (element instanceof IPackageFragment)
098: return getPackageContents((IPackageFragment) element);
099: if (fProvideMembers && element instanceof IType)
100: return getChildren((IType) element);
101: if (fProvideMembers && element instanceof ISourceReference
102: && element instanceof IParent)
103: return removeImportAndPackageDeclarations(super
104: .getChildren(element));
105: if (element instanceof IJavaProject)
106: return getPackageFragmentRoots((IJavaProject) element);
107: return super .getChildren(element);
108: } catch (JavaModelException e) {
109: return NO_CHILDREN;
110: } finally {
111: finishedReadInDisplayThread();
112: }
113: }
114:
115: private Object[] getPackageContents(IPackageFragment fragment)
116: throws JavaModelException {
117: ISourceReference[] sourceRefs;
118: if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
119: sourceRefs = fragment.getCompilationUnits();
120: } else {
121: IClassFile[] classFiles = fragment.getClassFiles();
122: List topLevelClassFile = new ArrayList();
123: for (int i = 0; i < classFiles.length; i++) {
124: IType type = classFiles[i].getType();
125: if (type != null && type.getDeclaringType() == null
126: && !type.isAnonymous() && !type.isLocal())
127: topLevelClassFile.add(classFiles[i]);
128: }
129: sourceRefs = (ISourceReference[]) topLevelClassFile
130: .toArray(new ISourceReference[topLevelClassFile
131: .size()]);
132: }
133:
134: Object[] result = new Object[0];
135: for (int i = 0; i < sourceRefs.length; i++)
136: result = concatenate(
137: result,
138: removeImportAndPackageDeclarations(getChildren(sourceRefs[i])));
139: return concatenate(result, fragment.getNonJavaResources());
140: }
141:
142: private Object[] removeImportAndPackageDeclarations(Object[] members) {
143: ArrayList tempResult = new ArrayList(members.length);
144: for (int i = 0; i < members.length; i++)
145: if (!(members[i] instanceof IImportContainer)
146: && !(members[i] instanceof IPackageDeclaration))
147: tempResult.add(members[i]);
148: return tempResult.toArray();
149: }
150:
151: private Object[] getChildren(IType type) throws JavaModelException {
152: IParent parent;
153: if (type.isBinary())
154: parent = type.getClassFile();
155: else {
156: parent = type.getCompilationUnit();
157: }
158: if (type.getDeclaringType() != null)
159: return type.getChildren();
160:
161: // Add import declarations
162: IJavaElement[] members = parent.getChildren();
163: ArrayList tempResult = new ArrayList(members.length);
164: for (int i = 0; i < members.length; i++)
165: if ((members[i] instanceof IImportContainer))
166: tempResult.add(members[i]);
167: tempResult.addAll(Arrays.asList(type.getChildren()));
168: return tempResult.toArray();
169: }
170:
171: protected Object[] getPackageFragmentRoots(IJavaProject project)
172: throws JavaModelException {
173: if (!project.getProject().isOpen())
174: return NO_CHILDREN;
175:
176: IPackageFragmentRoot[] roots = project
177: .getPackageFragmentRoots();
178: List list = new ArrayList(roots.length);
179: // filter out package fragments that correspond to projects and
180: // replace them with the package fragments directly
181: for (int i = 0; i < roots.length; i++) {
182: IPackageFragmentRoot root = roots[i];
183: if (!root.isExternal()) {
184: Object[] children = root.getChildren();
185: for (int k = 0; k < children.length; k++)
186: list.add(children[k]);
187: } else if (hasChildren(root)) {
188: list.add(root);
189: }
190: }
191: return concatenate(list.toArray(), project
192: .getNonJavaResources());
193: }
194:
195: // ---------------- Element change handling
196:
197: /* (non-Javadoc)
198: * Method declared on IContentProvider.
199: */
200: public void inputChanged(Viewer viewer, Object oldInput,
201: Object newInput) {
202: super .inputChanged(viewer, oldInput, newInput);
203:
204: if (newInput instanceof Collection) {
205: // Get a template object from the collection
206: Collection col = (Collection) newInput;
207: if (!col.isEmpty())
208: newInput = col.iterator().next();
209: else
210: newInput = null;
211: }
212: fInput = newInput;
213: }
214:
215: /* (non-Javadoc)
216: * Method declared on IContentProvider.
217: */
218: public void dispose() {
219: super .dispose();
220: JavaCore.removeElementChangedListener(this );
221: }
222:
223: /* (non-Javadoc)
224: * Method declared on IElementChangedListener.
225: */
226: public void elementChanged(final ElementChangedEvent event) {
227: try {
228: processDelta(event.getDelta());
229: } catch (JavaModelException e) {
230: JavaPlugin.log(e.getStatus());
231: }
232: }
233:
234: /**
235: * Processes a delta recursively. When more than two children are affected the
236: * tree is fully refreshed starting at this node. The delta is processed in the
237: * current thread but the viewer updates are posted to the UI thread.
238: */
239: protected void processDelta(IJavaElementDelta delta)
240: throws JavaModelException {
241: int kind = delta.getKind();
242: int flags = delta.getFlags();
243: final IJavaElement element = delta.getElement();
244: final boolean isElementValidForView = fBrowsingPart
245: .isValidElement(element);
246:
247: if (!getProvideWorkingCopy() && element instanceof IWorkingCopy
248: && ((IWorkingCopy) element).isWorkingCopy())
249: return;
250:
251: if (element != null
252: && element.getElementType() == IJavaElement.COMPILATION_UNIT
253: && !isOnClassPath((ICompilationUnit) element))
254: return;
255:
256: // handle open and closing of a solution or project
257: if (((flags & IJavaElementDelta.F_CLOSED) != 0)
258: || ((flags & IJavaElementDelta.F_OPENED) != 0)) {
259: postRefresh(null);
260: return;
261: }
262:
263: if (kind == IJavaElementDelta.REMOVED) {
264: Object parent = internalGetParent(element);
265: if (isElementValidForView) {
266: if (element instanceof IClassFile) {
267: postRemove(((IClassFile) element).getType());
268: } else if (element instanceof ICompilationUnit
269: && !((ICompilationUnit) element)
270: .isWorkingCopy()) {
271: postRefresh(null);
272: } else if (element instanceof ICompilationUnit
273: && ((ICompilationUnit) element).isWorkingCopy()) {
274: if (getProvideWorkingCopy())
275: postRefresh(null);
276: } else if (parent instanceof ICompilationUnit
277: && getProvideWorkingCopy()
278: && !((ICompilationUnit) parent).isWorkingCopy()) {
279: if (element instanceof IWorkingCopy
280: && ((IWorkingCopy) element).isWorkingCopy()) {
281: // working copy removed from system - refresh
282: postRefresh(null);
283: }
284: } else if (element instanceof IWorkingCopy
285: && ((IWorkingCopy) element).isWorkingCopy()
286: && parent != null && parent.equals(fInput))
287: // closed editor - removing working copy
288: postRefresh(null);
289: else
290: postRemove(element);
291: }
292:
293: if (fBrowsingPart.isAncestorOf(element, fInput)) {
294: if (element instanceof IWorkingCopy
295: && ((IWorkingCopy) element).isWorkingCopy()) {
296: postAdjustInputAndSetSelection(((IJavaElement) fInput)
297: .getPrimaryElement());
298: } else
299: postAdjustInputAndSetSelection(null);
300: }
301:
302: if (fInput != null && fInput.equals(element))
303: postRefresh(null);
304:
305: if (parent instanceof IPackageFragment
306: && fBrowsingPart.isValidElement(parent)) {
307: // refresh if package gets empty (might be filtered)
308: if (isPackageFragmentEmpty((IPackageFragment) parent)
309: && fViewer.testFindItem(parent) != null)
310: postRefresh(null);
311: }
312:
313: return;
314: }
315: if (kind == IJavaElementDelta.ADDED
316: && delta.getMovedFromElement() != null
317: && element instanceof ICompilationUnit)
318: return;
319:
320: if (kind == IJavaElementDelta.ADDED) {
321: if (isElementValidForView) {
322: Object parent = internalGetParent(element);
323: if (element instanceof IClassFile) {
324: postAdd(parent, ((IClassFile) element).getType());
325: } else if (element instanceof ICompilationUnit
326: && !((ICompilationUnit) element)
327: .isWorkingCopy()) {
328: postAdd(parent, ((ICompilationUnit) element)
329: .getTypes());
330: } else if (parent instanceof ICompilationUnit
331: && getProvideWorkingCopy()
332: && !((ICompilationUnit) parent).isWorkingCopy()) {
333: // do nothing
334: } else if (element instanceof IWorkingCopy
335: && ((IWorkingCopy) element).isWorkingCopy()) {
336: // new working copy comes to live
337: postRefresh(null);
338: } else
339: postAdd(parent, element);
340: } else if (fInput == null) {
341: IJavaElement newInput = fBrowsingPart
342: .findInputForJavaElement(element);
343: if (newInput != null)
344: postAdjustInputAndSetSelection(element);
345: } else if (element instanceof IType
346: && fBrowsingPart.isValidInput(element)) {
347: IJavaElement cu1 = element
348: .getAncestor(IJavaElement.COMPILATION_UNIT);
349: IJavaElement cu2 = ((IJavaElement) fInput)
350: .getAncestor(IJavaElement.COMPILATION_UNIT);
351: if (cu1 != null && cu2 != null && cu1.equals(cu2))
352: postAdjustInputAndSetSelection(element);
353: }
354: return;
355: }
356:
357: if (kind == IJavaElementDelta.CHANGED) {
358: if (fInput != null && fInput.equals(element)
359: && (flags & IJavaElementDelta.F_CHILDREN) != 0
360: && (flags & IJavaElementDelta.F_FINE_GRAINED) != 0) {
361: postRefresh(null, true);
362: return;
363: }
364: if (isElementValidForView
365: && (flags & IJavaElementDelta.F_MODIFIERS) != 0) {
366: postUpdateIcon(element);
367: }
368: }
369:
370: if (isClassPathChange(delta))
371: // throw the towel and do a full refresh
372: postRefresh(null);
373:
374: if ((flags & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0
375: && fInput instanceof IJavaElement) {
376: IPackageFragmentRoot pkgRoot = (IPackageFragmentRoot) element;
377: IJavaElement inputsParent = ((IJavaElement) fInput)
378: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
379: if (pkgRoot.equals(inputsParent))
380: postRefresh(null);
381: }
382:
383: // the source attachment of a JAR has changed
384: if (element instanceof IPackageFragmentRoot
385: && (((flags & IJavaElementDelta.F_SOURCEATTACHED) != 0 || ((flags & IJavaElementDelta.F_SOURCEDETACHED)) != 0)))
386: postUpdateIcon(element);
387:
388: IJavaElementDelta[] affectedChildren = delta
389: .getAffectedChildren();
390: if (affectedChildren.length > 1) {
391: // a package fragment might become non empty refresh from the parent
392: if (element instanceof IPackageFragment) {
393: IJavaElement parent = (IJavaElement) internalGetParent(element);
394: // avoid posting a refresh to an invisible parent
395: if (element.equals(fInput)) {
396: postRefresh(element);
397: } else {
398: postRefresh(parent);
399: }
400: }
401: // more than one child changed, refresh from here downwards
402: if (element instanceof IPackageFragmentRoot
403: && isElementValidForView) {
404: postRefresh(skipProjectPackageFragmentRoot((IPackageFragmentRoot) element));
405: return;
406: }
407: }
408: for (int i = 0; i < affectedChildren.length; i++) {
409: processDelta(affectedChildren[i]);
410: }
411: }
412:
413: private boolean isOnClassPath(ICompilationUnit element)
414: throws JavaModelException {
415: IJavaProject project = element.getJavaProject();
416: if (project == null || !project.exists())
417: return false;
418: return project.isOnClasspath(element);
419: }
420:
421: /**
422: * Updates the package icon
423: */
424: private void postUpdateIcon(final IJavaElement element) {
425: postRunnable(new Runnable() {
426: public void run() {
427: Control ctrl = fViewer.getControl();
428: if (ctrl != null && !ctrl.isDisposed())
429: fViewer
430: .update(
431: element,
432: new String[] { IBasicPropertyConstants.P_IMAGE });
433: }
434: });
435: }
436:
437: private void postRefresh(final Object root,
438: final boolean updateLabels) {
439: postRunnable(new Runnable() {
440: public void run() {
441: Control ctrl = fViewer.getControl();
442: if (ctrl != null && !ctrl.isDisposed())
443: fViewer.refresh(root, updateLabels);
444: }
445: });
446: }
447:
448: private void postRefresh(final Object root) {
449: postRefresh(root, false);
450: }
451:
452: private void postAdd(final Object parent, final Object element) {
453: postAdd(parent, new Object[] { element });
454: }
455:
456: private void postAdd(final Object parent, final Object[] elements) {
457: if (elements == null || elements.length <= 0)
458: return;
459:
460: postRunnable(new Runnable() {
461: public void run() {
462: Control ctrl = fViewer.getControl();
463: if (ctrl != null && !ctrl.isDisposed()) {
464: Object[] newElements = getNewElements(elements);
465: if (fViewer instanceof AbstractTreeViewer) {
466: if (fViewer.testFindItem(parent) == null) {
467: Object root = ((AbstractTreeViewer) fViewer)
468: .getInput();
469: if (root != null)
470: ((AbstractTreeViewer) fViewer).add(
471: root, newElements);
472: } else
473: ((AbstractTreeViewer) fViewer).add(parent,
474: newElements);
475: } else if (fViewer instanceof ListViewer)
476: ((ListViewer) fViewer).add(newElements);
477: else if (fViewer instanceof TableViewer)
478: ((TableViewer) fViewer).add(newElements);
479: if (fViewer.testFindItem(elements[0]) != null)
480: fBrowsingPart
481: .adjustInputAndSetSelection(elements[0]);
482: }
483: }
484: });
485: }
486:
487: private Object[] getNewElements(Object[] elements) {
488: int elementsLength = elements.length;
489: ArrayList result = new ArrayList(elementsLength);
490: for (int i = 0; i < elementsLength; i++) {
491: Object element = elements[i];
492: if (fViewer.testFindItem(element) == null)
493: result.add(element);
494: }
495: return result.toArray();
496: }
497:
498: private void postRemove(final Object element) {
499: postRemove(new Object[] { element });
500: }
501:
502: private void postRemove(final Object[] elements) {
503: if (elements.length <= 0)
504: return;
505:
506: postRunnable(new Runnable() {
507: public void run() {
508: Control ctrl = fViewer.getControl();
509: if (ctrl != null && !ctrl.isDisposed()) {
510: if (fViewer instanceof AbstractTreeViewer)
511: ((AbstractTreeViewer) fViewer).remove(elements);
512: else if (fViewer instanceof ListViewer)
513: ((ListViewer) fViewer).remove(elements);
514: else if (fViewer instanceof TableViewer)
515: ((TableViewer) fViewer).remove(elements);
516: }
517: }
518: });
519: }
520:
521: private void postAdjustInputAndSetSelection(final Object element) {
522: postRunnable(new Runnable() {
523: public void run() {
524: Control ctrl = fViewer.getControl();
525: if (ctrl != null && !ctrl.isDisposed()) {
526: ctrl.setRedraw(false);
527: fBrowsingPart.adjustInputAndSetSelection(element);
528: ctrl.setRedraw(true);
529: }
530: }
531: });
532: }
533:
534: protected void startReadInDisplayThread() {
535: if (isDisplayThread())
536: fReadsInDisplayThread++;
537: }
538:
539: protected void finishedReadInDisplayThread() {
540: if (isDisplayThread())
541: fReadsInDisplayThread--;
542: }
543:
544: private boolean isDisplayThread() {
545: Control ctrl = fViewer.getControl();
546: if (ctrl == null)
547: return false;
548:
549: Display currentDisplay = Display.getCurrent();
550: return currentDisplay != null
551: && currentDisplay.equals(ctrl.getDisplay());
552: }
553:
554: private void postRunnable(final Runnable r) {
555: Control ctrl = fViewer.getControl();
556: if (ctrl != null && !ctrl.isDisposed()) {
557: fBrowsingPart.setProcessSelectionEvents(false);
558: try {
559: if (isDisplayThread() && fReadsInDisplayThread == 0)
560: ctrl.getDisplay().syncExec(r);
561: else
562: ctrl.getDisplay().asyncExec(r);
563: } finally {
564: fBrowsingPart.setProcessSelectionEvents(true);
565: }
566: }
567: }
568:
569: /**
570: * Returns the parent for the element.
571: * <p>
572: * Note: This method will return a working copy if the
573: * parent is a working copy. The super class implementation
574: * returns the original element instead.
575: * </p>
576: */
577: protected Object internalGetParent(Object element) {
578: if (element instanceof IJavaProject) {
579: return ((IJavaProject) element).getJavaModel();
580: }
581: // try to map resources to the containing package fragment
582: if (element instanceof IResource) {
583: IResource parent = ((IResource) element).getParent();
584: Object jParent = JavaCore.create(parent);
585: if (jParent != null)
586: return jParent;
587: return parent;
588: }
589:
590: // for package fragments that are contained in a project package fragment
591: // we have to skip the package fragment root as the parent.
592: if (element instanceof IPackageFragment) {
593: IPackageFragmentRoot parent = (IPackageFragmentRoot) ((IPackageFragment) element)
594: .getParent();
595: return skipProjectPackageFragmentRoot(parent);
596: }
597: if (element instanceof IJavaElement)
598: return ((IJavaElement) element).getParent();
599:
600: return null;
601: }
602: }
|