1 19 20 package org.netbeans.modules.editor.java; 21 22 import com.sun.source.tree.ClassTree; 23 import com.sun.source.tree.EnhancedForLoopTree; 24 import com.sun.source.tree.Tree; 25 import com.sun.source.tree.VariableTree; 26 import com.sun.source.util.SourcePositions; 27 import com.sun.source.util.TreePath; 28 import com.sun.source.util.Trees; 29 30 import java.util.*; 31 32 import javax.lang.model.element.*; 33 import javax.lang.model.type.*; 34 import javax.lang.model.util.*; 35 import javax.swing.text.JTextComponent ; 36 37 import org.netbeans.api.java.lexer.JavaTokenId; 38 import org.netbeans.api.lexer.TokenHierarchy; 39 import org.netbeans.api.lexer.TokenId; 40 import org.netbeans.api.lexer.TokenSequence; 41 import org.netbeans.editor.*; 42 import org.netbeans.editor.ext.ExtSettingsDefaults; 43 import org.netbeans.editor.ext.ExtSettingsNames; 44 import org.netbeans.editor.ext.java.JavaTokenContext; 45 46 50 public class Utilities { 51 52 private static final String CAPTURED_WILDCARD = "<captured wildcard>"; private static final String ERROR = "<error>"; private static final String UNKNOWN = "<unknown>"; 56 private static boolean caseSensitive = true; 57 private static SettingsChangeListener settingsListener = new SettingsListener(); 58 private static boolean inited; 59 60 61 public static boolean startsWith(String theString, String prefix) { 62 if (theString == null || theString.length() == 0 || ERROR.equals(theString)) 63 return false; 64 if (prefix == null || prefix.length() == 0) 65 return true; 66 return isCaseSensitive() ? theString.startsWith(prefix) : 67 theString.toLowerCase().startsWith(prefix.toLowerCase()); 68 } 69 70 public static boolean isCaseSensitive() { 71 lazyInit(); 72 return caseSensitive; 73 } 74 75 public static void setCaseSensitive(boolean b) { 76 lazyInit(); 77 caseSensitive = b; 78 } 79 80 private static void lazyInit() { 81 if (!inited) { 82 inited = true; 83 Settings.addSettingsChangeListener(settingsListener); 84 setCaseSensitive(SettingsUtil.getBoolean(JavaKit.class, 85 ExtSettingsNames.COMPLETION_CASE_SENSITIVE, 86 ExtSettingsDefaults.defaultCompletionCaseSensitive)); 87 } 88 } 89 90 public static TreePath getPathElementOfKind(Tree.Kind kind, TreePath path) { 91 return getPathElementOfKind(EnumSet.of(kind), path); 92 } 93 94 public static TreePath getPathElementOfKind(EnumSet<Tree.Kind> kinds, TreePath path) { 95 while (path != null) { 96 if (kinds.contains(path.getLeaf().getKind())) 97 return path; 98 path = path.getParentPath(); 99 } 100 return null; 101 } 102 103 public static boolean isJavaContext(final JTextComponent component, final int offset) { 104 TokenSequence<JavaTokenId> ts = getJavaTokenSequence(component, offset); 105 if (ts == null) 106 return false; 107 switch(ts.token().id()) { 108 case DOUBLE_LITERAL: 109 if (ts.token().text().charAt(0) == '.') 110 break; 111 case CHAR_LITERAL: 112 case FLOAT_LITERAL: 113 case FLOAT_LITERAL_INVALID: 114 case INT_LITERAL: 115 case INVALID_COMMENT_END: 116 case JAVADOC_COMMENT: 117 case LONG_LITERAL: 118 case STRING_LITERAL: 119 case LINE_COMMENT: 120 case BLOCK_COMMENT: 121 return false; 122 } 123 return true; 124 } 125 126 public static TokenSequence<JavaTokenId> getJavaTokenSequence(final JTextComponent component, final int offset) { 127 TokenHierarchy hierarchy = TokenHierarchy.get(component.getDocument()); 128 if (hierarchy != null) { 129 TokenSequence<? extends TokenId> ts = hierarchy.tokenSequence(); 130 while(ts != null && ts.moveNext()) { 131 ts.move(offset); 132 if (!ts.moveNext()) 133 ts.movePrevious(); 134 if (ts.language() == JavaTokenId.language()) 135 return (TokenSequence<JavaTokenId>)ts; 136 ts = ts.embedded(); 137 } 138 } 139 return null; 140 } 141 142 public static CharSequence getTypeName(TypeMirror type, boolean fqn) { 143 return getTypeName(type, fqn, false); 144 } 145 146 public static CharSequence getTypeName(TypeMirror type, boolean fqn, boolean varArg) { 147 if (type == null) 148 return ""; return new TypeNameVisitor(varArg).visit(type, fqn); 150 } 151 152 public static CharSequence getElementName(Element el, boolean fqn) { 153 if (el == null || el.asType().getKind() == TypeKind.NONE) 154 return ""; return new ElementNameVisitor().visit(el, fqn); 156 } 157 158 public static Collection<? extends Element> getForwardReferences(TreePath path, int pos, SourcePositions sourcePositions, Trees trees) { 159 HashSet<Element> refs = new HashSet<Element>(); 160 while(path != null) { 161 switch(path.getLeaf().getKind()) { 162 case BLOCK: 163 case CLASS: 164 return refs; 165 case VARIABLE: 166 refs.add(trees.getElement(path)); 167 TreePath parent = path.getParentPath(); 168 if (parent.getLeaf().getKind() == Tree.Kind.CLASS) { 169 boolean isStatic = ((VariableTree)path.getLeaf()).getModifiers().getFlags().contains(Modifier.STATIC); 170 for(Tree member : ((ClassTree)parent.getLeaf()).getMembers()) { 171 if (member.getKind() == Tree.Kind.VARIABLE && sourcePositions.getStartPosition(path.getCompilationUnit(), member) >= pos && 172 (isStatic || !((VariableTree)member).getModifiers().getFlags().contains(Modifier.STATIC))) 173 refs.add(trees.getElement(new TreePath(parent, member))); 174 } 175 } 176 return refs; 177 case ENHANCED_FOR_LOOP: 178 EnhancedForLoopTree efl = (EnhancedForLoopTree)path.getLeaf(); 179 if (sourcePositions.getEndPosition(path.getCompilationUnit(), efl.getExpression()) >= pos) 180 refs.add(trees.getElement(new TreePath(path, efl.getVariable()))); 181 } 182 path = path.getParentPath(); 183 } 184 return refs; 185 } 186 187 public static List<String > varNamesSuggestions(TypeMirror type, String prefix, Types types, Elements elements, Iterable <? extends Element> locals, boolean isConst) { 188 List<String > result = new ArrayList<String >(); 189 if (type == null) 190 return result; 191 List<String > vnct = varNamesForType(type, types, elements); 192 if (isConst) { 193 List<String > ls = new ArrayList<String >(vnct.size()); 194 for (String s : vnct) 195 ls.add(getConstName(s)); 196 vnct = ls; 197 } 198 String p = prefix; 199 while (p != null && p.length() > 0) { 200 List<String > l = new ArrayList<String >(); 201 for(String name : vnct) 202 if (startsWith(name, p)) 203 l.add(name); 204 if (l.isEmpty()) { 205 p = nextName(p); 206 } else { 207 vnct = l; 208 prefix = prefix.substring(0, prefix.length() - p.length()); 209 p = null; 210 } 211 } 212 for (String name : vnct) { 213 boolean isPrimitive = type.getKind().isPrimitive(); 214 if (prefix != null && prefix.length() > 0) { 215 if (isConst) { 216 name = prefix.toUpperCase() + '_' + name; 217 } else { 218 name = prefix + Character.toUpperCase(name.charAt(0)) + name.substring(1); 219 } 220 } 221 int cnt = 1; 222 while (isClashing(name, locals)) { 223 if (isPrimitive) { 224 char c = name.charAt(0); 225 name = Character.toString(++c); 226 if (c == 'z') isPrimitive = false; 228 } else { 229 name += cnt++; 230 } 231 } 232 result.add(name); 233 } 234 return result; 235 } 236 237 public static boolean isInMethod(TreePath tp) { 238 while (tp != null) { 239 if (tp.getLeaf().getKind() == Tree.Kind.METHOD) { 240 return true; 241 } 242 243 tp = tp.getParentPath(); 244 } 245 246 return false; 247 } 248 249 private static List<String > varNamesForType(TypeMirror type, Types types, Elements elements) { 250 switch (type.getKind()) { 251 case ARRAY: 252 TypeMirror iterable = types.getDeclaredType(elements.getTypeElement("java.lang.Iterable")); TypeMirror ct = ((ArrayType)type).getComponentType(); 254 if (ct.getKind() == TypeKind.ARRAY && types.isSubtype(ct, iterable)) 255 return varNamesForType(ct, types, elements); 256 List<String > vnct = new ArrayList<String >(); 257 for (String name : varNamesForType(ct, types, elements)) 258 vnct.add(name.endsWith("s") ? name + "es" : name + "s"); return vnct; 260 case BOOLEAN: 261 case BYTE: 262 case CHAR: 263 case DOUBLE: 264 case FLOAT: 265 case INT: 266 case LONG: 267 case SHORT: 268 return Collections.<String >singletonList(type.toString().substring(0, 1)); 269 case TYPEVAR: 270 return Collections.<String >singletonList(type.toString().toLowerCase()); 271 case ERROR: 272 String tn = ((ErrorType)type).asElement().getSimpleName().toString(); 273 if (tn.toUpperCase().contentEquals(tn)) 274 return Collections.<String >singletonList(tn.toLowerCase()); 275 StringBuilder sb = new StringBuilder (); 276 ArrayList<String > al = new ArrayList<String >(); 277 if ("Iterator".equals(tn)) al.add("it"); while((tn = nextName(tn)).length() > 0) { 280 al.add(tn); 281 sb.append(tn.charAt(0)); 282 } 283 if (sb.length() > 0) 284 al.add(sb.toString()); 285 return al; 286 case DECLARED: 287 iterable = types.getDeclaredType(elements.getTypeElement("java.lang.Iterable")); tn = ((DeclaredType)type).asElement().getSimpleName().toString(); 289 if (tn.toUpperCase().contentEquals(tn)) 290 return Collections.<String >singletonList(tn.toLowerCase()); 291 sb = new StringBuilder (); 292 al = new ArrayList<String >(); 293 if ("Iterator".equals(tn)) al.add("it"); while((tn = nextName(tn)).length() > 0) { 296 al.add(tn); 297 sb.append(tn.charAt(0)); 298 } 299 if (types.isSubtype(type, iterable)) { 300 List<? extends TypeMirror> tas = ((DeclaredType)type).getTypeArguments(); 301 if (tas.size() > 0) { 302 TypeMirror et = tas.get(0); 303 if (et.getKind() == TypeKind.ARRAY || (et.getKind() != TypeKind.WILDCARD && types.isSubtype(et, iterable))) { 304 al.addAll(varNamesForType(et, types, elements)); 305 } else { 306 for (String name : varNamesForType(et, types, elements)) 307 al.add(name.endsWith("s") ? name + "es" : name + "s"); } 309 } 310 } 311 if (sb.length() > 0) 312 al.add(sb.toString()); 313 return al; 314 case WILDCARD: 315 TypeMirror bound = ((WildcardType)type).getExtendsBound(); 316 if (bound == null) 317 bound = ((WildcardType)type).getSuperBound(); 318 if (bound != null) 319 return varNamesForType(bound, types, elements); 320 } 321 return Collections.<String >emptyList(); 322 } 323 324 private static String getConstName(String s) { 325 StringBuilder sb = new StringBuilder (); 326 boolean prevUpper = false; 327 for (int i = 0; i < s.length(); i++) { 328 char c = s.charAt(i); 329 if (Character.isUpperCase(c)) { 330 if (!prevUpper) 331 sb.append('_'); 332 sb.append(c); 333 prevUpper = true; 334 } else { 335 sb.append(Character.toUpperCase(c)); 336 prevUpper = false; 337 } 338 } 339 return sb.toString(); 340 } 341 342 private static String nextName(CharSequence name) { 343 StringBuilder sb = new StringBuilder (); 344 for (int i = 0; i < name.length(); i++) { 345 char c = name.charAt(i); 346 if (Character.isUpperCase(c)) { 347 char lc = Character.toLowerCase(c); 348 sb.append(lc); 349 sb.append(name.subSequence(i + 1, name.length())); 350 break; 351 } 352 } 353 return sb.toString(); 354 } 355 356 private static boolean isClashing(String varName, Iterable <? extends Element> locals) { 357 if (JavaTokenContext.getKeyword(varName) != null) 358 return true; 359 for (Element e : locals) { 360 if ((e.getKind() == ElementKind.LOCAL_VARIABLE || e.getKind() == ElementKind.PARAMETER || e.getKind() == ElementKind.EXCEPTION_PARAMETER) && varName.contentEquals(e.getSimpleName())) 361 return true; 362 } 363 return false; 364 } 365 366 private static class SettingsListener implements SettingsChangeListener { 367 368 public void settingsChange(SettingsChangeEvent evt) { 369 setCaseSensitive(SettingsUtil.getBoolean(JavaKit.class, 370 ExtSettingsNames.COMPLETION_CASE_SENSITIVE, 371 ExtSettingsDefaults.defaultCompletionCaseSensitive)); 372 } 373 } 374 375 private static class TypeNameVisitor extends SimpleTypeVisitor6<StringBuilder ,Boolean > { 376 377 private boolean varArg; 378 379 private TypeNameVisitor(boolean varArg) { 380 super(new StringBuilder ()); 381 this.varArg = varArg; 382 } 383 384 @Override 385 public StringBuilder defaultAction(TypeMirror t, Boolean p) { 386 return DEFAULT_VALUE.append(t); 387 } 388 389 @Override 390 public StringBuilder visitDeclared(DeclaredType t, Boolean p) { 391 Element e = t.asElement(); 392 if (e instanceof TypeElement) { 393 TypeElement te = (TypeElement)e; 394 DEFAULT_VALUE.append((p ? te.getQualifiedName() : te.getSimpleName()).toString()); 395 Iterator<? extends TypeMirror> it = t.getTypeArguments().iterator(); 396 if (it.hasNext()) { 397 DEFAULT_VALUE.append("<"); while(it.hasNext()) { 399 visit(it.next(), p); 400 if (it.hasNext()) 401 DEFAULT_VALUE.append(", "); } 403 DEFAULT_VALUE.append(">"); } 405 return DEFAULT_VALUE; 406 } else { 407 return DEFAULT_VALUE.append(UNKNOWN); } 409 } 410 411 @Override 412 public StringBuilder visitArray(ArrayType t, Boolean p) { 413 boolean isVarArg = varArg; 414 varArg = false; 415 visit(t.getComponentType(), p); 416 return DEFAULT_VALUE.append(isVarArg ? "..." : "[]"); } 418 419 @Override 420 public StringBuilder visitTypeVariable(TypeVariable t, Boolean p) { 421 Element e = t.asElement(); 422 if (e != null) { 423 String name = e.getSimpleName().toString(); 424 if (!CAPTURED_WILDCARD.equals(name)) 425 return DEFAULT_VALUE.append(name); 426 } 427 DEFAULT_VALUE.append("?"); TypeMirror bound = t.getLowerBound(); 429 if (bound != null && bound.getKind() != TypeKind.NULL) { 430 DEFAULT_VALUE.append(" super "); visit(bound, p); 432 } else { 433 bound = t.getUpperBound(); 434 if (bound != null && bound.getKind() != TypeKind.NULL) { 435 DEFAULT_VALUE.append(" extends "); if (bound.getKind() == TypeKind.TYPEVAR) 437 bound = ((TypeVariable)bound).getLowerBound(); 438 visit(bound, p); 439 } 440 } 441 return DEFAULT_VALUE; 442 } 443 444 @Override 445 public StringBuilder visitWildcard(WildcardType t, Boolean p) { 446 DEFAULT_VALUE.append("?"); TypeMirror bound = t.getSuperBound(); 448 if (bound == null) { 449 bound = t.getExtendsBound(); 450 if (bound != null) { 451 DEFAULT_VALUE.append(" extends "); if (bound.getKind() == TypeKind.WILDCARD) 453 bound = ((WildcardType)bound).getSuperBound(); 454 visit(bound, p); 455 } 456 } else { 457 DEFAULT_VALUE.append(" super "); visit(bound, p); 459 } 460 return DEFAULT_VALUE; 461 } 462 463 public StringBuilder visitError(ErrorType t, Boolean p) { 464 Element e = t.asElement(); 465 if (e instanceof TypeElement) { 466 TypeElement te = (TypeElement)e; 467 return DEFAULT_VALUE.append((p ? te.getQualifiedName() : te.getSimpleName()).toString()); 468 } 469 return DEFAULT_VALUE; 470 } 471 } 472 473 private static class ElementNameVisitor extends SimpleElementVisitor6<StringBuilder ,Boolean > { 474 475 private ElementNameVisitor() { 476 super(new StringBuilder ()); 477 } 478 479 @Override 480 public StringBuilder visitPackage(PackageElement e, Boolean p) { 481 return DEFAULT_VALUE.append((p ? e.getQualifiedName() : e.getSimpleName()).toString()); 482 } 483 484 @Override 485 public StringBuilder visitType(TypeElement e, Boolean p) { 486 return DEFAULT_VALUE.append((p ? e.getQualifiedName() : e.getSimpleName()).toString()); 487 } 488 } 489 } 490 | Popular Tags |