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:
042: package org.netbeans.modules.java.navigation;
043:
044: import java.util.EnumSet;
045: import java.util.List;
046: import java.util.Set;
047: import java.util.regex.Matcher;
048: import java.util.regex.Pattern;
049: import java.util.regex.PatternSyntaxException;
050: import javax.lang.model.element.AnnotationValue;
051:
052: import javax.lang.model.element.Element;
053: import javax.swing.JTree;
054: import javax.swing.SwingUtilities;
055: import javax.lang.model.element.ExecutableElement;
056: import javax.lang.model.element.Modifier;
057: import javax.lang.model.element.PackageElement;
058: import javax.lang.model.element.TypeElement;
059: import javax.lang.model.element.TypeParameterElement;
060: import javax.lang.model.element.VariableElement;
061: import javax.lang.model.type.ArrayType;
062: import javax.lang.model.type.DeclaredType;
063: import static javax.lang.model.type.TypeKind.*;
064: import javax.lang.model.type.TypeMirror;
065: import javax.lang.model.type.TypeVariable;
066: import javax.lang.model.type.WildcardType;
067:
068: /**
069: *
070: * @author Sandip Chitale (Sandip.Chitale@Sun.Com)
071: */
072: class Utils {
073: static String format(Element element) {
074: return format(element, false, false);
075: }
076:
077: static String format(Element element, boolean forSignature,
078: boolean FQNs) {
079: StringBuilder stringBuilder = new StringBuilder();
080: format(element, stringBuilder, forSignature, FQNs);
081:
082: return stringBuilder.toString();
083: }
084:
085: static void format(Element element, StringBuilder stringBuilder,
086: boolean forSignature, boolean FQNs) {
087: if (element == null) {
088: return;
089: }
090:
091: boolean first = true;
092: Set<Modifier> modifiers = element.getModifiers();
093:
094: switch (element.getKind()) {
095: case PACKAGE:
096:
097: PackageElement packageElement = (PackageElement) element;
098: if (forSignature) {
099: stringBuilder.append("package "); // NOI18N
100: }
101: stringBuilder.append(packageElement.getQualifiedName());
102: break;
103:
104: case CLASS:
105: case INTERFACE:
106: case ENUM:
107: case ANNOTATION_TYPE:
108: if (forSignature) {
109: stringBuilder.append(toString(modifiers));
110: if (modifiers.size() > 0) {
111: if (stringBuilder.length() > 0) {
112: stringBuilder.append(" ");
113: }
114: }
115: }
116:
117: if (forSignature) {
118: switch (element.getKind()) {
119: case CLASS:
120: stringBuilder.append("class "); // NOI18N
121: break;
122: case INTERFACE:
123: stringBuilder.append("interface "); // NOI18N
124: break;
125: case ENUM:
126: stringBuilder.append("enum "); // NOI18N
127: break;
128: case ANNOTATION_TYPE:
129: stringBuilder.append("@interface "); // NOI18N
130: break;
131: }
132: }
133:
134: TypeElement typeElement = (TypeElement) element;
135: stringBuilder.append(FQNs ? typeElement.getQualifiedName()
136: .toString() : typeElement.getSimpleName()
137: .toString());
138:
139: formatTypeParameters(typeElement.getTypeParameters(),
140: stringBuilder, FQNs);
141:
142: break;
143:
144: case CONSTRUCTOR:
145: if (forSignature) {
146: stringBuilder.append(toString(modifiers));
147: if (modifiers.size() > 0) {
148: if (stringBuilder.length() > 0) {
149: stringBuilder.append(" ");
150: }
151: }
152: }
153:
154: ExecutableElement constructorElement = (ExecutableElement) element;
155: stringBuilder.append(constructorElement
156: .getEnclosingElement().getSimpleName().toString());
157: stringBuilder.append("(");
158: formatVariableElements(constructorElement.getParameters(),
159: constructorElement.isVarArgs(), stringBuilder, FQNs);
160: stringBuilder.append(")");
161:
162: List<? extends TypeMirror> thrownTypesMirrors = constructorElement
163: .getThrownTypes();
164: if (!thrownTypesMirrors.isEmpty()) {
165: stringBuilder.append(" throws "); // NOI18N
166: formatTypeMirrors(thrownTypesMirrors, stringBuilder,
167: FQNs);
168: }
169:
170: break;
171:
172: case METHOD:
173: ExecutableElement methodElement = (ExecutableElement) element;
174: TypeMirror returnTypeMirror = methodElement.getReturnType();
175: List<? extends TypeParameterElement> typeParameters = methodElement
176: .getTypeParameters();
177:
178: if (forSignature) {
179: stringBuilder.append(toString(modifiers));
180:
181: if (modifiers.size() > 0) {
182: if (stringBuilder.length() > 0) {
183: stringBuilder.append(" ");
184: }
185: }
186:
187: if ((typeParameters != null)
188: && (typeParameters.size() > 0)) {
189: formatTypeParameters(typeParameters, stringBuilder,
190: FQNs);
191: if (stringBuilder.length() > 0) {
192: stringBuilder.append(" ");
193: }
194: }
195:
196: formatTypeMirror(returnTypeMirror, stringBuilder, FQNs);
197: }
198:
199: if (stringBuilder.length() > 0) {
200: stringBuilder.append(" ");
201: }
202:
203: stringBuilder.append(methodElement.getSimpleName()
204: .toString());
205: stringBuilder.append("(");
206: formatVariableElements(methodElement.getParameters(),
207: methodElement.isVarArgs(), stringBuilder, FQNs);
208: stringBuilder.append(")");
209:
210: List<? extends TypeMirror> thrownTypesMirrorsByMethod = methodElement
211: .getThrownTypes();
212: if (!thrownTypesMirrorsByMethod.isEmpty()) {
213: stringBuilder.append(" throws "); // NOI18N
214: formatTypeMirrors(thrownTypesMirrorsByMethod,
215: stringBuilder, FQNs);
216: }
217:
218: if (forSignature) {
219: AnnotationValue annotationValue = methodElement
220: .getDefaultValue();
221: if (annotationValue != null) {
222: Object annotationValueValue = annotationValue
223: .getValue();
224: if (annotationValueValue != null) {
225: stringBuilder.append(" default "); // NOI18N
226: if (annotationValueValue instanceof String) {
227: stringBuilder.append("\"");
228: } else if (annotationValueValue instanceof Character) {
229: stringBuilder.append("\'");
230: }
231: stringBuilder.append(String
232: .valueOf(annotationValueValue));
233: if (annotationValueValue instanceof String) {
234: stringBuilder.append("\"");
235: } else if (annotationValueValue instanceof Character) {
236: stringBuilder.append("\'");
237: }
238: }
239: }
240: } else {
241: stringBuilder.append(":");
242:
243: formatTypeMirror(returnTypeMirror, stringBuilder, FQNs);
244:
245: if ((typeParameters != null)
246: && (typeParameters.size() > 0)) {
247: stringBuilder.append(":");
248: formatTypeParameters(typeParameters, stringBuilder,
249: FQNs);
250: }
251:
252: if (JavaMembersAndHierarchyOptions.isShowInherited()) {
253: stringBuilder.append(" [");
254: stringBuilder.append(getClassName(element
255: .getEnclosingElement().toString(), FQNs));
256: stringBuilder.append("]");
257: }
258: }
259:
260: break;
261:
262: case TYPE_PARAMETER:
263: TypeParameterElement typeParameterElement = (TypeParameterElement) element;
264: stringBuilder.append(typeParameterElement.getSimpleName());
265:
266: List<? extends TypeMirror> bounds = null;
267: try {
268: bounds = typeParameterElement.getBounds();
269: if ((bounds != null) && (bounds.size() > 0)) {
270: if (bounds.size() == 1
271: && "java.lang.Object".equals(bounds.get(0)
272: .toString())) { // NOI18N
273: } else {
274: stringBuilder.append(" extends "); // NOI18N
275: first = true;
276: for (TypeMirror typeMirror : bounds) {
277: if (first) {
278: first = false;
279: } else {
280: stringBuilder.append(" & "); // NOI18N
281: }
282: formatTypeMirror(typeMirror, stringBuilder,
283: FQNs);
284: }
285: }
286: }
287: } catch (NullPointerException npe) {
288: // Bug?
289: }
290:
291: break;
292:
293: case FIELD:
294: VariableElement fieldElement = (VariableElement) element;
295: if (forSignature) {
296: stringBuilder.append(toString(modifiers));
297:
298: if (stringBuilder.length() > 0) {
299: stringBuilder.append(" ");
300: }
301:
302: formatTypeMirror(fieldElement.asType(), stringBuilder,
303: FQNs);
304: }
305:
306: if (stringBuilder.length() > 0) {
307: stringBuilder.append(" ");
308: }
309:
310: stringBuilder.append(fieldElement.getSimpleName()
311: .toString());
312:
313: if (forSignature) {
314: Object fieldValue = fieldElement.getConstantValue();
315: if (fieldValue != null) {
316: stringBuilder.append(" = ");
317: if (fieldValue instanceof String) {
318: stringBuilder.append("\"");
319: } else if (fieldValue instanceof Character) {
320: stringBuilder.append("\'");
321: }
322: stringBuilder.append(String.valueOf(fieldValue));
323: if (fieldValue instanceof String) {
324: stringBuilder.append("\"");
325: } else if (fieldValue instanceof Character) {
326: stringBuilder.append("\'");
327: }
328: }
329: } else {
330: stringBuilder.append(":");
331:
332: formatTypeMirror(fieldElement.asType(), stringBuilder,
333: FQNs);
334:
335: if (JavaMembersAndHierarchyOptions.isShowInherited()) {
336: stringBuilder.append(" [");
337: stringBuilder.append(getClassName(element
338: .getEnclosingElement().toString(), FQNs));
339: stringBuilder.append("]");
340: }
341: }
342:
343: break;
344:
345: case ENUM_CONSTANT:
346: stringBuilder.append(element.toString());
347:
348: if (JavaMembersAndHierarchyOptions.isShowInherited()) {
349: stringBuilder.append(" [");
350: stringBuilder.append(getClassName(element
351: .getEnclosingElement().toString(), FQNs));
352: stringBuilder.append("]");
353: }
354:
355: break;
356:
357: case PARAMETER:
358: case LOCAL_VARIABLE:
359: VariableElement variableElement = (VariableElement) element;
360: formatTypeMirror(variableElement.asType(), stringBuilder,
361: FQNs);
362: stringBuilder.append(" ");
363: stringBuilder.append(element.getSimpleName().toString());
364:
365: break;
366: }
367: }
368:
369: static void formatTypeMirror(TypeMirror typeMirror,
370: StringBuilder stringBuilder, boolean FQNs) {
371: if (typeMirror == null) {
372: return;
373: }
374:
375: boolean first = true;
376:
377: switch (typeMirror.getKind()) {
378: case BOOLEAN:
379: case BYTE:
380: case CHAR:
381: case DOUBLE:
382: case FLOAT:
383: case INT:
384: case LONG:
385: case NONE:
386: case NULL:
387: case SHORT:
388: case VOID:
389: stringBuilder.append(typeMirror);
390:
391: break;
392:
393: case TYPEVAR:
394: TypeVariable typeVariable = (TypeVariable) typeMirror;
395: stringBuilder.append(typeVariable.asElement()
396: .getSimpleName().toString());
397: break;
398:
399: case WILDCARD:
400: WildcardType wildcardType = (WildcardType) typeMirror;
401: stringBuilder.append("?");
402: if (wildcardType.getExtendsBound() != null) {
403: stringBuilder.append(" extends "); // NOI18N
404: formatTypeMirror(wildcardType.getExtendsBound(),
405: stringBuilder, FQNs);
406: }
407: if (wildcardType.getSuperBound() != null) {
408: stringBuilder.append(" super "); // NOI18N
409: formatTypeMirror(wildcardType.getSuperBound(),
410: stringBuilder, FQNs);
411: }
412:
413: break;
414:
415: case DECLARED:
416: DeclaredType declaredType = (DeclaredType) typeMirror;
417: Element element = declaredType.asElement();
418: if (element instanceof TypeElement) {
419: stringBuilder.append(FQNs ? ((TypeElement) element)
420: .getQualifiedName().toString() : element
421: .getSimpleName().toString());
422: } else {
423: stringBuilder
424: .append(element.getSimpleName().toString());
425: }
426: List<? extends TypeMirror> typeArgs = declaredType
427: .getTypeArguments();
428: if (!typeArgs.isEmpty()) {
429: stringBuilder.append("<");
430: formatTypeMirrors(typeArgs, stringBuilder, FQNs);
431: stringBuilder.append(">");
432: }
433:
434: break;
435:
436: case ARRAY:
437:
438: int dims = 0;
439:
440: while (typeMirror.getKind() == ARRAY) {
441: dims++;
442: typeMirror = ((ArrayType) typeMirror)
443: .getComponentType();
444: }
445:
446: formatTypeMirror(typeMirror, stringBuilder, FQNs);
447:
448: for (int i = 0; i < dims; i++) {
449: stringBuilder.append("[]");
450: }
451:
452: break;
453: }
454: }
455:
456: static void formatTypeParameters(
457: List<? extends TypeParameterElement> typeParameters,
458: StringBuilder stringBuilder, boolean FQNs) {
459: if ((typeParameters == null) || (typeParameters.size() == 0)) {
460: return;
461: }
462:
463: boolean first = true;
464: if (typeParameters.size() > 0) {
465: stringBuilder.append("<");
466: first = true;
467:
468: for (TypeParameterElement typeParameterElement : typeParameters) {
469: if (first) {
470: first = false;
471: } else {
472: stringBuilder.append(", ");
473: }
474:
475: format(typeParameterElement, stringBuilder, false, FQNs);
476: }
477:
478: stringBuilder.append(">");
479: }
480: }
481:
482: static void formatVariableElements(
483: List<? extends VariableElement> variableElements,
484: boolean varArgs, StringBuilder stringBuilder, boolean FQNs) {
485: if ((variableElements == null)
486: || (variableElements.size() == 0)) {
487: return;
488: }
489:
490: boolean first = true;
491:
492: for (VariableElement variableElement : variableElements) {
493: if (first) {
494: first = false;
495: } else {
496: stringBuilder.append(", ");
497: }
498:
499: format(variableElement, stringBuilder, false, FQNs);
500: }
501:
502: if (varArgs) {
503: stringBuilder.append("...");
504: }
505: }
506:
507: static void formatTypeMirrors(
508: List<? extends TypeMirror> thrownTypeMirros,
509: StringBuilder stringBuilder, boolean FQNs) {
510: if ((thrownTypeMirros == null)
511: || (thrownTypeMirros.size() == 0)) {
512: return;
513: }
514:
515: boolean first = true;
516:
517: for (TypeMirror typeMirror : thrownTypeMirros) {
518: if (first) {
519: first = false;
520: } else {
521: stringBuilder.append(", ");
522: }
523:
524: formatTypeMirror(typeMirror, stringBuilder, FQNs);
525: }
526: }
527:
528: static int getIntModifiers(Set<Modifier> modifiers) {
529: int intModifiers = 0;
530:
531: if (modifiers.contains(Modifier.ABSTRACT)) {
532: intModifiers |= java.lang.reflect.Modifier.ABSTRACT;
533: }
534:
535: if (modifiers.contains(Modifier.FINAL)) {
536: intModifiers |= java.lang.reflect.Modifier.FINAL;
537: }
538:
539: if (modifiers.contains(Modifier.NATIVE)) {
540: intModifiers |= java.lang.reflect.Modifier.NATIVE;
541: }
542:
543: if (modifiers.contains(Modifier.PRIVATE)) {
544: intModifiers |= java.lang.reflect.Modifier.PRIVATE;
545: }
546:
547: if (modifiers.contains(Modifier.PROTECTED)) {
548: intModifiers |= java.lang.reflect.Modifier.PROTECTED;
549: }
550:
551: if (modifiers.contains(Modifier.PUBLIC)) {
552: intModifiers |= java.lang.reflect.Modifier.PUBLIC;
553: }
554:
555: if (modifiers.contains(Modifier.STATIC)) {
556: intModifiers |= java.lang.reflect.Modifier.STATIC;
557: }
558:
559: if (modifiers.contains(Modifier.STRICTFP)) {
560: intModifiers |= java.lang.reflect.Modifier.STRICT;
561: }
562:
563: if (modifiers.contains(Modifier.SYNCHRONIZED)) {
564: intModifiers |= java.lang.reflect.Modifier.SYNCHRONIZED;
565: }
566:
567: if (modifiers.contains(Modifier.TRANSIENT)) {
568: intModifiers |= java.lang.reflect.Modifier.TRANSIENT;
569: }
570:
571: if (modifiers.contains(Modifier.VOLATILE)) {
572: intModifiers |= java.lang.reflect.Modifier.VOLATILE;
573: }
574:
575: return intModifiers;
576: }
577:
578: static String toString(Set<Modifier> modifiers) {
579: return java.lang.reflect.Modifier
580: .toString(getIntModifiers(modifiers));
581: }
582:
583: static Set<Modifier> getModifiers(int intModifiers) {
584: EnumSet<Modifier> modifiers = EnumSet.noneOf(Modifier.class);
585:
586: if ((intModifiers & java.lang.reflect.Modifier.ABSTRACT) != 0) {
587: modifiers.add(Modifier.ABSTRACT);
588: }
589:
590: if ((intModifiers & java.lang.reflect.Modifier.FINAL) != 0) {
591: modifiers.add(Modifier.FINAL);
592: }
593:
594: if ((intModifiers & java.lang.reflect.Modifier.NATIVE) != 0) {
595: modifiers.add(Modifier.NATIVE);
596: }
597:
598: if ((intModifiers & java.lang.reflect.Modifier.PRIVATE) != 0) {
599: modifiers.add(Modifier.PRIVATE);
600: }
601:
602: if ((intModifiers & java.lang.reflect.Modifier.PROTECTED) != 0) {
603: modifiers.add(Modifier.PROTECTED);
604: }
605:
606: if ((intModifiers & java.lang.reflect.Modifier.PUBLIC) != 0) {
607: modifiers.add(Modifier.PUBLIC);
608: }
609:
610: if ((intModifiers & java.lang.reflect.Modifier.STATIC) != 0) {
611: modifiers.add(Modifier.STATIC);
612: }
613:
614: if ((intModifiers & java.lang.reflect.Modifier.STRICT) != 0) {
615: modifiers.add(Modifier.STRICTFP);
616: }
617:
618: if ((intModifiers & java.lang.reflect.Modifier.SYNCHRONIZED) != 0) {
619: modifiers.add(Modifier.SYNCHRONIZED);
620: }
621:
622: if ((intModifiers & java.lang.reflect.Modifier.TRANSIENT) != 0) {
623: modifiers.add(Modifier.TRANSIENT);
624: }
625:
626: if ((intModifiers & java.lang.reflect.Modifier.VOLATILE) != 0) {
627: modifiers.add(Modifier.VOLATILE);
628: }
629:
630: return modifiers;
631: }
632:
633: static boolean patternMatch(JavaElement javaToolsJavaElement,
634: String pattern, String patternLowerCase) {
635:
636: if (pattern == null) {
637: return true;
638: }
639:
640: String patternRegexpString = pattern;
641:
642: if (pattern.trim().length() == 0) {
643: patternRegexpString = pattern + ".*";
644: } else {
645: // TODO FIx the replacement of * and ?
646: patternRegexpString = pattern.replaceAll(
647: Pattern.quote("*"), Matcher.quoteReplacement(".*"))
648: .replaceAll(Pattern.quote("?"),
649: Matcher.quoteReplacement("."))
650: + (pattern.endsWith("$") ? "" : ".*");
651: }
652:
653: String name = javaToolsJavaElement.getName();
654:
655: try {
656: Pattern compiledPattern = Pattern.compile(
657: patternRegexpString, JavaMembersAndHierarchyOptions
658: .isCaseSensitive() ? 0
659: : Pattern.CASE_INSENSITIVE);
660: Matcher m = compiledPattern.matcher(name);
661:
662: return m.matches();
663: } catch (PatternSyntaxException pse) {
664: if (JavaMembersAndHierarchyOptions.isCaseSensitive()) {
665: return name.startsWith(pattern);
666: }
667:
668: return name.toLowerCase().startsWith(patternLowerCase);
669: }
670: }
671:
672: static String getClassName(String className) {
673: return getClassName(className, false);
674: }
675:
676: static String getClassName(String className, boolean FQNs) {
677: // Handle generic type names i.e. strip off parameters
678: int firstLessThan = className.indexOf('<');
679:
680: if (firstLessThan != -1) {
681: className = className.substring(0, firstLessThan);
682: }
683:
684: if (!FQNs) {
685: int lastDot = className.lastIndexOf('.');
686:
687: if (lastDot != -1) {
688: className = className.substring(lastDot + 1);
689: }
690: }
691:
692: return className;
693: }
694:
695: static String getClassNameSansPackage(String className) {
696: // Handle generic type names i.e. strip off parameters
697: int firstLessThan = className.indexOf('<');
698:
699: if (firstLessThan != -1) {
700: className = className.substring(0, firstLessThan);
701: }
702:
703: int lastDot = className.lastIndexOf('.');
704:
705: if (lastDot != -1) {
706: className = className.substring(lastDot + 1);
707: }
708:
709: return className;
710: }
711:
712: // Tree movement
713: static void firstRow(JTree tree) {
714: int rowCount = tree.getRowCount();
715: if (rowCount > 0) {
716: tree.setSelectionRow(0);
717: scrollTreeToSelectedRow(tree);
718: }
719: }
720:
721: static void previousRow(JTree tree) {
722: int rowCount = tree.getRowCount();
723: if (rowCount > 0) {
724: int selectedRow = tree.getSelectionModel()
725: .getMinSelectionRow();
726: if (selectedRow == -1) {
727: selectedRow = (rowCount - 1);
728: } else {
729: selectedRow--;
730: if (selectedRow < 0) {
731: selectedRow = (rowCount - 1);
732: }
733: }
734: tree.setSelectionRow(selectedRow);
735: scrollTreeToSelectedRow(tree);
736: }
737: }
738:
739: static void nextRow(JTree tree) {
740: int rowCount = tree.getRowCount();
741: if (rowCount > 0) {
742: int selectedRow = tree.getSelectionModel()
743: .getMinSelectionRow();
744: if (selectedRow == -1) {
745: selectedRow = 0;
746: tree.setSelectionRow(selectedRow);
747: } else {
748: selectedRow++;
749: }
750: tree.setSelectionRow(selectedRow % rowCount);
751: scrollTreeToSelectedRow(tree);
752: }
753: }
754:
755: static void lastRow(JTree tree) {
756: int rowCount = tree.getRowCount();
757: if (rowCount > 0) {
758: tree.setSelectionRow(rowCount - 1);
759: scrollTreeToSelectedRow(tree);
760: }
761: }
762:
763: static void scrollTreeToSelectedRow(final JTree tree) {
764: final int selectedRow = tree.getLeadSelectionRow();
765: if (selectedRow >= 0) {
766: SwingUtilities.invokeLater(new Runnable() {
767: public void run() {
768: tree.scrollRectToVisible(tree
769: .getRowBounds(selectedRow));
770: }
771: });
772: }
773: }
774: }
|