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