2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.java.navigation;
44
import java.io.IOException;
45
import java.util.ArrayList;
46
import java.util.Collection;
47
import java.util.EnumSet;
48
import java.util.LinkedHashSet;
49
import java.util.List;
51
import javax.lang.model.element.Element;
52
import javax.lang.model.element.ElementKind;
53
import javax.lang.model.element.Modifier;
54
import javax.lang.model.element.NestingKind;
55
import javax.lang.model.element.TypeElement;
56
import javax.lang.model.type.TypeMirror;
57
import javax.lang.model.util.Types;
58
import javax.swing.Icon;
59
import javax.swing.tree.DefaultMutableTreeNode;
60
import javax.swing.tree.DefaultTreeModel;
61
import javax.swing.tree.TreeNode;
63
import org.netbeans.api.java.classpath.ClassPath;
64
import org.netbeans.api.java.source.Task;
65
import org.netbeans.api.java.source.ClassIndex;
66
import org.netbeans.api.java.source.ClasspathInfo;
67
import org.netbeans.api.java.source.CompilationController;
68
import org.netbeans.api.java.source.CompilationInfo;
69
import org.netbeans.api.java.source.ElementHandle;
70
import org.netbeans.api.java.source.JavaSource;
71
import org.netbeans.api.java.source.JavaSource.Phase;
72
import org.netbeans.api.java.source.SourceUtils;
73
import org.netbeans.api.java.source.ui.ElementIcons;
74
import org.netbeans.api.java.source.ui.ElementJavadoc;
75
import org.netbeans.api.java.source.ui.ElementOpen;
76
import org.netbeans.api.project.Project;
77
import org.netbeans.api.project.SourceGroup;
78
import org.netbeans.api.project.Sources;
79
import org.netbeans.api.project.ui.OpenProjects;
80
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
81
import org.openide.ErrorManager;
82
import org.openide.awt.StatusDisplayer;
83
import org.openide.filesystems.FileObject;
84
import org.openide.util.NbBundle;
87
* The tree model for hierarchy pop up window.
89
* @author Sandip Chitale (Sandip.Chitale@Sun.Com)
91
public final class JavaHierarchyModel extends DefaultTreeModel {
92
static Element[] EMPTY_ELEMENTS_ARRAY = new Element[0];
93
static ElementHandle[] EMPTY_ELEMENTHANDLES_ARRAY = new ElementHandle[0];
95
private static final ClassPath EMPTY_CLASSPATH = ClassPathSupport.createClassPath( new FileObject[0] );
98
* Holds value of property pattern.
100
private FileObject fileObject;
101
private ElementHandle[] elementHandles;
105
public JavaHierarchyModel(FileObject fileObject, Element[] elements, CompilationInfo compilationInfo) {
107
this.fileObject = fileObject;
109
if ((elements == null) || (elements.length == 0)) {
110
elementHandles = EMPTY_ELEMENTHANDLES_ARRAY;
112
List<ElementHandle> elementHandlesList = new ArrayList<ElementHandle>(elements.length);
114
for (Element element : elements) {
115
elementHandlesList.add(ElementHandle.create(element));
118
elementHandles = elementHandlesList.toArray(EMPTY_ELEMENTHANDLES_ARRAY);
121
update(elements, compilationInfo);
124
public void update() {
125
update(elementHandles);
128
private void update(final ElementHandle[] elementHandles) {
129
if ((elementHandles == null) && (elementHandles.length == 0)) {
133
JavaSource javaSource = JavaSource.forFileObject(fileObject);
135
if (javaSource != null) {
137
javaSource.runUserActionTask(new Task<CompilationController>() {
139
CompilationController compilationController)
141
compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
143
List<Element> elementsList = new ArrayList<Element>(elementHandles.length);
145
for (ElementHandle elementHandle : elementHandles) {
146
elementsList.add(elementHandle.resolve(
147
compilationController));
150
Element[] elements = elementsList.toArray(EMPTY_ELEMENTS_ARRAY);
151
update(elements, compilationController);
156
} catch (IOException ioe) {
157
ErrorManager.getDefault().notify(ioe);
162
private void update(final Element[] elements,
163
CompilationInfo compilationInfo) {
164
if ((elements == null) && (elements.length == 0)) {
168
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
170
for (Element element : elements) {
171
if ((element.getKind() == ElementKind.CLASS) ||
172
(element.getKind() == ElementKind.INTERFACE) ||
173
(element.getKind() == ElementKind.ENUM) ||
174
(element.getKind() == ElementKind.ANNOTATION_TYPE)) {
175
if (JavaMembersAndHierarchyOptions.isShowSuperTypeHierarchy()) {
176
root.add(new TypeTreeNode(fileObject,
177
((TypeElement) element), compilationInfo));
179
Types types = compilationInfo.getTypes();
180
TypeElement typeElement = ((TypeElement) element);
181
List<TypeElement> superClasses = new ArrayList<TypeElement>();
182
superClasses.add(typeElement);
184
TypeElement superClass = (TypeElement) types.asElement(typeElement.getSuperclass());
185
while (superClass != null) {
186
superClasses.add(0, superClass);
187
superClass = (TypeElement) types.asElement(superClass.getSuperclass());;
189
DefaultMutableTreeNode parent = root;
190
for(TypeElement superTypeElement:superClasses) {
191
FileObject fileObject = SourceUtils.getFile(ElementHandle.create(superTypeElement), compilationInfo.getClasspathInfo());
192
DefaultMutableTreeNode child = new SimpleTypeTreeNode(fileObject, superTypeElement, compilationInfo, typeElement != superTypeElement || typeElement.getQualifiedName().equals(Object.class.getName()));
193
parent.insert(child, 0);
196
JavaMembersAndHierarchyOptions.setSubTypeHierarchyDepth(superClasses.size()+2);
204
void fireTreeNodesChanged() {
205
super.fireTreeNodesChanged(this, getPathToRoot((TreeNode)getRoot()), null, null);
208
private abstract class AbstractHierarchyTreeNode
209
extends DefaultMutableTreeNode implements JavaElement {
210
private FileObject fileObject;
211
private ElementHandle<?extends Element> elementHandle;
212
private ElementKind elementKind;
213
private Set<Modifier> modifiers;
214
private String name = "";
215
private String label = "";
216
private String FQNlabel = "";
217
private String tooltip = null;
218
private Icon icon = null;
219
private ElementJavadoc javaDoc = null;
220
private final ClasspathInfo cpInfo;
222
private boolean loaded = false;
224
AbstractHierarchyTreeNode(FileObject fileObject,
225
Element element, CompilationInfo compilationInfo) {
226
this( fileObject, element, compilationInfo, false);
229
AbstractHierarchyTreeNode(FileObject fileObject,
230
Element element, CompilationInfo compilationInfo, boolean lazyLoadChildren) {
231
this.fileObject = fileObject;
232
this.elementHandle = ElementHandle.create(element);
233
this.elementKind = element.getKind();
234
this.modifiers = element.getModifiers();
235
this.cpInfo = compilationInfo.getClasspathInfo();
237
setName(element.getSimpleName().toString());
238
setIcon(ElementIcons.getElementIcon(element.getKind(), element.getModifiers()));
239
setLabel(Utils.format(element));
240
setFQNLabel(Utils.format(element, false, true));
241
setToolTip(Utils.format(element, true, JavaMembersAndHierarchyOptions.isShowFQN()));
243
if (!lazyLoadChildren) {
245
loadChildren(element, compilationInfo);
253
public int getChildCount() {
261
return super.getChildCount();
264
public FileObject getFileObject() {
268
public String getName() {
272
public Set<Modifier> getModifiers() {
276
public ElementKind getElementKind() {
280
protected void setName(String name) {
284
public String getLabel() {
288
protected void setLabel(String label) {
292
public String getFQNLabel() {
296
protected void setFQNLabel(String FQNlabel) {
297
this.FQNlabel = FQNlabel;
300
public String getTooltip() {
304
protected void setToolTip(String tooltip) {
305
this.tooltip = tooltip;
308
public Icon getIcon() {
312
protected void setIcon(Icon icon) {
316
protected void setElementHandle(
317
ElementHandle<?extends Element> elementHandle) {
318
this.elementHandle = elementHandle;
321
public ElementJavadoc getJavaDoc() {
322
if (javaDoc == null) {
323
JavaSource javaSource = JavaSource.forFileObject(fileObject);
325
if (javaSource != null) {
327
javaSource.runUserActionTask(new Task<CompilationController>() {
329
CompilationController compilationController)
331
compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
332
Element element = elementHandle.resolve(compilationController);
333
setJavaDoc(ElementJavadoc.create(compilationController, element));
336
} catch (IOException ioe) {
337
ErrorManager.getDefault().notify(ioe);
344
protected void setJavaDoc(ElementJavadoc javaDoc) {
345
this.javaDoc = javaDoc;
348
public ElementHandle getElementHandle() {
349
return elementHandle;
352
public void gotoElement() {
356
protected void loadChildren() {
357
JavaSource javaSource = JavaSource.create(cpInfo);
358
if (javaSource != null) {
360
javaSource.runUserActionTask(new Task<CompilationController>() {
361
public void run(CompilationController compilationController)
363
compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
365
Element element = elementHandle.resolve(compilationController);
366
if (element instanceof TypeElement && ((TypeElement)element).getQualifiedName().toString().equals(Object.class.getName())) {
368
loadChildren(element, compilationController);
374
} catch (IOException ioe) {
375
ErrorManager.getDefault().notify(ioe);
380
protected abstract void loadChildren(Element element,
381
CompilationInfo compilationInfo);
383
public String toString() {
384
return (JavaMembersAndHierarchyOptions.isShowFQN()? getFQNLabel() : getLabel());
387
protected void openElementHandle() {
388
if (fileObject == null) {
389
StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(JavaHierarchyModel.class, "MSG_CouldNotOpenElement", getLabel()));
393
if (elementHandle == null) {
397
ElementOpen.open(fileObject, elementHandle);
402
private class TypeTreeNode extends AbstractHierarchyTreeNode {
403
private boolean inSuperClassRole;
405
TypeTreeNode(FileObject fileObject, TypeElement typeElement,
406
CompilationInfo compilationInfo) {
407
this(fileObject, typeElement, compilationInfo, false);
410
TypeTreeNode(FileObject fileObject, TypeElement typeElement,
411
CompilationInfo compilationInfo, boolean inSuperClassRole) {
412
super(fileObject, typeElement, compilationInfo);
413
this.inSuperClassRole = inSuperClassRole;
416
public boolean isLeaf() {
420
protected void loadChildren(Element element,
421
CompilationInfo compilationInfo) {
422
loadChildren(element, compilationInfo, 0);
425
protected int loadChildren(Element element,
426
CompilationInfo compilationInfo, int index) {
427
Types types = compilationInfo.getTypes();
429
TypeElement typeElement = (TypeElement) element;
431
TypeElement superClass = (TypeElement) types.asElement(typeElement.getSuperclass());
432
if (superClass != null && !superClass.getQualifiedName().toString().equals(Object.class.getName())) {
433
FileObject fileObject = SourceUtils.getFile(ElementHandle.create(superClass), compilationInfo.getClasspathInfo());
434
insert(new TypeTreeNode(fileObject, superClass, compilationInfo, true), index++);
436
List<? extends TypeMirror> interfaces = typeElement.getInterfaces();
437
for (TypeMirror interfaceMirror:interfaces) {
438
TypeElement interfaceElement = (TypeElement) types.asElement(interfaceMirror);
439
if (interfaceElement != null) {
440
FileObject fileObject = SourceUtils.getFile(ElementHandle.create(interfaceElement), compilationInfo.getClasspathInfo());
441
insert(new TypeTreeNode(fileObject, (TypeElement)interfaceElement, compilationInfo, true), index++);
445
if (JavaMembersAndHierarchyOptions.isShowInner()) {
446
if (!inSuperClassRole) {
447
for (Element childElement:typeElement.getEnclosedElements()) {
448
AbstractHierarchyTreeNode node = null;
449
if ((childElement.getKind() == ElementKind.CLASS) ||
450
(childElement.getKind() == ElementKind.INTERFACE) ||
451
(childElement.getKind() == ElementKind.ENUM) ||
452
(childElement.getKind() == ElementKind.ANNOTATION_TYPE)) {
453
node = new TypeTreeNode(fileObject, (TypeElement)childElement, compilationInfo, true);
454
insert(node, index++);
463
private class SimpleTypeTreeNode extends AbstractHierarchyTreeNode {
464
private boolean inSuperClassRole;
466
SimpleTypeTreeNode(FileObject fileObject, TypeElement typeElement,
467
CompilationInfo compilationInfo) {
468
this(fileObject, typeElement, compilationInfo, false);
471
SimpleTypeTreeNode(FileObject fileObject, TypeElement typeElement,
472
CompilationInfo compilationInfo, boolean inSuperClassRole) {
473
super(fileObject, typeElement, compilationInfo, inSuperClassRole);
474
this.inSuperClassRole = inSuperClassRole;
477
public boolean isLeaf() {
481
protected void loadChildren(Element element,
482
CompilationInfo compilationInfo) {
483
if (inSuperClassRole) {
487
TypeElement typeElement = (TypeElement) element;
488
// prevent showing sub classes of java.lang.Object
489
if (typeElement.getQualifiedName().toString().equals(Object.class.getName())) {
490
StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(JavaHierarchyModel.class, "MSG_WontShowSubTypesOfObject", Object.class.getName())); // TODO
495
Project[] openProjects = OpenProjects.getDefault().getOpenProjects();
496
if (openProjects == null) {
499
Set<ElementHandle<TypeElement>> processedImplementorElementHandles = new LinkedHashSet<ElementHandle<TypeElement>>();
501
ElementHandle<TypeElement> typeElementHandle = ElementHandle.create(typeElement);
503
final int[] index = new int[] {0};
504
// Walk through open projects
505
for (Project project : openProjects) {
507
Collection<? extends Sources> sourcess = project.getLookup().lookupAll(Sources.class);
508
if (sourcess == null) {
512
// Walk through sources
513
for (Sources sources : sourcess) {
514
// Get Source groups of type java
515
SourceGroup[] sourceGroups = sources.getSourceGroups("java");
516
if (sourceGroups == null) {
520
// Walk through source groups
521
for (SourceGroup sourceGroup : sourceGroups) {
522
// Get root file object
523
FileObject rootFileObject = sourceGroup.getRootFolder();
524
if (rootFileObject == null) {
529
ClassPath classPath =ClassPathSupport.createClassPath(new FileObject[] {rootFileObject});
530
ClassPath bootClassPath =ClassPath.getClassPath(rootFileObject, ClassPath.BOOT);
531
ClassPath compileClassPath =ClassPath.getClassPath(rootFileObject, ClassPath.COMPILE);
532
if (classPath != null) {
533
ClasspathInfo classpathInfo = ClasspathInfo.create(bootClassPath, compileClassPath, classPath);
534
if (classpathInfo != null) {
535
ClassIndex classIndex = classpathInfo.getClassIndex();
536
if (classIndex != null) {
537
Set<ElementHandle<TypeElement>> implementors = classIndex.getElements(typeElementHandle,
538
EnumSet.of(ClassIndex.SearchKind.IMPLEMENTORS),
539
EnumSet.of(ClassIndex.SearchScope.SOURCE, ClassIndex.SearchScope.DEPENDENCIES));
540
for (ElementHandle<TypeElement> implementorElementHandle: implementors) {
541
if (processedImplementorElementHandles.contains(implementorElementHandle)) {
544
processedImplementorElementHandles.add(implementorElementHandle);
545
final ElementHandle<TypeElement> finalImplementorElementHandle = implementorElementHandle;
546
FileObject implementorfileObject = SourceUtils.getFile(implementorElementHandle, classpathInfo);
547
if (implementorfileObject == null) {
550
JavaSource javaSource = JavaSource.forFileObject(implementorfileObject);
551
if (javaSource != null) {
553
javaSource.runUserActionTask(new Task<CompilationController>() {
555
public void run(CompilationController compilationController)
557
compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
558
Element implementor = finalImplementorElementHandle.resolve(compilationController);
559
if (implementor instanceof TypeElement && ((TypeElement)implementor).getNestingKind() != NestingKind.ANONYMOUS) {
560
FileObject fo = SourceUtils.getFile(ElementHandle.create(implementor), compilationController.getClasspathInfo());
562
insert(new SimpleTypeTreeNode(fo, (TypeElement) implementor, compilationController), index[0]++);
567
} catch (IOException ioe) {
568
ErrorManager.getDefault().notify(ioe);