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