001package daikon.tools.jtb; 002 003import daikon.*; 004import daikon.inv.Equality; 005import daikon.inv.Invariant; 006import daikon.inv.filter.*; 007import java.io.PrintWriter; 008import java.io.Reader; 009import java.io.StringReader; 010import java.io.StringWriter; 011import java.io.Writer; 012import java.lang.reflect.Constructor; 013import java.lang.reflect.Method; 014import java.util.ArrayList; 015import java.util.Arrays; 016import java.util.Enumeration; 017import java.util.HashSet; 018import java.util.Iterator; 019import java.util.List; 020import java.util.Set; 021import java.util.Vector; 022import jtb.JavaParser; 023import jtb.ParseException; 024import jtb.syntaxtree.*; 025import jtb.visitor.*; 026import org.checkerframework.checker.nullness.qual.Nullable; 027import org.checkerframework.checker.signature.qual.BinaryName; 028import org.checkerframework.checker.signature.qual.ClassGetName; 029import org.checkerframework.checker.signature.qual.Identifier; 030import org.plumelib.reflection.Signatures; 031import org.plumelib.util.StringsPlume; 032 033/** Static methods for manipulating the AST. */ 034@SuppressWarnings({"rawtypes", "nullness"}) // not generics-correct 035public class Ast { 036 037 /** Do not instantiate. */ 038 private Ast() { 039 throw new Error("Do not instantiate"); 040 } 041 042 /** The line separator. */ 043 private static final String lineSep = System.lineSeparator(); 044 045 // /////////////////////////////////////////////////////////////////////////// 046 // Visitors 047 // 048 049 // Reads an AST from the input stream, applies the visitor to the AST, 050 // reformats only to insert comments, and writes the resulting AST to the 051 // output stream. 052 public static void applyVisitorInsertComments( 053 String javafilename, Node root, Writer output, AnnotateVisitor visitor) { 054 root.accept(visitor); 055 root.accept(new InsertCommentFormatter(visitor.addedComments)); 056 try (PrintWriter writer = new PrintWriter(output, true)) { 057 for (int i = 0; i < visitor.javaFileLines.size(); i++) { 058 writer.println(visitor.javaFileLines.get(i)); 059 } 060 } 061 062 // root.accept(new TreeDumper(output)); 063 } 064 065 // Reads an AST from the input stream, applies the visitor to the AST, 066 // completely reformats the Ast (losing previous formatting), and writes 067 // the resulting AST to the output stream. 068 public static void applyVisitorReformat(Reader input, Writer output, Visitor visitor) { 069 JavaParser parser = new JavaParser(input); 070 Node root; 071 try { 072 root = parser.CompilationUnit(); 073 } catch (ParseException e) { 074 e.printStackTrace(); 075 throw new Daikon.UserError("ParseException in applyVisitorReformat"); 076 } 077 root.accept(visitor); 078 // This is unfortunately necessary because TreeDumper dies if line or 079 // column numbers are out of sync. Also see InsertCommentFormatter and 080 // applyVisitorInsertComments. 081 root.accept(new TreeFormatter(2, 80)); 082 root.accept(new TreeDumper(output)); 083 } 084 085 // /////////////////////////////////////////////////////////////////////////// 086 // Printing and parsing 087 // 088 089 // Formats an AST as a String. 090 // This version does not reformat the tree (which blows away formatting 091 // information). The call to "removeWhitespace" may do the wrong thing 092 // for embedded strings, however. In any event, the result is not 093 // intended for direct human consumption. 094 public static String format(Node n) { 095 StringWriter w = new StringWriter(); 096 n.accept(new TreeDumper(w)); 097 // This is incorrect. A "//" comment ending in a period, for example, will 098 // cause the line after it to become part of the comment as well. 099 // If not intended for human consumption, why remove whitespace? 100 // return removeWhitespace(w.toString()); 101 return removeWhitespace(quickFixForInternalComment(w.toString())); 102 } 103 104 // Formats an AST as a String. 105 // This is a debugging version that outputs all tree nodes: 106 // terminals and non-terminals alike. 107 public static String formatEntireTree(Node n) { 108 StringWriter w = new StringWriter(); 109 n.accept(new TreeDumper(w, true)); 110 // delete empty lines, but otherwise leave text alone 111 return w.toString().replaceAll("(?m)^[ \t]*\r?\n", ""); 112 } 113 114 /** 115 * This method translates a line like. 116 * 117 * <pre>{@code 118 * a statement; // a comment 119 * }</pre> 120 * 121 * into 122 * 123 * <pre> 124 * a statement; // a comment// 125 * </pre> 126 * 127 * @param s a Java code line that might contain a comment 128 * @return the line with its comment, if any, tweaked 129 */ 130 public static String quickFixForInternalComment(String s) { 131 StringBuilder b = new StringBuilder(); 132 String[] split = StringsPlume.splitLines(s); 133 for (int i = 0; i < split.length; i++) { 134 String line = split[i]; 135 b.append(line); 136 if (line.indexOf("//") != -1) { 137 b.append("//"); 138 b.append(lineSep); 139 b.append("/* */"); 140 } 141 b.append(lineSep); 142 } 143 return b.toString(); 144 } 145 146 // Formats the line enclosing a node 147 public static String formatCurrentLine(Node n) { 148 Node current = n; 149 while (current.getParent() != null && format(current.getParent()).indexOf(lineSep) < 0) { 150 current = current.getParent(); 151 } 152 return format(current); 153 } 154 155 /** 156 * Creates an AST from a String. 157 * 158 * @param type the type of the result 159 * @param stringRep the string to parse 160 * @return an AST created from the string 161 */ 162 public static Node create(String type, String stringRep) { 163 return create(type, new Class<?>[] {}, new Object[] {}, stringRep); 164 } 165 166 // Creates an AST from a String 167 public static Node create(String type, Class<?>[] argTypes, Object[] args, String stringRep) { 168 JavaParser parser = new JavaParser(new StringReader(stringRep)); 169 Node n; 170 try { 171 Method m = JavaParser.class.getMethod(type, argTypes); 172 n = (Node) m.invoke(parser, args); 173 } catch (Exception e) { 174 System.err.println("create(" + type + ", \"" + stringRep + "\")"); 175 e.printStackTrace(); 176 throw new Daikon.UserError("Error in Ast.create"); 177 } 178 return n; 179 } 180 181 // /////////////////////////////////////////////////////////////////////////// 182 // Names (fully qualified and otherwise) 183 // 184 185 /** 186 * Returns true if the string is an access modifier: "public, "protected", or "private". 187 * 188 * @param s a string 189 * @return true if the string is an access modifier 190 */ 191 public static boolean isAccessModifier(String s) { 192 return s.equals("public") || s.equals("protected") || s.equals("private"); 193 } 194 195 /** 196 * Returns the name of the formal parameter. 197 * 198 * @param p a formal parameter 199 * @return the name of the formal parameter 200 */ 201 // f4 -> VariableDeclaratorId() 202 @SuppressWarnings("signature:return") // string manipulation 203 public static @Identifier String getName(FormalParameter p) { 204 String name = format(p.f4); 205 int startBrackets = name.indexOf('['); 206 if (startBrackets == -1) { 207 return name; 208 } else { 209 return name.substring(0, startBrackets); 210 } 211 } 212 213 // f2 -> Type() 214 // f4 -> VariableDeclaratorId() 215 public static String getType(FormalParameter fp) { 216 217 StringWriter w = new StringWriter(); 218 fp.accept(new TreeDumper(w)); 219 220 FormalParameter p = (FormalParameter) create("FormalParameter", w.toString()); 221 222 p.accept(new TreeFormatter()); 223 224 String type = format(p.f2); 225 String name = format(p.f4); 226 227 // format() removes whitespace around brackets, so this test is safe. 228 while (name.endsWith("[]")) { 229 type += "[]"; 230 name = name.substring(0, name.length() - 2); 231 } 232 233 return type; 234 } 235 236 /** 237 * Returns the name of the method. 238 * 239 * @param m a method 240 * @return the name of the method 241 */ 242 // f2 -> MethodDeclarator() 243 @SuppressWarnings("signature:return") // JTB is not annotated 244 public static @Identifier String getName(MethodDeclaration m) { 245 return m.f2.f0.tokenImage; 246 } 247 248 /** 249 * Returns the name of the constructor. 250 * 251 * @param m a constructor 252 * @return the name of the constructor 253 */ 254 // f1 -> <IDENTIFIER> 255 @SuppressWarnings("signature:return") // JTB is not annotated 256 public static @Identifier String getName(ConstructorDeclaration m) { 257 return m.f1.tokenImage; 258 } 259 260 // Returns the name of the package for this compilation unit, or null if 261 // no package was specified. 262 public static @Nullable String getPackage(CompilationUnit u) { 263 NodeOptional o = u.f0; 264 if (o.present()) { 265 PackageDeclaration p = (PackageDeclaration) o.node; 266 return format(p.f2); // f2 -> Name() 267 } else { 268 return null; 269 } 270 } 271 272 // Return the fully qualified name of the method (not including params). 273 // <package>.<class>*.<method> 274 // If the method is in an anonymous inner class, "$inner" is used to 275 // represent the name of the inner class. 276 public static String getFullName(MethodDeclaration method) { 277 String className = getClassName(method); 278 String methodName = getName(method); 279 return className + "." + methodName; 280 } 281 282 // Used to be called "getFullName", but that was misleading. 283 // Returns the fully qualified signature of a method. 284 // <package>.<class>*.<method>(<params>) 285 // If the method is in an anonymous inner class, "$inner" is used to 286 // represent the name of the inner class. 287 public static String getFullSignature(MethodDeclaration method) { 288 String className = getClassName(method); 289 String methodDeclarator = getMethodDeclarator(method); 290 return className + "." + methodDeclarator; 291 } 292 293 /** 294 * Returns the classname if the given type declaration declares a ClassOrInterfaceDeclaration. 295 * Otherwise returns null. 296 */ 297 public static @Nullable @BinaryName String getClassNameForType(TypeDeclaration d) { 298 299 // Grammar production for TypeDeclaration: 300 // f0 -> ";" 301 // | Modifiers() ( ClassOrInterfaceDeclaration(modifiers) | EnumDeclaration(modifiers) | 302 // AnnotationTypeDeclaration(modifiers) ) 303 304 NodeChoice c = d.f0; 305 if (c.which == 0) { 306 return null; 307 } else { 308 NodeSequence seq = (NodeSequence) c.choice; 309 NodeChoice c2 = (NodeChoice) seq.elementAt(1); 310 if (c2.choice instanceof ClassOrInterfaceDeclaration) { 311 return getClassName(c2.choice); 312 } else { 313 return null; 314 } 315 } 316 } 317 318 /** Returns the fully qualified name of the class containing the node. */ 319 public static @BinaryName String getClassName(Node d) { 320 321 ClassOrInterfaceDeclaration n = 322 (d instanceof ClassOrInterfaceDeclaration) 323 ? (ClassOrInterfaceDeclaration) d 324 : (ClassOrInterfaceDeclaration) Ast.getParent(ClassOrInterfaceDeclaration.class, d); 325 326 String packageName; 327 CompilationUnit unit = (CompilationUnit) getParent(CompilationUnit.class, n); 328 String getPackage = getPackage(unit); 329 if (getPackage != null) { 330 packageName = getPackage + "."; 331 } else { 332 packageName = ""; 333 } 334 335 String className = n.f1.tokenImage + "."; // f1 -> <IDENTIFIER> 336 337 Node currentNode = n; 338 while (true) { 339 ClassOrInterfaceBody b = 340 (ClassOrInterfaceBody) getParent(ClassOrInterfaceBody.class, currentNode); 341 if (b == null) { 342 break; 343 } 344 Node n1 = b.getParent(); 345 assert n1 instanceof ClassOrInterfaceDeclaration; 346 if (isInner((ClassOrInterfaceDeclaration) n1)) { 347 // TODO: This works for anonymous classes (maybe), but is wrong for 348 // non-anonymous inner classes. 349 className = "$inner." + className; 350 currentNode = b; 351 } else { 352 String s = ((ClassOrInterfaceDeclaration) n1).f1.tokenImage; 353 className = s + "." + className; 354 currentNode = n1; 355 } 356 } 357 358 String result = packageName + className; 359 if (result.endsWith(".")) { 360 result = result.substring(0, result.length() - 1); 361 } 362 363 @SuppressWarnings("signature") // string concatenation, etc. 364 @BinaryName String result_bnfna = result; 365 return result_bnfna; 366 } 367 368 // f2 -> MethodDeclarator() 369 public static void setName(MethodDeclaration m, String name) { 370 m.f2.f0.tokenImage = name; 371 } 372 373 // f1 -> [ AssignmentOperator() Expression() ] 374 // Return the primary expression on the left-hand side of an assignment 375 public static PrimaryExpression assignment2primaryexpression(Expression n) { 376 // All this could perhaps be replaced with an ad-hoc visitor, as is 377 // done in nodeTokenAfter(). But it is written now, so leave it as is. 378 379 assert n.f1.present(); 380 ConditionalExpression ce = n.f0; 381 assert !ce.f1.present(); 382 ConditionalOrExpression coe = ce.f0; 383 assert !coe.f1.present(); 384 ConditionalAndExpression cae = coe.f0; 385 assert !cae.f1.present(); 386 InclusiveOrExpression ioe = cae.f0; 387 assert !ioe.f1.present(); 388 ExclusiveOrExpression eoe = ioe.f0; 389 assert !eoe.f1.present(); 390 AndExpression ande = eoe.f0; 391 assert !ande.f1.present(); 392 EqualityExpression ee = ande.f0; 393 assert !ee.f1.present(); 394 InstanceOfExpression iofe = ee.f0; 395 assert !iofe.f1.present(); 396 RelationalExpression re = iofe.f0; 397 assert !re.f1.present(); 398 ShiftExpression se = re.f0; 399 assert !se.f1.present(); 400 AdditiveExpression adde = se.f0; 401 assert !adde.f1.present(); 402 MultiplicativeExpression me = adde.f0; 403 assert !me.f1.present(); 404 UnaryExpression ue = me.f0; 405 UnaryExpressionNotPlusMinus uenpm = (UnaryExpressionNotPlusMinus) ue.f0.choice; 406 PostfixExpression pfe = (PostfixExpression) uenpm.f0.choice; 407 assert !pfe.f1.present(); 408 PrimaryExpression pe = pfe.f0; 409 return pe; 410 } 411 412 public static String fieldName(PrimaryExpression pe) { 413 414 // System.out.println("fieldName(" + pe + ")"); 415 416 // PrimaryExpression: 417 // f0 -> PrimaryPrefix() 418 // f1 -> ( PrimarySuffix() )* 419 420 // First, try to get a name from the PrimarySuffix. 421 422 NodeListOptional pslist = pe.f1; 423 if (pslist.size() > 0) { 424 PrimarySuffix ps = (PrimarySuffix) pslist.elementAt(pslist.size() - 1); 425 NodeChoice psnc = ps.f0; 426 // PrimarySuffix: 427 // f0 -> "." "super" 428 // | "." "this" 429 // | "." AllocationExpression() 430 // | MemberSelector() 431 // | "[" Expression() "]" 432 // | "." <IDENTIFIER> 433 // | Arguments() 434 switch (psnc.which) { 435 case 5: 436 NodeSequence sn = (NodeSequence) psnc.choice; 437 assert sn.size() == 2; 438 return ((NodeToken) sn.elementAt(1)).tokenImage; 439 } 440 } 441 442 // If it was impossible to get a name from the PrimarySuffix, 443 // try the PrimaryPrefix. 444 445 // PrimaryPrefix: 446 // f0 -> Literal() 447 // | ( <IDENTIFIER> "." )* "this" 448 // | "super" "." <IDENTIFIER> 449 // | ClassOrInterfaceType() "." "super" "." <IDENTIFIER> 450 // | "(" Expression() ")" 451 // | AllocationExpression() 452 // | ResultType() "." "class" 453 // | Name() 454 PrimaryPrefix pp = pe.f0; 455 NodeChoice ppnc = pp.f0; 456 switch (ppnc.which) { 457 case 2: 458 NodeSequence sn = (NodeSequence) ppnc.choice; 459 assert sn.size() == 3; 460 return ((NodeToken) sn.elementAt(2)).tokenImage; 461 case 7: 462 return fieldName((Name) ppnc.choice); 463 } 464 465 return null; 466 } 467 468 public static String fieldName(Name n) { 469 NodeListOptional nlo = n.f1; 470 if (nlo.present()) { 471 NodeSequence ns = (NodeSequence) nlo.elementAt(nlo.size() - 1); 472 assert ns.size() == 2; 473 NodeToken nt = (NodeToken) ns.elementAt(1); 474 return nt.tokenImage; 475 } else { 476 return n.f0.tokenImage; 477 } 478 } 479 480 // /////////////////////////////////////////////////////////////////////////// 481 // Comments 482 // 483 484 // Add the comment to the first regular token in the tree, after all 485 // other special tokens (comments) associated with that token. 486 public static NodeToken addComment(Node n, String comment) { 487 return addComment(n, comment, false); 488 } 489 490 // Add the comment to the first regular token in the tree. 491 // If first is true, then it is inserted before all other special tokens; 492 // otherwise, it is inserted after them. 493 public static NodeToken addComment(Node n, String comment, boolean first) { 494 NodeToken nt = new NodeToken(comment); 495 addComment(n, nt, first); 496 return nt; 497 } 498 499 // Add the comment to the first regular token in the tree, after all 500 // other special tokens (comments) associated with that token. 501 public static void addComment(Node n, NodeToken comment) { 502 addComment(n, comment, false); 503 } 504 505 /** 506 * Add the comment to the first regular token in the tree, as a "special token" (comment). 507 * 508 * <p>If first is true, then it is inserted before all other special tokens; otherwise, it is 509 * inserted after them. 510 * 511 * <p>Postcondition (make sure you preserve it if you modify this method): comment.beginLine and 512 * comment.beginColumn have been assigned the line and column number where this comment will go. 513 */ 514 public static void addComment(Node n, NodeToken comment, boolean first) { 515 class AddCommentVisitor extends DepthFirstVisitor { 516 private boolean seenToken = false; 517 private final NodeToken comment; 518 private final boolean first; 519 520 AddCommentVisitor(NodeToken comment, boolean first) { 521 this.comment = comment; 522 this.first = first; 523 } 524 525 @Override 526 public void visit(NodeToken node) { 527 if (!seenToken) { 528 seenToken = true; 529 if (first && (node.numSpecials() > 0)) { 530 comment.beginLine = node.getSpecialAt(0).beginLine; 531 comment.beginColumn = node.getSpecialAt(0).beginColumn; 532 // System.out.println("Set from special <<<" + node.getSpecialAt(0).trim() + ">>>"); 533 } else { 534 comment.beginLine = node.beginLine; 535 comment.beginColumn = node.beginColumn; 536 } 537 if (first) { 538 addFirstSpecial(node, comment); 539 } else { 540 node.addSpecial(comment); 541 } 542 // System.out.println("comment (" + comment.beginLine + "," + comment.beginColumn + ") = " 543 // + comment.tokenImage + "; node (" + node.beginLine + "," + node.beginColumn + ")= " + 544 // node.tokenImage); 545 } 546 } 547 } 548 549 // String briefComment = comment.toString(); 550 // if (briefComment.length() > 70) 551 // briefComment = briefComment.substring(0,70) + "..."; 552 // System.out.printf("addComment at %d:%d, first=%b%n <<<%s>>>%n", 553 // comment.beginLine, comment.beginColumn, first, briefComment); 554 555 n.accept(new AddCommentVisitor(comment, first)); 556 } 557 558 // This seems to set the beginLine and beginColumn for the comment, but 559 // how is the comment inserted into the tree? The lines that ought to do 560 // that are commented out (without explanation). The explanation is that 561 // despite the comment, this does NOT do insertion. It just determines 562 // where the insertion ought to occur. The change forces the client to 563 // do the insertion. This should be documented/fixed. -MDE 7/13/2003 564 565 /** 566 * Add the comment to the first regular token in the tree. If first is true, then it is inserted 567 * before all other special tokens; otherwise, it is inserted after them. 568 * 569 * <p>Exception: If first is true, may heuristically place the comment it in the middle of the 570 * list of special tokens (comments), in order to place it at the same column as the real node. 571 * 572 * <p>Postcondition (make sure you preserve it if you modify this method): comment.beginLine and 573 * comment.beginColumn have been assigned the line and column number where this comment will go. 574 */ 575 public static void findLineAndCol(Node n, NodeToken comment, boolean first) { 576 class AddCommentVisitor extends DepthFirstVisitor { 577 private boolean seenToken = false; 578 private final NodeToken comment; 579 private final boolean first; 580 581 AddCommentVisitor(NodeToken comment, boolean first) { 582 this.comment = comment; 583 this.first = first; 584 } 585 586 @Override 587 public void visit(NodeToken node) { 588 if (!seenToken) { 589 seenToken = true; 590 if (first 591 && (node.numSpecials() > 1) 592 && (node.getSpecialAt(0).beginColumn 593 != node.getSpecialAt(node.numSpecials() - 1).beginColumn)) { 594 // There are multiple comments, and the first and last ones 595 // start at different columns. Search from the end, finding 596 // the first one with a different column. 597 int i = node.numSpecials() - 1; 598 while (node.getSpecialAt(i - 1).beginColumn == node.getSpecialAt(i).beginColumn) { 599 i--; 600 } 601 comment.beginLine = node.getSpecialAt(i).beginLine; 602 comment.beginColumn = node.getSpecialAt(i).beginColumn; 603 // addNthSpecial(node, comment, i); 604 } else if (first && (node.numSpecials() > 0)) { 605 comment.beginLine = node.getSpecialAt(0).beginLine; 606 comment.beginColumn = node.getSpecialAt(0).beginColumn; 607 // System.out.println("findLineAndCol: set from special <<<" + node.getSpecialAt(0) + 608 // ">>>"); 609 // addFirstSpecial(node, comment); 610 } else { 611 comment.beginLine = node.beginLine; 612 comment.beginColumn = node.beginColumn; 613 // node.addSpecial(comment); 614 } 615 // System.out.printf("comment placed at (%d,%d) = <<<%s>>> for node (%d,%d)= <<<%s>>>%n", 616 // comment.beginLine, comment.beginColumn, comment.tokenImage.trim(), 617 // node.beginLine, node.beginColumn, node.tokenImage.trim()); 618 } 619 } 620 } 621 n.accept(new AddCommentVisitor(comment, first)); 622 } 623 624 /** Adds the comment to the first regular token in the tree, before the ith special token. */ 625 @SuppressWarnings("JdkObsolete") // JTB uses Vector 626 public static void addNthSpecial(NodeToken n, NodeToken s, int i) { 627 if (n.specialTokens == null) { 628 n.specialTokens = new Vector<NodeToken>(); 629 } 630 n.specialTokens.insertElementAt(s, i); 631 s.setParent(n); 632 } 633 634 /** Adds the comment to the first regular token in the tree, *before* all other special tokens. */ 635 public static void addFirstSpecial(NodeToken n, NodeToken s) { 636 addNthSpecial(n, s, 0); 637 } 638 639 // Return the first NodeToken after (all of) the specified Node. 640 public static NodeToken nodeTokenAfter(Node n) { 641 // After the traversal, the "lastNodeToken" slot contains the 642 // last NodeToken visited. 643 class LastNodeTokenVisitor extends DepthFirstVisitor { 644 NodeToken lastNodeToken = null; 645 646 @Override 647 public void visit(NodeToken node) { 648 lastNodeToken = node; 649 } 650 } 651 // After the traversal, the "nextNodeToken" slot contains the token 652 // visited immediately after "predecessor". ("predecessor" should be a 653 // descendant of the token from which traversal starts.) 654 class NextNodeTokenVisitor extends DepthFirstVisitor { 655 private boolean seenPredecessor = false; 656 NodeToken nextNodeToken; 657 private final NodeToken predecessor; 658 659 NextNodeTokenVisitor(NodeToken predecessor) { 660 this.predecessor = predecessor; 661 } 662 663 @Override 664 @SuppressWarnings("interning") 665 public void visit(NodeToken node) { 666 if (!seenPredecessor) { 667 if (node == predecessor) { 668 seenPredecessor = true; 669 } 670 } else if (nextNodeToken == null) { 671 nextNodeToken = node; 672 } 673 } 674 } 675 676 // set predecessor 677 LastNodeTokenVisitor lntv = new LastNodeTokenVisitor(); 678 n.accept(lntv); 679 NodeToken predecessor = lntv.lastNodeToken; 680 if (predecessor == null) { 681 throw new Error("No lastNodeToken for " + n); 682 } 683 684 // We don't know how high in the tree we need to go in order to find a 685 // successor, so iteratively go higher until success. This has bad 686 // worst-case performance, but should be acceptable in practice. 687 NodeToken result = null; 688 Node parent = n.getParent(); 689 while ((result == null) && (parent != null)) { 690 NextNodeTokenVisitor nntv = new NextNodeTokenVisitor(predecessor); 691 parent.accept(nntv); 692 result = nntv.nextNodeToken; 693 parent = parent.getParent(); 694 } 695 if (result == null) { 696 throw new Error("No nextNodeToken for " + n); 697 } 698 return result; 699 } 700 701 // Removes all the special tokens (annotations and other comments) 702 // from the first regular token in the method 703 public static void removeAnnotations(MethodDeclaration m) { 704 class RemoveAnnotationsVisitor extends DepthFirstVisitor { 705 private boolean seenToken = false; 706 707 @Override 708 public void visit(NodeToken n) { 709 if (!seenToken) { 710 seenToken = true; 711 n.specialTokens = null; 712 } 713 } 714 } 715 716 m.accept(new RemoveAnnotationsVisitor()); 717 } 718 719 // /////////////////////////////////////////////////////////////////////////// 720 // Whitespace 721 // 722 723 /** 724 * Removes whitespace around punctuation: ., (, ), [, ]. 725 * 726 * @param arg a string 727 * @return the string with whitespace removed 728 */ 729 public static String removeWhitespace(String arg) { 730 arg = arg.trim(); 731 arg = StringsPlume.removeWhitespaceAround(arg, "."); 732 arg = StringsPlume.removeWhitespaceAround(arg, "("); 733 arg = StringsPlume.removeWhitespaceAround(arg, ")"); 734 arg = StringsPlume.removeWhitespaceAround(arg, "["); 735 arg = StringsPlume.removeWhitespaceBefore(arg, "]"); 736 return arg; 737 } 738 739 // /////////////////////////////////////////////////////////////////////////// 740 // PptMap manipulation 741 // 742 743 // /////////////////////////////////////////////////////////////////////////// 744 // Reflection 745 // 746 747 /** 748 * Returns the class corresponding to the given node. 749 * 750 * @param n a node 751 * @return the class corresponding to the given node 752 */ 753 public static Class<?> getClass(Node n) { 754 String ast_classname = getClassName(n); 755 if (ast_classname.indexOf("$inner") != -1) { 756 return null; 757 } 758 return getClass(ast_classname); 759 } 760 761 public static Class<?> getClass(@ClassGetName String s) { 762 try { 763 Class<?> c = Class.forName(s); 764 assert c != null; 765 return c; 766 } catch (ClassNotFoundException e) { 767 String orig_s = s; 768 // System.out.println("Lookup failed: " + s); 769 // We should have found it. Maybe there is a name mangling problem. 770 // Systematically change each "." to "$" in an attempt to fix it. 771 while (true) { 772 int dot_pos = s.lastIndexOf('.'); 773 if (dot_pos == -1) { 774 throw new Error("Didn't find class " + orig_s); 775 } 776 @SuppressWarnings("signature") // string concatenation 777 @ClassGetName String new_s = s.substring(0, dot_pos) + "$" + s.substring(dot_pos + 1); 778 s = new_s; 779 // System.out.println("Lookup trying: " + s); 780 try { 781 Class<?> c = Class.forName(s); 782 assert c != null; 783 return c; 784 } catch (ClassNotFoundException ex) { 785 throw new Error("This can't happen"); 786 } 787 } 788 // throw new Error("Control cannot reach here"); 789 } 790 } 791 792 public static Method getMethod(MethodDeclaration methoddecl) { 793 Class<?> c = getClass(methoddecl); 794 return getMethod(c, methoddecl); 795 } 796 797 public static Method getMethod(Class<?> c, MethodDeclaration methoddecl) { 798 String ast_methodname = getName(methoddecl); 799 List<FormalParameter> ast_params = getParameters(methoddecl); 800 801 List<Method> publicMethods = Arrays.<Method>asList(c.getMethods()); 802 List<Method> declaredMethods = Arrays.<Method>asList(c.getDeclaredMethods()); 803 List<Method> allMethods = new ArrayList<>(); 804 allMethods.addAll(publicMethods); 805 allMethods.addAll(declaredMethods); 806 807 Method[] meths = allMethods.toArray(new Method[0]); 808 809 for (int i = 0; i < meths.length; i++) { 810 811 Method meth = meths[i]; 812 // System.out.println("getMethod(" + c.getName() + ", " + getName(methoddecl) + ") checking " 813 // + meth.getName()); 814 if (!typeMatch(meth.getName(), ast_methodname)) { 815 continue; 816 } 817 818 Class<?>[] params = meth.getParameterTypes(); 819 if (paramsMatch(params, ast_params)) { 820 // System.out.println("getMatch succeeded: " + ppt.name()); 821 return meth; 822 } 823 } 824 return null; 825 } 826 827 public static Constructor<?> getConstructor(ConstructorDeclaration constructordecl) { 828 Class<?> c = getClass(constructordecl); 829 return getConstructor(c, constructordecl); 830 } 831 832 public static Constructor<?> getConstructor(Class<?> c, ConstructorDeclaration constructordecl) { 833 String ast_constructorname = getName(constructordecl); 834 835 List<FormalParameter> ast_params = getParameters(constructordecl); 836 837 List<Constructor<?>> publicConstructors = Arrays.<Constructor<?>>asList(c.getConstructors()); 838 List<Constructor<?>> declaredConstructors = 839 Arrays.<Constructor<?>>asList(c.getDeclaredConstructors()); 840 List<Constructor<?>> allConstructors = new ArrayList<>(); 841 allConstructors.addAll(publicConstructors); 842 allConstructors.addAll(declaredConstructors); 843 844 Constructor<?>[] constrs = allConstructors.toArray(new Constructor<?>[0]); 845 846 for (int i = 0; i < constrs.length; i++) { 847 848 Constructor<?> constr = constrs[i]; 849 if (!typeMatch(constr.getName(), ast_constructorname)) { 850 continue; 851 } 852 Class<?>[] params = constr.getParameterTypes(); 853 if (paramsMatch(params, ast_params)) { 854 // System.out.println("getMatch succeeded: " + ppt.name()); 855 return constr; 856 } 857 } 858 return null; 859 } 860 861 public static boolean paramsMatch(Class<?>[] params, List<FormalParameter> ast_params) { 862 863 if (params.length != ast_params.size()) { 864 return false; 865 } 866 // Now check whether args match. 867 int j = 0; 868 for (Iterator<FormalParameter> itor = ast_params.iterator(); itor.hasNext(); j++) { 869 String ast_param = getType(itor.next()); 870 Class<?> param = params[j]; 871 // System.out.println("Comparing " + param + " to " + ast_param + ":"); 872 if (!typeMatch(classnameForSourceOutput(param), ast_param)) { 873 return false; 874 } 875 } 876 return true; 877 } 878 879 // return true if m is defined in any superclass of its class 880 public static boolean isOverride(MethodDeclaration methdecl) { 881 ClassOrInterfaceDeclaration classOrInterface = 882 (ClassOrInterfaceDeclaration) getParent(ClassOrInterfaceDeclaration.class, methdecl); 883 if (classOrInterface != null && isInterface(classOrInterface)) { 884 return false; 885 } 886 Class<?> c = getClass(methdecl); 887 if (c == null) { 888 return false; 889 } 890 // System.out.println("isOverride(" + getName(methdecl) + "): class=" + c.getName() + "; super=" 891 // + c.getSuperclass()); 892 return isOverride(c.getSuperclass(), methdecl); 893 } 894 895 // return true if methdecl is defined in c or any of its superclasses 896 public static boolean isOverride(Class<?> c, MethodDeclaration methdecl) { 897 // System.out.println("isOverride(" + c.getName() + ", " + getName(methdecl) + ")"); 898 Method meth = getMethod(c, methdecl); 899 if (meth != null) { 900 // System.out.println("isOverride => true"); 901 return true; 902 } 903 Class<?> superclass = c.getSuperclass(); 904 if (superclass == null) { 905 return false; 906 } 907 return isOverride(superclass, methdecl); 908 } 909 910 // return true if methdecl is defined in any interface of its class 911 public static boolean isImplementation(MethodDeclaration methdecl) { 912 913 ClassOrInterfaceDeclaration classOrInterface = 914 (ClassOrInterfaceDeclaration) getParent(ClassOrInterfaceDeclaration.class, methdecl); 915 if (classOrInterface != null && isInterface(classOrInterface)) { 916 return false; 917 } 918 Class<?> c = getClass(methdecl); 919 if (c == null) { 920 return false; 921 } 922 // System.out.println("isImplementation(" + getName(methdecl) + "): class=" + c.getName()); 923 Class<?>[] interfaces = c.getInterfaces(); 924 for (int i = 0; i < interfaces.length; i++) { 925 if (isImplementation(interfaces[i], methdecl)) { 926 return true; 927 } 928 } 929 return false; 930 } 931 932 // return true if methdecl is defined in c or any of its interfaces 933 public static boolean isImplementation(Class<?> c, MethodDeclaration methdecl) { 934 // System.out.println("isImplementation(" + c.getName() + ", " + getName(methdecl) + ")"); 935 Method meth = getMethod(c, methdecl); 936 if (meth != null) { 937 // System.out.println("isImplementation => true"); 938 return true; 939 } 940 Class<?>[] interfaces = c.getInterfaces(); 941 for (int i = 0; i < interfaces.length; i++) { 942 if (isImplementation(interfaces[i], methdecl)) { 943 return true; 944 } 945 } 946 return false; 947 } 948 949 // /////////////////////////////////////////////////////////////////////////// 950 // Etc. 951 // 952 953 // Following the chain of parent pointers from the child, returns 954 // the first node of the specified type or a subtype. Returns null 955 // if no parent of that type. 956 public static @Nullable Node getParent(Class<?> type, Node child) { 957 Node currentNode = child.getParent(); 958 while (true) { 959 if (type.isInstance(currentNode)) { 960 return currentNode; 961 } else { 962 if (currentNode == null) { 963 return null; 964 } 965 currentNode = currentNode.getParent(); 966 } 967 } 968 } 969 970 public static void addDeclaration(ClassOrInterfaceBody c, ClassOrInterfaceBodyDeclaration d) { 971 c.f1.addNode(d); 972 } 973 974 // The "access" argument should be one of "public", "protected", or "private". 975 @SuppressWarnings("JdkObsolete") // JTB uses Enumeration 976 public static void setAccess(MethodDeclaration m, String access) { 977 // The following four confusing lines are a following of the 978 // syntax tree to get to the modifiers. 979 ClassOrInterfaceBodyDeclaration decl = 980 (ClassOrInterfaceBodyDeclaration) Ast.getParent(ClassOrInterfaceBodyDeclaration.class, m); 981 NodeChoice nc = decl.f0; 982 NodeSequence sequence = (NodeSequence) nc.choice; 983 Modifiers modifiers = (Modifiers) sequence.elementAt(0); 984 NodeListOptional options = modifiers.f0; 985 for (Enumeration e = options.elements(); e.hasMoreElements(); ) { // non-generic due to JTB 986 NodeChoice c = (NodeChoice) e.nextElement(); 987 988 if (c.choice instanceof NodeToken) { 989 NodeToken t = (NodeToken) c.choice; 990 String token = t.tokenImage; 991 if (token.equals("public") || token.equals("protected") || token.equals("private")) { 992 t.tokenImage = access; 993 return; 994 } 995 } 996 } 997 // The method did not have any modifier 998 NodeToken t = new NodeToken(access); 999 NodeChoice c = new NodeChoice(t); 1000 options.addNode(c); 1001 } 1002 1003 @SuppressWarnings("JdkObsolete") // JTB uses Enumeration 1004 public static void removeMethodDeclAnnotations(MethodDeclaration method) { 1005 // The following four confusing lines are a following of the 1006 // syntax tree to get to the modifiers. 1007 ClassOrInterfaceBodyDeclaration decl = 1008 (ClassOrInterfaceBodyDeclaration) 1009 Ast.getParent(ClassOrInterfaceBodyDeclaration.class, method); 1010 NodeChoice nc = decl.f0; 1011 NodeSequence sequence = (NodeSequence) nc.choice; 1012 Modifiers modifiers = (Modifiers) sequence.elementAt(0); 1013 NodeListOptional options = modifiers.f0; 1014 1015 NodeListOptional filteredOptions = new NodeListOptional(); 1016 1017 for (Enumeration e = options.elements(); e.hasMoreElements(); ) { // non-generic due to JTB 1018 NodeChoice c = (NodeChoice) e.nextElement(); 1019 1020 if (!(c.choice instanceof jtb.syntaxtree.Annotation)) { 1021 filteredOptions.addNode(c); 1022 } 1023 } 1024 1025 modifiers.f0 = filteredOptions; 1026 } 1027 1028 // returns true if, for some node in the tree, node.tokenImage.equals(s) 1029 public static boolean contains(Node n, String s) { 1030 class ContainsVisitor extends DepthFirstVisitor { 1031 boolean found = false; 1032 private final String s; 1033 1034 ContainsVisitor(String s) { 1035 this.s = s; 1036 } 1037 1038 @Override 1039 public void visit(NodeToken node) { 1040 found = found || s.equals(node.tokenImage); 1041 } 1042 } 1043 ContainsVisitor cv = new ContainsVisitor(s); 1044 n.accept(cv); 1045 return cv.found; 1046 } 1047 1048 // Body must begin and end with a brace. 1049 public static void setBody(MethodDeclaration m, String body) { 1050 m.f4.choice = (Block) Ast.create("Block", body); 1051 } 1052 1053 // Returns the body of a method, including the leading "{" and trailing "}" 1054 public static String getBody(MethodDeclaration m) { 1055 return format(m.f4.choice); 1056 } 1057 1058 public static String getReturnType(MethodDeclaration m) { 1059 Node n = m.f1.f0.choice; 1060 return format(n); 1061 } 1062 1063 public static String getMethodDeclarator(MethodDeclaration m) { 1064 MethodDeclarator d = m.f2; 1065 return format(d); 1066 } 1067 1068 // Returns the parameters of the method, as a list of 1069 // FormalParameter objects. Returns an empty list if there are no 1070 // parameters. 1071 public static List<FormalParameter> getParameters(MethodDeclaration m) { 1072 class GetParametersVisitor extends DepthFirstVisitor { 1073 List<FormalParameter> parameters = new ArrayList<>(); 1074 1075 @Override 1076 public void visit(FormalParameter p) { 1077 parameters.add(p); 1078 } 1079 } 1080 GetParametersVisitor v = new GetParametersVisitor(); 1081 MethodDeclarator d = m.f2; 1082 d.accept(v); 1083 return v.parameters; 1084 } 1085 1086 // Returns the parameters of the constructor, as a list of 1087 // FormalParameter objects. Does not include implicit parameters for 1088 // inner classes. 1089 public static List<FormalParameter> getParametersNoImplicit(ConstructorDeclaration cd) { 1090 class GetParametersVisitor extends DepthFirstVisitor { 1091 List<FormalParameter> parameters = new ArrayList<>(); 1092 1093 @Override 1094 public void visit(FormalParameter p) { 1095 parameters.add(p); 1096 } 1097 } 1098 GetParametersVisitor v = new GetParametersVisitor(); 1099 FormalParameters fp = cd.f2; 1100 fp.accept(v); 1101 return v.parameters; 1102 } 1103 1104 // Returns the parameters of the constructor, as a list of 1105 // FormalParameter objects. Returns an empty list if there are no 1106 // parameters. 1107 public static List<FormalParameter> getParameters(ConstructorDeclaration cd) { 1108 class GetParametersVisitor extends DepthFirstVisitor { 1109 List<FormalParameter> parameters = new ArrayList<>(); 1110 1111 @Override 1112 public void visit(FormalParameter p) { 1113 parameters.add(p); 1114 } 1115 } 1116 GetParametersVisitor v = new GetParametersVisitor(); 1117 FormalParameters fp = cd.f2; 1118 fp.accept(v); 1119 1120 // Inner class constructors have implicit outer class parameter, which had 1121 // caused the constructor signatures not to match (and thus invariants would 1122 // not merge into inner class constructors) 1123 1124 // Look into replacing getClass because that requires that the compiled class 1125 // be in the classpath 1126 1127 Node innerClassNode = getParent(ClassOrInterfaceDeclaration.class, cd); 1128 Node outerClassNode = getParent(ClassOrInterfaceDeclaration.class, innerClassNode); 1129 1130 // boolean isNestedStatic = false; 1131 // Node nestedMaybe = innerClassNode.getParent(); 1132 // if (nestedMaybe instanceof NestedClassDeclaration) { 1133 // if (isStatic((NestedClassDeclaration)nestedMaybe)) { 1134 // isNestedStatic = true; 1135 // } 1136 // } 1137 1138 if (isInner((ClassOrInterfaceDeclaration) innerClassNode) 1139 && !isStatic((ClassOrInterfaceDeclaration) innerClassNode)) { 1140 NodeToken classNameToken = ((ClassOrInterfaceDeclaration) outerClassNode).f1; 1141 ClassOrInterfaceType name = 1142 new ClassOrInterfaceType(classNameToken, new NodeOptional(), new NodeListOptional()); 1143 NodeSequence n10 = new NodeSequence(name); 1144 NodeSequence n9 = new NodeSequence(n10); 1145 n9.addNode(new NodeListOptional()); 1146 ReferenceType refType = new ReferenceType(new NodeChoice(n9, 1)); 1147 jtb.syntaxtree.Type type = new jtb.syntaxtree.Type(new NodeChoice(refType, 1)); 1148 // Setting all the line and column info on the NodeTokens is a hassle. 1149 // So we sidestep and 'cheat' by putting a blank as the first character of the name. 1150 // This works because we are going to re-parse it in the node Create method. 1151 VariableDeclaratorId dummyOuterParamName = 1152 new VariableDeclaratorId(new NodeToken(" outer$this"), new NodeListOptional()); 1153 FormalParameter implicitOuter = 1154 new FormalParameter( 1155 new Modifiers(new NodeListOptional()), 1156 new NodeOptional(), 1157 type, 1158 new NodeOptional(), 1159 dummyOuterParamName); 1160 v.parameters.add(0, implicitOuter); 1161 } 1162 1163 return v.parameters; 1164 } 1165 1166 public static void addImport(CompilationUnit u, ImportDeclaration i) { 1167 u.f1.addNode(i); 1168 } 1169 1170 // Returns a list of Strings, the names of all the variables in the node. 1171 // The node is an expression, conditional expression, or primary 1172 // expression. 1173 @SuppressWarnings("JdkObsolete") // JTB uses Enumeration 1174 public static Set<String> getVariableNames(Node expr) { 1175 1176 class GetSymbolNamesVisitor extends DepthFirstVisitor { 1177 Set<String> symbolNames = new HashSet<>(); 1178 1179 @Override 1180 public void visit(Name n) { 1181 Node gp = n.getParent().getParent(); 1182 if (gp instanceof PrimaryPrefix) { 1183 PrimaryExpression ggp = (PrimaryExpression) gp.getParent(); 1184 for (Enumeration e = getPrimarySuffixes(ggp); e.hasMoreElements(); ) { 1185 PrimarySuffix s = (PrimarySuffix) e.nextElement(); 1186 if (s.f0.choice instanceof Arguments) { 1187 return; 1188 } 1189 } 1190 symbolNames.add(format(n)); 1191 } 1192 } 1193 } 1194 1195 GetSymbolNamesVisitor v = new GetSymbolNamesVisitor(); 1196 expr.accept(v); 1197 return v.symbolNames; 1198 } 1199 1200 /** 1201 * Returns an Enumeration of PrimarySuffix objects (but the static type of the elements is only 1202 * known to be Node). 1203 */ 1204 public static Enumeration getPrimarySuffixes(PrimaryExpression p) { 1205 return p.f1.elements(); 1206 } 1207 1208 // Return true if the strings are equal, or if abbreviated is a suffix 1209 // of goal. This wouldn't be necessary if we did full type resolution. 1210 static boolean typeMatch(String pptTypeString, String astTypeString) { 1211 // System.out.println("Comparing " + pptTypeString + " to " + astTypeString); 1212 if (astTypeString.equals(pptTypeString)) { 1213 return true; 1214 } 1215 // If astTypeString is missing the leading package name, permit a match 1216 if (pptTypeString.endsWith(astTypeString) 1217 && (pptTypeString.charAt(pptTypeString.length() - astTypeString.length() - 1) == '.')) { 1218 return true; 1219 } 1220 return false; 1221 } 1222 1223 /** Returns true if this is the main method for this class. */ 1224 public static boolean isMain(MethodDeclaration md) { 1225 if (Ast.getName(md).equals("main")) { 1226 List<FormalParameter> params = Ast.getParameters(md); 1227 if (params.size() == 1) { 1228 FormalParameter fp = params.get(0); 1229 String paramtype = Ast.getType(fp); 1230 if (Ast.typeMatch("java.lang.String[]", paramtype)) { 1231 return true; 1232 } 1233 } 1234 } 1235 return false; 1236 } 1237 1238 /** 1239 * This code is taken from and modified from daikon.PrintInvariants.print_invariants. The main 1240 * modification is that instead of printing the invariants, we return a list of them. 1241 * modifications involve removing code that I don't need here, like printing of debugging info. 1242 * 1243 * <p>[[ TODO: instead of duplicating code, you should add this method to PrintInvariants (or 1244 * wherever it belongs) and have PrintInvariants.print_invariants call it. ]] 1245 */ 1246 public static List<Invariant> getInvariants(PptTopLevel ppt, PptMap ppt_map) { 1247 1248 // make names easier to read before printing 1249 ppt.simplify_variable_names(); 1250 1251 // I could instead sort the PptSlice objects, then sort the invariants 1252 // in each PptSlice. That would be more efficient, but this is 1253 // probably not a bottleneck anyway. 1254 List<Invariant> invs_vector = new ArrayList<>(ppt.getInvariants()); 1255 1256 Invariant[] invs_array = invs_vector.toArray(new Invariant[0]); 1257 Arrays.sort(invs_array, PptTopLevel.icfp); 1258 1259 Global.non_falsified_invariants += invs_array.length; 1260 1261 List<Invariant> accepted_invariants = new ArrayList<>(); 1262 1263 for (int i = 0; i < invs_array.length; i++) { 1264 Invariant inv = invs_array[i]; 1265 1266 assert !(inv instanceof Equality); 1267 for (int j = 0; j < inv.ppt.var_infos.length; j++) { 1268 assert !inv.ppt.var_infos[j].missingOutOfBounds() 1269 : "var '" + inv.ppt.var_infos[j].name() + "' out of bounds in " + inv.format(); 1270 } 1271 InvariantFilters fi = InvariantFilters.defaultFilters(); 1272 1273 boolean fi_accepted = true; 1274 InvariantFilter filter_result = fi.shouldKeep(inv); 1275 fi_accepted = (filter_result == null); 1276 1277 // Never print the guarding predicates themselves, they should only 1278 // print as part of GuardingImplications 1279 if (fi_accepted && !inv.isGuardingPredicate) { 1280 Global.reported_invariants++; 1281 accepted_invariants.add(inv); 1282 } 1283 } 1284 1285 accepted_invariants = InvariantFilters.addEqualityInvariants(accepted_invariants); 1286 1287 return accepted_invariants; 1288 } 1289 1290 public static boolean isStatic(ClassOrInterfaceDeclaration n) { 1291 return isStaticInternal((Node) n); 1292 } 1293 1294 public static boolean isStatic(MethodDeclaration n) { 1295 return isStaticInternal((Node) n); 1296 } 1297 1298 public static Modifiers getModifiers(ClassOrInterfaceDeclaration n) { 1299 return getModifiersInternal((Node) n); 1300 } 1301 1302 public static Modifiers getModifiers(MethodDeclaration n) { 1303 return getModifiersInternal((Node) n); 1304 } 1305 1306 private static Modifiers getModifiersInternal(Node n) { 1307 1308 assert (n instanceof MethodDeclaration) || (n instanceof ClassOrInterfaceDeclaration); 1309 1310 ClassOrInterfaceBodyDeclaration decl = 1311 (ClassOrInterfaceBodyDeclaration) getParent(ClassOrInterfaceBodyDeclaration.class, n); 1312 assert decl != null; 1313 NodeSequence seq = (NodeSequence) decl.f0.choice; 1314 return (Modifiers) seq.elementAt(0); 1315 } 1316 1317 private static boolean isStaticInternal(Node n) { 1318 // Grammar production for ClassOrInterfaceBodyDeclaration 1319 // f0 -> Initializer() 1320 // | Modifiers() ( ClassOrInterfaceDeclaration(modifiers) | EnumDeclaration(modifiers) | 1321 // ConstructorDeclaration() | FieldDeclaration(modifiers) | MethodDeclaration(modifiers) ) 1322 // | ";" 1323 assert (n instanceof MethodDeclaration) || (n instanceof ClassOrInterfaceDeclaration); 1324 1325 Modifiers modifiers = getModifiersInternal(n); 1326 if (modifierPresent(modifiers, "static")) { 1327 return true; 1328 } else { 1329 return false; 1330 } 1331 } 1332 1333 public static boolean modifierPresent(Modifiers modifiers, String modifierString) { 1334 // Grammar production: 1335 // f0 -> ( ( "public" | "static" | "protected" | "private" | "final" | "abstract" | 1336 // "synchronized" | "native" | "transient" | "volatile" | "strictfp" | Annotation() ) )* 1337 1338 NodeListOptional list = modifiers.f0; 1339 for (int i = 0; i < list.size(); i++) { 1340 NodeChoice nodeChoice = (NodeChoice) list.elementAt(i); 1341 1342 if (nodeChoice.choice instanceof NodeToken) { 1343 NodeToken keyword = (NodeToken) nodeChoice.choice; 1344 if (keyword.toString().equals(modifierString)) { 1345 return true; 1346 } 1347 } 1348 } 1349 return false; 1350 } 1351 1352 public static boolean isInner(ClassOrInterfaceDeclaration n) { 1353 if (getParent(ClassOrInterfaceDeclaration.class, n) != null) { 1354 return true; 1355 } else { 1356 return false; 1357 } 1358 } 1359 1360 public static boolean isInAnonymousClass(Node node) { 1361 ClassOrInterfaceBody clsbody = 1362 (ClassOrInterfaceBody) Ast.getParent(ClassOrInterfaceBody.class, node); 1363 return !(clsbody.getParent() instanceof ClassOrInterfaceDeclaration); 1364 } 1365 1366 public static boolean isInterface(ClassOrInterfaceBody n) { 1367 // n.getParent won't be an ClassOrInterfaceDeclaration for anonymous 1368 // class bodies such as in new List() { ... } -- anonymous classes can 1369 // never be interfaces, however, so that's simple enough to handle. :) 1370 if (!(n.getParent() instanceof ClassOrInterfaceDeclaration)) { 1371 return false; 1372 } 1373 1374 return isInterface((ClassOrInterfaceDeclaration) n.getParent()); 1375 } 1376 1377 public static boolean isInterface(ClassOrInterfaceDeclaration n) { 1378 // Grammar production for ClassOrInterfaceDeclaration: 1379 // f0 -> ( "class" | "interface" ) 1380 // f1 -> <IDENTIFIER> 1381 // f2 -> [ TypeParameters() ] 1382 // f3 -> [ ExtendsList(isInterface) ] 1383 // f4 -> [ ImplementsList(isInterface) ] 1384 // f5 -> ClassOrInterfaceBody(isInterface) 1385 NodeToken t = (NodeToken) n.f0.choice; 1386 String token = t.tokenImage; 1387 if (token.equals("interface")) { 1388 return true; 1389 } else { 1390 return false; 1391 } 1392 } 1393 1394 public static boolean isPrimitive(jtb.syntaxtree.Type n) { 1395 // Grammar production: 1396 // f0 -> ReferenceType() 1397 // | PrimitiveType() 1398 return (n.f0.choice instanceof PrimitiveType); 1399 } 1400 1401 public static boolean isReference(jtb.syntaxtree.Type n) { 1402 // Grammar production: 1403 // f0 -> ReferenceType() 1404 // | PrimitiveType() 1405 return (n.f0.choice instanceof ReferenceType); 1406 } 1407 1408 public static boolean isArray(jtb.syntaxtree.Type n) { 1409 // Grammar production: 1410 // f0 -> PrimitiveType() ( "[" "]" )+ 1411 // | ( ClassOrInterfaceType() ) ( "[" "]" )* 1412 if (isPrimitive(n)) { 1413 return false; 1414 } 1415 assert isReference(n); 1416 ReferenceType refType = (ReferenceType) n.f0.choice; 1417 NodeSequence seq = (NodeSequence) refType.f0.choice; 1418 if (seq.elementAt(0) instanceof PrimitiveType) { 1419 return true; // see grammar--must be array in this case 1420 } 1421 NodeListOptional opt = (NodeListOptional) seq.elementAt(1); 1422 return opt.present(); 1423 } 1424 1425 public static String classnameForSourceOutput(Class<?> c) { 1426 1427 assert !c.equals(Void.TYPE); 1428 1429 if (c.isPrimitive()) { 1430 return c.getName(); 1431 } else if (c.isArray()) { 1432 return Signatures.classGetNameToBinaryName(c.getName()); 1433 } else { 1434 return c.getName(); 1435 } 1436 } 1437}