Refactory: xdebug.ui plugin.
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpdt / ui / StandardJavaElementContentProvider.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.ui;
12
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import net.sourceforge.phpdt.core.ICompilationUnit;
17 import net.sourceforge.phpdt.core.IJavaElement;
18 import net.sourceforge.phpdt.core.IJavaElementDelta;
19 import net.sourceforge.phpdt.core.IJavaModel;
20 import net.sourceforge.phpdt.core.IJavaProject;
21 import net.sourceforge.phpdt.core.IPackageFragment;
22 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
23 import net.sourceforge.phpdt.core.IParent;
24 import net.sourceforge.phpdt.core.ISourceReference;
25 import net.sourceforge.phpdt.core.JavaCore;
26 import net.sourceforge.phpdt.core.JavaModelException;
27 import net.sourceforge.phpdt.internal.corext.util.JavaModelUtil;
28
29 import org.eclipse.core.resources.IFile;
30 import org.eclipse.core.resources.IFolder;
31 import org.eclipse.core.resources.IProject;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.runtime.CoreException;
34 import org.eclipse.jface.viewers.ITreeContentProvider;
35 import org.eclipse.jface.viewers.Viewer;
36
37 /**
38  * A base content provider for Java elements. It provides access to the Java
39  * element hierarchy without listening to changes in the Java model. If updating
40  * the presentation on Java model change is required than clients have to
41  * subclass, listen to Java model changes and have to update the UI using
42  * corresponding methods provided by the JFace viewers or their own UI
43  * presentation.
44  * <p>
45  * The following Java element hierarchy is surfaced by this content provider:
46  * <p>
47  * 
48  * <pre>
49  *  Java model (
50  * <code>
51  * IJavaModel
52  * </code>
53  * )
54  *  Java project (
55  * <code>
56  * IJavaProject
57  * </code>
58  * )
59  *  package fragment root (
60  * <code>
61  * IPackageFragmentRoot
62  * </code>
63  * )
64  *  package fragment (
65  * <code>
66  * IPackageFragment
67  * </code>
68  * )
69  *  compilation unit (
70  * <code>
71  * ICompilationUnit
72  * </code>
73  * )
74  *  binary class file (
75  * <code>
76  * IClassFile
77  * </code>
78  * )
79  * </pre>
80  * 
81  * </p>
82  * <p>
83  * Note that when the entire Java project is declared to be package fragment
84  * root, the corresponding package fragment root element that normally appears
85  * between the Java project and the package fragments is automatically filtered
86  * out.
87  * </p>
88  * This content provider can optionally return working copy elements for members
89  * below compilation units. If enabled, working copy members are returned for
90  * those compilation units in the Java element hierarchy for which a shared
91  * working copy exists in JDT core.
92  * 
93  * @see net.sourceforge.phpdt.ui.IWorkingCopyProvider
94  * @see JavaCore#getSharedWorkingCopies(net.sourceforge.phpdt.core.IBufferFactory)
95  * 
96  * @since 2.0
97  */
98 public class StandardJavaElementContentProvider implements
99                 ITreeContentProvider, IWorkingCopyProvider {
100
101         protected static final Object[] NO_CHILDREN = new Object[0];
102
103         protected boolean fProvideMembers = false;
104
105         protected boolean fProvideWorkingCopy = false;
106
107         /**
108          * Creates a new content provider. The content provider does not provide
109          * members of compilation units or class files and it does not provide
110          * working copy elements.
111          */
112         public StandardJavaElementContentProvider() {
113         }
114
115         /**
116          * Creates a new <code>StandardJavaElementContentProvider</code>.
117          * 
118          * @param provideMembers
119          *            if <code>true</code> members below compilation units and
120          *            class files are provided.
121          * @param provideWorkingCopy
122          *            if <code>true</code> the element provider provides working
123          *            copies members of compilation units which have an associated
124          *            working copy in JDT core. Otherwise only original elements are
125          *            provided.
126          */
127         public StandardJavaElementContentProvider(boolean provideMembers,
128                         boolean provideWorkingCopy) {
129                 fProvideMembers = provideMembers;
130                 fProvideWorkingCopy = provideWorkingCopy;
131         }
132
133         /**
134          * Returns whether members are provided when asking for a compilation units
135          * or class file for its children.
136          * 
137          * @return <code>true</code> if the content provider provides members;
138          *         otherwise <code>false</code> is returned
139          */
140         public boolean getProvideMembers() {
141                 return fProvideMembers;
142         }
143
144         /**
145          * Sets whether the content provider is supposed to return members when
146          * asking a compilation unit or class file for its children.
147          * 
148          * @param b
149          *            if <code>true</code> then members are provided. If
150          *            <code>false</code> compilation units and class files are the
151          *            leaves provided by this content provider.
152          */
153         public void setProvideMembers(boolean b) {
154                 fProvideMembers = b;
155         }
156
157         /**
158          * Returns whether the provided members are from a working copy or the
159          * original compilation unit.
160          * 
161          * @return <code>true</code> if the content provider provides working copy
162          *         members; otherwise <code>false</code> is returned
163          * 
164          * @see #setProvideWorkingCopy(boolean)
165          */
166         public boolean getProvideWorkingCopy() {
167                 return fProvideWorkingCopy;
168         }
169
170         /**
171          * Sets whether the members are provided from a shared working copy that
172          * exists for a original compilation unit in the Java element hierarchy.
173          * 
174          * @param b
175          *            if <code>true</code> members are provided from a working
176          *            copy if one exists in JDT core. If <code>false</code> the
177          *            provider always returns original elements.
178          */
179         public void setProvideWorkingCopy(boolean b) {
180                 fProvideWorkingCopy = b;
181         }
182
183         /*
184          * (non-Javadoc)
185          * 
186          * @see IWorkingCopyProvider#providesWorkingCopies()
187          */
188         public boolean providesWorkingCopies() {
189                 return fProvideWorkingCopy;
190         }
191
192         /*
193          * (non-Javadoc) Method declared on IStructuredContentProvider.
194          */
195         public Object[] getElements(Object parent) {
196                 return getChildren(parent);
197         }
198
199         /*
200          * (non-Javadoc) Method declared on IContentProvider.
201          */
202         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
203         }
204
205         /*
206          * (non-Javadoc) Method declared on IContentProvider.
207          */
208         public void dispose() {
209         }
210
211         /*
212          * (non-Javadoc) Method declared on ITreeContentProvider.
213          */
214         public Object[] getChildren(Object element) {
215                 if (!exists(element))
216                         return NO_CHILDREN;
217
218                 try {
219                         if (element instanceof IJavaModel)
220                                 return getJavaProjects((IJavaModel) element);
221
222                         // if (element instanceof IJavaProject)
223                         // return getPackageFragmentRoots((IJavaProject)element);
224                         //                      
225                         if (element instanceof IPackageFragmentRoot)
226                                 return getPackageFragments((IPackageFragmentRoot) element);
227
228                         // if (element instanceof IPackageFragment)
229                         // return getPackageContents((IPackageFragment)element);
230
231                         if (element instanceof IFolder)
232                                 return getResources((IFolder) element);
233
234                         if (fProvideMembers && element instanceof ISourceReference
235                                         && element instanceof IParent) {
236                                 if (fProvideWorkingCopy && element instanceof ICompilationUnit) {
237                                         element = JavaModelUtil
238                                                         .toWorkingCopy((ICompilationUnit) element);
239                                 }
240                                 return ((IParent) element).getChildren();
241                         }
242                 } catch (JavaModelException e) {
243                         return NO_CHILDREN;
244                 }
245                 return NO_CHILDREN;
246         }
247
248         /*
249          * (non-Javadoc)
250          * 
251          * @see ITreeContentProvider
252          */
253         public boolean hasChildren(Object element) {
254                 if (fProvideMembers) {
255                         // assume CUs and class files are never empty
256                         if (element instanceof ICompilationUnit) {
257                                 // ||
258                                 // element instanceof IClassFile) {
259                                 return true;
260                         }
261                 } else {
262                         // don't allow to drill down into a compilation unit or class file
263                         if (element instanceof ICompilationUnit ||
264                         // element instanceof IClassFile ||
265                                         element instanceof IFile)
266                                 return false;
267                 }
268
269                 if (element instanceof IJavaProject) {
270                         IJavaProject jp = (IJavaProject) element;
271                         if (!jp.getProject().isOpen()) {
272                                 return false;
273                         }
274                 }
275
276                 if (element instanceof IParent) {
277                         try {
278                                 // when we have Java children return true, else we fetch all the
279                                 // children
280                                 if (((IParent) element).hasChildren())
281                                         return true;
282                         } catch (JavaModelException e) {
283                                 return true;
284                         }
285                 }
286                 Object[] children = getChildren(element);
287                 return (children != null) && children.length > 0;
288         }
289
290         /*
291          * (non-Javadoc) Method declared on ITreeContentProvider.
292          */
293         public Object getParent(Object element) {
294                 if (!exists(element))
295                         return null;
296                 return internalGetParent(element);
297         }
298
299         private Object[] getPackageFragments(IPackageFragmentRoot root)
300                         throws JavaModelException {
301                 IJavaElement[] fragments = root.getChildren();
302                 // Object[] nonJavaResources= root.getNonJavaResources();
303                 // if (nonJavaResources == null)
304                 return fragments;
305                 // return concatenate(fragments, nonJavaResources);
306         }
307
308         /**
309          * Note: This method is for internal use only. Clients should not call this
310          * method.
311          */
312         // protected Object[] getPackageFragmentRoots(IJavaProject project) throws
313         // JavaModelException {
314         // if (!project.getProject().isOpen())
315         // return NO_CHILDREN;
316         //                      
317         // IPackageFragmentRoot[] roots= project.getPackageFragmentRoots();
318         // List list= new ArrayList(roots.length);
319         // // filter out package fragments that correspond to projects and
320         // // replace them with the package fragments directly
321         // for (int i= 0; i < roots.length; i++) {
322         // IPackageFragmentRoot root= (IPackageFragmentRoot)roots[i];
323         // if (isProjectPackageFragmentRoot(root)) {
324         // Object[] children= root.getChildren();
325         // for (int k= 0; k < children.length; k++)
326         // list.add(children[k]);
327         // }
328         // else if (hasChildren(root)) {
329         // list.add(root);
330         // }
331         // }
332         // return concatenate(list.toArray(), project.getNonJavaResources());
333         // }
334         /**
335          * Note: This method is for internal use only. Clients should not call this
336          * method.
337          */
338         protected Object[] getJavaProjects(IJavaModel jm) throws JavaModelException {
339                 return jm.getJavaProjects();
340         }
341
342         // private Object[] getPackageContents(IPackageFragment fragment) throws
343         // JavaModelException {
344         // if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
345         // return concatenate(fragment.getCompilationUnits(),
346         // fragment.getNonJavaResources());
347         // }
348         // return concatenate(fragment.getClassFiles(),
349         // fragment.getNonJavaResources());
350         // }
351
352         private Object[] getResources(IFolder folder) {
353                 try {
354                         // filter out folders that are package fragment roots
355                         Object[] members = folder.members();
356                         List nonJavaResources = new ArrayList();
357                         for (int i = 0; i < members.length; i++) {
358                                 Object o = members[i];
359                                 // A folder can also be a package fragement root in the
360                                 // following case
361                                 // Project
362                                 // + src <- source folder
363                                 // + excluded <- excluded from class path
364                                 // + included <- a new source folder.
365                                 // Included is a member of excluded, but since it is rendered as
366                                 // a source
367                                 // folder we have to exclude it as a normal child.
368                                 if (o instanceof IFolder) {
369                                         IJavaElement element = JavaCore.create((IFolder) o);
370                                         if (element instanceof IPackageFragmentRoot
371                                                         && element.exists()) {
372                                                 continue;
373                                         }
374                                 }
375                                 nonJavaResources.add(o);
376                         }
377                         return nonJavaResources.toArray();
378                 } catch (CoreException e) {
379                         return NO_CHILDREN;
380                 }
381         }
382
383         /**
384          * Note: This method is for internal use only. Clients should not call this
385          * method.
386          */
387         protected boolean isClassPathChange(IJavaElementDelta delta) {
388
389                 // need to test the flags only for package fragment roots
390                 if (delta.getElement().getElementType() != IJavaElement.PACKAGE_FRAGMENT_ROOT)
391                         return false;
392
393                 int flags = delta.getFlags();
394                 return (delta.getKind() == IJavaElementDelta.CHANGED
395                                 && ((flags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) != 0)
396                                 || ((flags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) || ((flags & IJavaElementDelta.F_REORDER) != 0));
397         }
398
399         /**
400          * Note: This method is for internal use only. Clients should not call this
401          * method.
402          */
403         protected Object skipProjectPackageFragmentRoot(IPackageFragmentRoot root) {
404                 try {
405                         if (isProjectPackageFragmentRoot(root))
406                                 return root.getParent();
407                         return root;
408                 } catch (JavaModelException e) {
409                         return root;
410                 }
411         }
412
413         /**
414          * Note: This method is for internal use only. Clients should not call this
415          * method.
416          */
417         protected boolean isPackageFragmentEmpty(IJavaElement element)
418                         throws JavaModelException {
419                 if (element instanceof IPackageFragment) {
420                         IPackageFragment fragment = (IPackageFragment) element;
421                         if (!(fragment.hasChildren()))
422                                 // ||
423                                 // fragment.getNonJavaResources().length > 0) &&
424                                 // fragment.hasSubpackages())
425                                 return true;
426                 }
427                 return false;
428         }
429
430         /**
431          * Note: This method is for internal use only. Clients should not call this
432          * method.
433          */
434         protected boolean isProjectPackageFragmentRoot(IPackageFragmentRoot root)
435                         throws JavaModelException {
436                 IResource resource = root.getResource();
437                 return (resource instanceof IProject);
438         }
439
440         /**
441          * Note: This method is for internal use only. Clients should not call this
442          * method.
443          */
444         protected boolean exists(Object element) {
445                 if (element == null) {
446                         return false;
447                 }
448                 if (element instanceof IResource) {
449                         return ((IResource) element).exists();
450                 }
451                 if (element instanceof IJavaElement) {
452                         return ((IJavaElement) element).exists();
453                 }
454                 return true;
455         }
456
457         /**
458          * Note: This method is for internal use only. Clients should not call this
459          * method.
460          */
461         protected Object internalGetParent(Object element) {
462                 if (element instanceof IJavaProject) {
463                         return ((IJavaProject) element).getJavaModel();
464                 }
465                 // try to map resources to the containing package fragment
466                 if (element instanceof IResource) {
467                         IResource parent = ((IResource) element).getParent();
468                         IJavaElement jParent = JavaCore.create(parent);
469                         // http://bugs.eclipse.org/bugs/show_bug.cgi?id=31374
470                         if (jParent != null && jParent.exists())
471                                 return jParent;
472                         return parent;
473                 }
474
475                 // for package fragments that are contained in a project package
476                 // fragment
477                 // we have to skip the package fragment root as the parent.
478                 if (element instanceof IPackageFragment) {
479                         IPackageFragmentRoot parent = (IPackageFragmentRoot) ((IPackageFragment) element)
480                                         .getParent();
481                         return skipProjectPackageFragmentRoot(parent);
482                 }
483                 if (element instanceof IJavaElement) {
484                         IJavaElement candidate = ((IJavaElement) element).getParent();
485                         // If the parent is a CU we might have shown working copy elements
486                         // below CU level. If so
487                         // return the original element instead of the working copy.
488                         if (candidate != null
489                                         && candidate.getElementType() == IJavaElement.COMPILATION_UNIT) {
490                                 candidate = JavaModelUtil
491                                                 .toOriginal((ICompilationUnit) candidate);
492                         }
493                         return candidate;
494                 }
495                 return null;
496         }
497
498         /**
499          * Note: This method is for internal use only. Clients should not call this
500          * method.
501          */
502         protected static Object[] concatenate(Object[] a1, Object[] a2) {
503                 int a1Len = a1.length;
504                 int a2Len = a2.length;
505                 Object[] res = new Object[a1Len + a2Len];
506                 System.arraycopy(a1, 0, res, 0, a1Len);
507                 System.arraycopy(a2, 0, res, a1Len, a2Len);
508                 return res;
509         }
510
511 }