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-2006 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.gsfret.navigation;
042:
043: import java.awt.Image;
044: import java.util.Comparator;
045: import java.util.HashMap;
046: import java.util.HashSet;
047: import java.util.List;
048: import java.util.Set;
049: import javax.swing.Action;
050: import javax.swing.Icon;
051: import org.netbeans.modules.gsf.api.ElementHandle;
052: import org.netbeans.modules.gsf.api.ElementKind;
053: import org.netbeans.modules.gsf.api.Modifier;
054: import org.netbeans.modules.gsf.api.StructureItem;
055: import org.netbeans.modules.gsf.api.StructureItem;
056: import org.netbeans.modules.gsfret.navigation.actions.OpenAction;
057: import org.openide.filesystems.FileObject;
058: import org.openide.nodes.AbstractNode;
059: import org.openide.nodes.Children;
060: import org.openide.nodes.Children;
061: import org.openide.nodes.Node;
062: import org.openide.util.Utilities;
063:
064: /**
065: * This file is originally from Retouche, the Java Support
066: * infrastructure in NetBeans. I have modified the file as little
067: * as possible to make merging Retouche fixes back as simple as
068: * possible.
069: * <p>
070: * GSF changes made: Instead of accessing fields on Description object,
071: * replace references to Description with StructureItem interface (descriptions
072: * supplied by language plugins), make method calls on this interface rather
073: * than accessing fields directly. Some data such as the "ui" field was moved
074: * into ElementNode itself rather than sitting on the description object which
075: * is no longer under our control.
076: * <p>
077: * Node representing an element
078: *
079: *
080: * @author Petr Hrebejk
081: */
082: public class ElementNode extends AbstractNode {
083:
084: private static Node WAIT_NODE;
085:
086: private OpenAction openAction;
087: private StructureItem description;
088: private ClassMemberPanelUI ui;
089: private FileObject fileObject; // For the root description
090:
091: /** Creates a new instance of TreeNode */
092: public ElementNode(StructureItem description,
093: ClassMemberPanelUI ui, FileObject fileObject) {
094: super (description.isLeaf() ? Children.LEAF
095: : new ElementChilren((List<StructureItem>) description
096: .getNestedItems(), ui.getFilters(), ui,
097: fileObject));
098: this .description = description;
099: setDisplayName(description.getName());
100: this .ui = ui;
101: this .fileObject = fileObject;
102: }
103:
104: @Override
105: public Image getIcon(int type) {
106: Icon icon = Icons.getElementIcon(description.getKind(),
107: description.getModifiers());
108: if (icon != null) {
109: return Utilities.icon2Image(icon);
110: } else {
111: return super .getIcon(type);
112: }
113: }
114:
115: @Override
116: public Image getOpenedIcon(int type) {
117: return getIcon(type);
118: }
119:
120: @Override
121: public java.lang.String getDisplayName() {
122: return description.getName();
123: }
124:
125: @Override
126: public String getHtmlDisplayName() {
127: return description.getHtml();
128: }
129:
130: @Override
131: public Action[] getActions(boolean context) {
132:
133: if (context || description.getName() == null) {
134: return ui.getActions();
135: } else {
136: Action panelActions[] = ui.getActions();
137:
138: Action actions[] = new Action[2 + panelActions.length];
139: actions[0] = getOpenAction();
140: actions[1] = null;
141: for (int i = 0; i < panelActions.length; i++) {
142: actions[2 + i] = panelActions[i];
143: }
144: return actions;
145: }
146: }
147:
148: @Override
149: public Action getPreferredAction() {
150: return getOpenAction();
151: }
152:
153: private synchronized Action getOpenAction() {
154: if (openAction == null) {
155: FileObject fo = ui.getFileObject();
156: openAction = new OpenAction(description.getElementHandle(),
157: fo);
158: }
159: return openAction;
160: }
161:
162: static synchronized Node getWaitNode() {
163: if (WAIT_NODE == null) {
164: WAIT_NODE = new WaitNode();
165: }
166: return WAIT_NODE;
167: }
168:
169: public void refreshRecursively() {
170: Children ch = getChildren();
171: if (ch instanceof ElementChilren) {
172: ((ElementChilren) ch).resetKeys(
173: (List<StructureItem>) description.getNestedItems(),
174: ui.getFilters());
175: for (Node sub : ch.getNodes()) {
176: ui.expandNode(sub);
177: ((ElementNode) sub).refreshRecursively();
178: }
179: }
180: }
181:
182: public ElementNode getNodeForOffset(int offset) {
183: if (getDescription().getPosition() > offset) {
184: return null;
185: }
186:
187: // Inefficient linear search because the children may not be
188: // ordered according to the source
189: Children ch = getChildren();
190: if (ch instanceof ElementChilren) {
191: Node[] children = ch.getNodes();
192: for (int i = 0; i < children.length; i++) {
193: ElementNode c = (ElementNode) children[i];
194: long start = c.getDescription().getPosition();
195: if (start <= offset) {
196: long end = c.getDescription().getEndPosition();
197: if (end >= offset) {
198: return c.getNodeForOffset(offset);
199: }
200: }
201: }
202: }
203:
204: return this ;
205: }
206:
207: public void updateRecursively(StructureItem newDescription) {
208: Children ch = getChildren();
209: if (ch instanceof ElementChilren) {
210: HashSet<StructureItem> oldSubs = new HashSet<StructureItem>(
211: description.getNestedItems());
212:
213: // Create a hashtable which maps StructureItem to node.
214: // We will then identify the nodes by the description. The trick is
215: // that the new and old description are equal and have the same hashcode
216: Node[] nodes = ch.getNodes(true);
217: HashMap<StructureItem, ElementNode> oldD2node = new HashMap<StructureItem, ElementNode>();
218: for (Node node : nodes) {
219: oldD2node.put(((ElementNode) node).description,
220: (ElementNode) node);
221: }
222:
223: // Now refresh keys
224: ((ElementChilren) ch).resetKeys(
225: (List<StructureItem>) newDescription
226: .getNestedItems(), ui.getFilters());
227:
228: // Reread nodes
229: nodes = ch.getNodes(true);
230:
231: for (StructureItem newSub : newDescription.getNestedItems()) {
232: ElementNode node = oldD2node.get(newSub);
233: if (node != null) { // filtered out
234: if (!oldSubs.contains(newSub)
235: && node.getChildren() != Children.LEAF) {
236: ui.expandNode(node); // Make sure new nodes get expanded
237: }
238: node.updateRecursively(newSub); // update the node recursively
239: }
240: }
241: }
242:
243: StructureItem oldDescription = description; // Remember old description
244: description = newDescription; // set new descrioption to the new node
245: if (oldDescription.getHtml() != null
246: && !oldDescription.getHtml().equals(
247: description.getHtml())) {
248: // Different headers => we need to fire displayname change
249: fireDisplayNameChange(oldDescription.getHtml(), description
250: .getHtml());
251: }
252: if (oldDescription.getModifiers() != null
253: && !oldDescription.getModifiers().equals(
254: newDescription.getModifiers())) {
255: fireIconChange();
256: fireOpenedIconChange();
257: }
258: }
259:
260: public StructureItem getDescription() {
261: return description;
262: }
263:
264: public FileObject getFileObject() {
265: return fileObject;
266: }
267:
268: // XXX There's a typo in this name
269: private static final class ElementChilren extends
270: Children.Keys<StructureItem> {
271: private ClassMemberPanelUI ui;
272: private FileObject fileObject;
273:
274: public ElementChilren(List<StructureItem> descriptions,
275: ClassMemberFilters filters, ClassMemberPanelUI ui,
276: FileObject fileObject) {
277: resetKeys(descriptions, filters);
278: this .ui = ui;
279: this .fileObject = fileObject;
280: }
281:
282: protected Node[] createNodes(StructureItem key) {
283: return new Node[] { new ElementNode(key, ui, fileObject) };
284: }
285:
286: void resetKeys(List<StructureItem> descriptions,
287: ClassMemberFilters filters) {
288: setKeys(filters.filter(descriptions));
289: }
290: }
291:
292: /** Stores all interesting data about given element.
293: */
294: static class Description {
295:
296: public static final Comparator<StructureItem> ALPHA_COMPARATOR = new DescriptionComparator(
297: true);
298: public static final Comparator<StructureItem> POSITION_COMPARATOR = new DescriptionComparator(
299: false);
300:
301: ClassMemberPanelUI ui;
302:
303: //FileObject fileObject; // For the root description
304:
305: String name;
306: ElementHandle elementHandle;
307: ElementKind kind;
308: Set<Modifier> modifiers;
309: List<Description> subs;
310: String htmlHeader;
311: long pos;
312:
313: Description(ClassMemberPanelUI ui) {
314: this .ui = ui;
315: }
316:
317: @Override
318: public boolean equals(Object o) {
319:
320: if (o == null) {
321: //System.out.println("- f nul");
322: return false;
323: }
324:
325: if (!(o instanceof Description)) {
326: // System.out.println("- not a desc");
327: return false;
328: }
329:
330: Description d = (Description) o;
331:
332: if (kind != d.kind) {
333: // System.out.println("- kind");
334: return false;
335: }
336:
337: // Findbugs warns about this field being uninitialized on the following line!
338: if (!name.equals(d.name)) {
339: // System.out.println("- name");
340: return false;
341: }
342:
343: // if ( !this.elementHandle.signatureEquals(d.elementHandle) ) {
344: // return false;
345: // }
346:
347: /*
348: if ( !modifiers.equals(d.modifiers)) {
349: // E.println("- modifiers");
350: return false;
351: }
352: */
353:
354: // System.out.println("Equals called");
355: return true;
356: }
357:
358: @Override
359: public int hashCode() {
360: int hash = 7;
361:
362: hash = 29 * hash
363: + (this .name != null ? this .name.hashCode() : 0);
364: hash = 29 * hash
365: + (this .kind != null ? this .kind.hashCode() : 0);
366: // hash = 29 * hash + (this.modifiers != null ? this.modifiers.hashCode() : 0);
367: return hash;
368: }
369:
370: private static class DescriptionComparator implements
371: Comparator<StructureItem> {
372:
373: boolean alpha;
374:
375: DescriptionComparator(boolean alpha) {
376: this .alpha = alpha;
377: }
378:
379: public int compare(StructureItem d1, StructureItem d2) {
380: if (alpha) {
381: if (k2i(d1.getKind()) != k2i(d2.getKind())) {
382: return k2i(d1.getKind()) - k2i(d2.getKind());
383: }
384:
385: return d1.getName().compareTo(d2.getName());
386: } else {
387: return d1.getPosition() == d2.getPosition() ? 0
388: : d1.getPosition() < d2.getPosition() ? -1
389: : 1;
390: }
391: }
392:
393: int k2i(ElementKind kind) {
394: switch (kind) {
395: case CONSTRUCTOR:
396: return 1;
397: case METHOD:
398: case DB:
399: return 2;
400: case FIELD:
401: return 3;
402: case CLASS:
403: // case INTERFACE:
404: // case ENUM:
405: // case ANNOTATION_TYPE:
406: // return 4;
407:
408: // TODO - what about other types?
409: default:
410: return 100;
411: }
412: }
413: }
414:
415: }
416:
417: private static class WaitNode extends AbstractNode {
418:
419: private Image waitIcon = Utilities
420: .loadImage("org/netbeans/modules/gsfret/navigation/resources/wait.gif"); // NOI18N
421:
422: WaitNode() {
423: super (Children.LEAF);
424: }
425:
426: @Override
427: public Image getIcon(int type) {
428: return waitIcon;
429: }
430:
431: @Override
432: public Image getOpenedIcon(int type) {
433: return getIcon(type);
434: }
435:
436: @java.lang.Override
437: public java.lang.String getDisplayName() {
438: return "Please Wait...";
439: }
440:
441: }
442:
443: }
|