001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.java.navigation;
042:
043: import java.awt.Image;
044: import java.awt.datatransfer.Transferable;
045: import java.io.IOException;
046: import java.util.Comparator;
047: import java.util.HashMap;
048: import java.util.HashSet;
049: import java.util.List;
050: import java.util.Set;
051: import javax.lang.model.element.Element;
052: import javax.lang.model.element.ElementKind;
053: import javax.lang.model.element.Modifier;
054: import javax.swing.Action;
055: import org.netbeans.api.java.source.ClasspathInfo;
056: import org.netbeans.api.java.source.ElementHandle;
057: import org.netbeans.api.java.source.SourceUtils;
058: import org.netbeans.api.java.source.TreePathHandle;
059: import org.netbeans.api.java.source.ui.ElementIcons;
060: import org.netbeans.modules.java.navigation.ElementNode.Description;
061: import org.netbeans.modules.java.navigation.actions.OpenAction;
062: import org.netbeans.modules.refactoring.api.ui.RefactoringActionsFactory;
063: import org.openide.filesystems.FileObject;
064: import org.openide.nodes.AbstractNode;
065: import org.openide.nodes.Children;
066: import org.openide.nodes.Children;
067: import org.openide.nodes.Node;
068: import org.openide.util.Lookup;
069: import org.openide.util.NbBundle;
070: import org.openide.util.Utilities;
071: import org.openide.util.datatransfer.PasteType;
072: import org.openide.util.lookup.Lookups;
073:
074: /** Node representing an Element
075: *
076: * @author Petr Hrebejk
077: */
078: public class ElementNode extends AbstractNode {
079:
080: private static Node WAIT_NODE;
081:
082: private OpenAction openAction;
083: private Description description;
084:
085: /** Creates a new instance of TreeNode */
086: public ElementNode(Description description) {
087: super (description.subs == null ? Children.LEAF
088: : new ElementChilren(description.subs, description.ui
089: .getFilters()),
090: description.treePathHandle == null ? null : Lookups
091: .singleton(description.treePathHandle));
092: this .description = description;
093: setDisplayName(description.name);
094: }
095:
096: @Override
097: public Image getIcon(int type) {
098: return description.kind == null ? super .getIcon(type)
099: : Utilities.icon2Image(ElementIcons.getElementIcon(
100: description.kind, description.modifiers));
101: }
102:
103: @Override
104: public Image getOpenedIcon(int type) {
105: return getIcon(type);
106: }
107:
108: @Override
109: public java.lang.String getDisplayName() {
110: if (description.name != null) {
111: return description.name;
112: }
113: if (description.fileObject != null) {
114: return description.fileObject.getNameExt();
115: }
116: return null;
117: }
118:
119: @Override
120: public String getHtmlDisplayName() {
121: return description.htmlHeader;
122: }
123:
124: @Override
125: public Action[] getActions(boolean context) {
126:
127: if (context || description.name == null) {
128: return description.ui.getActions();
129: } else {
130: Action panelActions[] = description.ui.getActions();
131:
132: Action actions[] = new Action[4 + panelActions.length];
133: actions[0] = getOpenAction();
134: actions[1] = RefactoringActionsFactory.whereUsedAction();
135: actions[2] = RefactoringActionsFactory.popupSubmenuAction();
136: actions[3] = null;
137: for (int i = 0; i < panelActions.length; i++) {
138: actions[4 + i] = panelActions[i];
139: }
140: return actions;
141: }
142: }
143:
144: @Override
145: public Action getPreferredAction() {
146: return getOpenAction();
147: }
148:
149: @Override
150: public boolean canCopy() {
151: return false;
152: }
153:
154: @Override
155: public boolean canCut() {
156: return false;
157: }
158:
159: @Override
160: public boolean canDestroy() {
161: return false;
162: }
163:
164: @Override
165: public boolean canRename() {
166: return false;
167: }
168:
169: @Override
170: public PasteType getDropType(Transferable t, int action, int index) {
171: return null;
172: }
173:
174: @Override
175: public Transferable drag() throws IOException {
176: return null;
177: }
178:
179: @Override
180: protected void createPasteTypes(Transferable t, List<PasteType> s) {
181: // Do nothing
182: }
183:
184: private synchronized Action getOpenAction() {
185: if (openAction == null) {
186: FileObject fo = description.getFileObject();
187: openAction = new OpenAction(description.elementHandle, fo,
188: description.name);
189: }
190: return openAction;
191: }
192:
193: static synchronized Node getWaitNode() {
194: if (WAIT_NODE == null) {
195: WAIT_NODE = new WaitNode();
196: }
197: return WAIT_NODE;
198: }
199:
200: public void refreshRecursively() {
201: Children ch = getChildren();
202: if (ch instanceof ElementChilren) {
203: boolean scrollOnExpand = description.ui.getScrollOnExpand();
204: description.ui.setScrollOnExpand(false);
205: ((ElementChilren) ch).resetKeys(description.subs,
206: description.ui.getFilters());
207: for (Node sub : ch.getNodes()) {
208: description.ui.expandNode(sub);
209: ((ElementNode) sub).refreshRecursively();
210: }
211: description.ui.setScrollOnExpand(scrollOnExpand);
212: }
213: }
214:
215: public ElementNode getNodeForElement(ElementHandle<Element> eh) {
216:
217: if (getDescritption().elementHandle != null
218: && eh.signatureEquals(getDescritption().elementHandle)) {
219: return this ;
220: }
221:
222: Children ch = getChildren();
223: if (ch instanceof ElementChilren) {
224: for (Node sub : ch.getNodes()) {
225: ElementNode result = ((ElementNode) sub)
226: .getNodeForElement(eh);
227: if (result != null) {
228: return result;
229: }
230: }
231: }
232:
233: return null;
234: }
235:
236: public void updateRecursively(Description newDescription) {
237: Children ch = getChildren();
238: if (ch instanceof ElementChilren) {
239: HashSet<Description> oldSubs = new HashSet<Description>(
240: description.subs);
241:
242: // Create a hashtable which maps Description to node.
243: // We will then identify the nodes by the description. The trick is
244: // that the new and old description are equal and have the same hashcode
245: Node[] nodes = ch.getNodes(true);
246: HashMap<Description, ElementNode> oldD2node = new HashMap<Description, ElementNode>();
247: for (Node node : nodes) {
248: oldD2node.put(((ElementNode) node).description,
249: (ElementNode) node);
250: }
251:
252: // Now refresh keys
253: ((ElementChilren) ch).resetKeys(newDescription.subs,
254: newDescription.ui.getFilters());
255:
256: // Reread nodes
257: nodes = ch.getNodes(true);
258:
259: for (Description newSub : newDescription.subs) {
260: ElementNode node = oldD2node.get(newSub);
261: if (node != null) { // filtered out
262: if (!oldSubs.contains(newSub)
263: && node.getChildren() != Children.LEAF) {
264: description.ui.expandNode(node); // Make sure new nodes get expanded
265: }
266: node.updateRecursively(newSub); // update the node recursively
267: }
268: }
269: }
270:
271: Description oldDescription = description; // Remember old description
272: description = newDescription; // set new descrioption to the new node
273: if (oldDescription.htmlHeader != null
274: && !oldDescription.htmlHeader
275: .equals(description.htmlHeader)) {
276: // Different headers => we need to fire displayname change
277: fireDisplayNameChange(oldDescription.htmlHeader,
278: description.htmlHeader);
279: }
280: if (oldDescription.modifiers != null
281: && !oldDescription.modifiers
282: .equals(newDescription.modifiers)) {
283: fireIconChange();
284: fireOpenedIconChange();
285: }
286: }
287:
288: public Description getDescritption() {
289: return description;
290: }
291:
292: private static final class ElementChilren extends
293: Children.Keys<Description> {
294:
295: public ElementChilren(List<Description> descriptions,
296: ClassMemberFilters filters) {
297: resetKeys(descriptions, filters);
298: }
299:
300: protected Node[] createNodes(Description key) {
301: return new Node[] { new ElementNode(key) };
302: }
303:
304: void resetKeys(List<Description> descriptions,
305: ClassMemberFilters filters) {
306: setKeys(filters.filter(descriptions));
307: }
308:
309: }
310:
311: /** Stores all interesting data about given element.
312: */
313: static class Description {
314:
315: public static final Comparator<Description> ALPHA_COMPARATOR = new DescriptionComparator(
316: true);
317: public static final Comparator<Description> POSITION_COMPARATOR = new DescriptionComparator(
318: false);
319:
320: ClassMemberPanelUI ui;
321:
322: FileObject fileObject; // For the root description
323:
324: final String name;
325: final ElementHandle<? extends Element> elementHandle;
326: final TreePathHandle treePathHandle;
327: final ElementKind kind;
328: Set<Modifier> modifiers;
329: List<Description> subs;
330: String htmlHeader;
331: long pos;
332: boolean isInherited;
333: ClasspathInfo cpInfo;
334:
335: Description(ClassMemberPanelUI ui) {
336: this .ui = ui;
337: this .name = null;
338: this .elementHandle = null;
339: this .treePathHandle = null;
340: this .kind = null;
341: this .isInherited = false;
342: }
343:
344: Description(ClassMemberPanelUI ui, String name,
345: ElementHandle<? extends Element> elementHandle,
346: ElementKind kind, TreePathHandle tpHandle,
347: boolean inherited) {
348: this .ui = ui;
349: this .name = name;
350: this .elementHandle = elementHandle;
351: this .kind = kind;
352: this .isInherited = inherited;
353: this .treePathHandle = tpHandle;
354: }
355:
356: public FileObject getFileObject() {
357: if (!isInherited)
358: return ui.getFileObject();
359: return SourceUtils.getFile(elementHandle, cpInfo);
360: }
361:
362: @Override
363: public boolean equals(Object o) {
364:
365: if (o == null) {
366: //System.out.println("- f nul");
367: return false;
368: }
369:
370: if (!(o instanceof Description)) {
371: // System.out.println("- not a desc");
372: return false;
373: }
374:
375: Description d = (Description) o;
376:
377: if (kind != d.kind) {
378: // System.out.println("- kind");
379: return false;
380: }
381:
382: if (!name.equals(d.name)) {
383: // System.out.println("- name");
384: return false;
385: }
386:
387: if (!this .elementHandle.signatureEquals(d.elementHandle)) {
388: return false;
389: }
390:
391: /*
392: if ( !modifiers.equals(d.modifiers)) {
393: // E.println("- modifiers");
394: return false;
395: }
396: */
397:
398: // System.out.println("Equals called");
399: return true;
400: }
401:
402: public int hashCode() {
403: int hash = 7;
404:
405: hash = 29 * hash
406: + (this .name != null ? this .name.hashCode() : 0);
407: hash = 29 * hash
408: + (this .kind != null ? this .kind.hashCode() : 0);
409: // hash = 29 * hash + (this.modifiers != null ? this.modifiers.hashCode() : 0);
410: return hash;
411: }
412:
413: private static class DescriptionComparator implements
414: Comparator<Description> {
415:
416: boolean alpha;
417:
418: DescriptionComparator(boolean alpha) {
419: this .alpha = alpha;
420: }
421:
422: public int compare(Description d1, Description d2) {
423:
424: if (alpha) {
425: return alphaCompare(d1, d2);
426: } else {
427: if (d1.isInherited && !d2.isInherited)
428: return 1;
429: if (!d1.isInherited && d2.isInherited)
430: return -1;
431: if (d1.isInherited && d2.isInherited) {
432: return alphaCompare(d1, d2);
433: }
434: return d1.pos == d2.pos ? 0 : d1.pos < d2.pos ? -1
435: : 1;
436: }
437: }
438:
439: int alphaCompare(Description d1, Description d2) {
440: if (k2i(d1.kind) != k2i(d2.kind)) {
441: return k2i(d1.kind) - k2i(d2.kind);
442: }
443:
444: return d1.name.compareTo(d2.name);
445: }
446:
447: int k2i(ElementKind kind) {
448: switch (kind) {
449: case CONSTRUCTOR:
450: return 1;
451: case METHOD:
452: return 2;
453: case FIELD:
454: return 3;
455: case CLASS:
456: case INTERFACE:
457: case ENUM:
458: case ANNOTATION_TYPE:
459: return 4;
460: default:
461: return 100;
462: }
463: }
464:
465: }
466:
467: }
468:
469: private static class WaitNode extends AbstractNode {
470:
471: private Image waitIcon = Utilities
472: .loadImage("org/netbeans/modules/java/navigation/resources/wait.gif"); // NOI18N
473:
474: WaitNode() {
475: super (Children.LEAF);
476: }
477:
478: @Override
479: public Image getIcon(int type) {
480: return waitIcon;
481: }
482:
483: @Override
484: public Image getOpenedIcon(int type) {
485: return getIcon(type);
486: }
487:
488: @java.lang.Override
489: public java.lang.String getDisplayName() {
490: return NbBundle.getMessage(ElementNode.class,
491: "LBL_WaitNode"); // NOI18N
492: }
493:
494: }
495:
496: }
|