001package daikon.tools.jtb; 002 003import java.util.List; 004import jtb.syntaxtree.*; 005import jtb.visitor.*; 006import org.plumelib.util.StringsPlume; 007 008/** 009 * InsertCommentFormatter is a visitor that does not actually insert comments, but instead corrects 010 * positioning fields of all the tokens in the tree to accomodate already-inserted comments, while 011 * modifying the formatting as little as possible. (It edits the {begin,end}{Line,Column} fields.) 012 * 013 * <p>Each inserted comment either affects only the rest of its line -- by shifting all subsequent 014 * characters rightward -- or only subsequent lines -- by shifting lines downward. 015 * 016 * <p>The caller must supply the collection of inserted comments for recognition by this visitor. 017 */ 018public class InsertCommentFormatter extends DepthFirstVisitor { 019 private boolean debugInsert = false; 020 021 private List<NodeToken> comments; 022 private int columnshift = 0; 023 private int lineshift = 0; 024 private int columnshiftline = -1; // the line currently being column-shifted. 025 026 // Column shifting only applies to a single line, then is turned off again. 027 // States for the variables: 028 // columnshift == 0, columnshiftline == -1: 029 // no column shifting being done 030 // columnshift != 0, columnshiftline == -1: 031 // column shifting being done, but first real token not yet found 032 // columnshift != 0, columnshiftline != -1: 033 // column shifting being done, applies only to specified line 034 035 private static final String lineSep = System.lineSeparator(); 036 037 public InsertCommentFormatter(List<NodeToken> comments) { 038 this.comments = comments; 039 } 040 041 /** 042 * Returns the number of lines that this token spans in the source code. 043 * 044 * @param n a NodeToken 045 * @return the number of lines that this token spans in the source code 046 */ 047 private static int numLines(NodeToken n) { 048 String image = n.tokenImage; 049 return StringsPlume.count(image, lineSep); 050 } 051 052 private static int numColumns(NodeToken n) { 053 if (numLines(n) > 0) { 054 return 0; 055 } else { 056 return n.tokenImage.length(); 057 } 058 } 059 060 @Override 061 public void visit(NodeToken n) { 062 if (debugInsert) { 063 System.out.println( 064 "Visit (at " 065 + n.beginLine 066 + "," 067 + n.beginColumn 068 + ") (in comments = " 069 + comments.contains(n) 070 + ") " 071 + n.tokenImage); 072 } 073 074 // See comment at use of this variable below 075 boolean prev_is_double_slash_comment = false; 076 077 // Handle special tokens first 078 if (n.numSpecials() > 0) { // handles case when n.specialTokens is null 079 for (NodeToken s : n.specialTokens) { 080 visit(s); 081 prev_is_double_slash_comment = s.tokenImage.startsWith("//"); 082 } 083 } 084 085 if ((columnshift == 0) && (lineshift == 0)) { 086 // nothing to do 087 } else { 088 if (columnshift != 0) { 089 if (columnshiftline == -1) { 090 columnshiftline = n.beginLine; 091 } 092 if (columnshiftline != n.beginLine) { 093 columnshift = 0; 094 columnshiftline = -1; 095 } 096 } 097 n.beginLine += lineshift; 098 n.endLine += lineshift; 099 n.beginColumn += columnshift; 100 n.endColumn += columnshift; 101 if (debugInsert) { 102 System.out.println( 103 "Shifted by " + lineshift + "," + columnshift + ": <<<" + n.tokenImage.trim() + ">>>"); 104 } 105 } 106 // Special-case the situation of ending a file with a "//"-style 107 // comment that does not start at the beginning of its line; in that 108 // case, we need to increment the "" token for EOF to start at the next 109 // line. Otherwise the "" EOF token is marked as starting at the end 110 // of the previous line, though the "//"-style comment doesn't end 111 // until the start of the next line. Without this code, 112 // jtb.visitor.TreeDumper.visit throws an error. 113 if (n.tokenImage.equals("") && prev_is_double_slash_comment) { 114 assert n.beginLine == n.endLine && n.beginColumn == n.endColumn; 115 n.beginLine += 1; 116 n.beginColumn = 1; 117 n.endLine += 1; 118 n.endColumn = 1; 119 } 120 if (comments.contains(n)) { 121 columnshift += numColumns(n); 122 lineshift += numLines(n); 123 } 124 if (debugInsert) { 125 System.out.println( 126 "End visit (at " + n.beginLine + "," + n.beginColumn + ") " + n.tokenImage); 127 } 128 } 129}