/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Part of the Processing project - http://processing.org Original Copyright (c) 1997, 1998 Van Di-Han HO. All Rights Reserved. Updates Copyright (c) 2001 Jason Pell. Further updates Copyright (c) 2003 Martin Gomez, Ateneo de Manila University Bug fixes Copyright (c) 2005-09 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package processing.app.tools; import processing.app.*; import processing.core.PApplet; import java.io.*; /** * Handler for dealing with auto format. * Contributed by Martin Gomez, additional bug fixes by Ben Fry. * * After some further digging, this code in fact appears to be a modified * version of Jason Pell's GPLed "Java Beautifier" class found here: * http://www.geocities.com/jasonpell/programs.html * Which is itself based on code from Van Di-Han Ho: * http://www.geocities.com/~starkville/vancbj_idx.html * [Ben Fry, August 2009] */ public class AutoFormat implements Tool { Editor editor; static final int BLOCK_MAXLEN = 1024; StringBuffer strOut; int indentValue; String indentChar; int EOF; CharArrayReader reader; int readCount, indexBlock, lineLength, lineNumber; char chars[]; String strBlock; int s_level[]; int c_level; int sp_flg[][]; int s_ind[][]; int s_if_lev[]; int s_if_flg[]; int if_lev, if_flg, level; int ind[]; int e_flg, paren; static int p_flg[]; char l_char, p_char; int a_flg, q_flg, ct; int s_tabs[][]; String w_if_, w_else, w_for, w_ds, w_case, w_cpp_comment, w_jdoc; int jdoc, j; char string[]; char cc; int s_flg; int peek; char peekc; int tabs; char last_char; char c; String line_feed; public void init(Editor editor) { this.editor = editor; } public String getMenuTitle() { return "Auto Format"; } public void comment() throws IOException { int save_s_flg; save_s_flg = s_flg; int done = 0; c = string[j++] = getchr(); // extra char while (done == 0) { c = string[j++] = getchr(); while ((c != '/') && (j < string.length)) { if(c == '\n' || c == '\r') { lineNumber++; putcoms(); s_flg = 1; } c = string[j++] = getchr(); } //String tmpstr = new String(string); if (j>1 && string[j-2] == '*') { done = 1; jdoc = 0; } } putcoms(); s_flg = save_s_flg; jdoc = 0; return; } public char get_string() throws IOException { char ch; ch = '*'; while (true) { switch (ch) { default: ch = string[j++] = getchr(); if (ch == '\\') { string[j++] = getchr(); break; } if (ch == '\'' || ch == '"') { cc = string[j++] = getchr(); while (cc != ch) { if (cc == '\\') string[j++] = getchr(); cc = string[j++] = getchr(); } break; } if (ch == '\n' || ch == '\r') { indent_puts(); a_flg = 1; break; } else { return(ch); } } } } public void indent_puts() { string[j] = '\0'; if (j > 0) { if (s_flg != 0) { if((tabs > 0) && (string[0] != '{') && (a_flg == 1)) { tabs++; } p_tabs(); s_flg = 0; if ((tabs > 0) && (string[0] != '{') && (a_flg == 1)) { tabs--; } a_flg = 0; } String j_string = new String(string); strOut.append(j_string.substring(0,j)); for (int i=0; i<j; i++) string[i] = '\0'; j = 0; } else { if (s_flg != 0) { s_flg = 0; a_flg = 0; } } } //public void fprintf(int outfil, String out_string) { public void fprintf(String out_string) { //int out_len = out_string.length(); //String j_string = new String(string); strOut.append(out_string); } public int grabLines() { return lineNumber; } /* special edition of put string for comment processing */ public void putcoms() { int i = 0; int sav_s_flg = s_flg; if(j > 0) { if(s_flg != 0) { p_tabs(); s_flg = 0; } string[j] = '\0'; i = 0; while (string[i] == ' ') i++; if (lookup_com(w_jdoc) == 1) jdoc = 1; String strBuffer = new String(string,0,j); if (string[i] == '/' && string[i+1]=='*') { if ((last_char != ';') && (sav_s_flg==1) ) { //fprintf(outfil, strBuffer.substring(i,j)); fprintf(strBuffer.substring(i,j)); } else { //fprintf(outfil, strBuffer); fprintf(strBuffer); } } else { if (string[i]=='*' || jdoc == 0) //fprintf (outfil, " "+strBuffer.substring(i,j)); fprintf (" "+strBuffer.substring(i,j)); else //fprintf (outfil, " * "+strBuffer.substring(i,j)); fprintf (" * "+strBuffer.substring(i,j)); } j = 0; string[0] = '\0'; } } public void cpp_comment() throws IOException { c = getchr(); while(c != '\n' && c != '\r' && j<133) { string[j++] = c; c = getchr(); } lineNumber++; indent_puts(); s_flg = 1; } /* expand indentValue into tabs and spaces */ public void p_tabs() { int i,k; if (tabs<0) tabs = 0; if (tabs==0) return; i = tabs * indentValue; // calc number of spaces //j = i/8; /* calc number of tab chars */ for (k=0; k < i; k++) { strOut.append(indentChar); } } public char getchr() throws IOException { if((peek < 0) && (last_char != ' ') && (last_char != '\t')) { if((last_char != '\n') && (last_char != '\r')) p_char = last_char; } if(peek > 0) /* char was read previously */ { last_char = peekc; peek = -1; } else /* read next char in string */ { indexBlock++; if (indexBlock >= lineLength) { for (int ib=0; ib<readCount; ib++) chars[ib] = '\0'; lineLength = readCount = 0; reader.mark(1); if (reader.read() != -1) { reader.reset(); // back to the mark readCount = reader.read(chars); lineLength = readCount; strBlock = new String(chars); indexBlock = 0; last_char = strBlock.charAt(indexBlock); peek = -1; peekc = '`'; } else { EOF = 1; peekc = '\0'; } } else { last_char = strBlock.charAt(indexBlock); } } peek = -1; if (last_char == '\r') { last_char = getchr(); } return last_char; } /* else processing */ public void gotelse() { tabs = s_tabs[c_level][if_lev]; p_flg[level] = sp_flg[c_level][if_lev]; ind[level] = s_ind[c_level][if_lev]; if_flg = 1; } /* read to new_line */ public int getnl() throws IOException { int save_s_flg; save_s_flg = tabs; peekc = getchr(); //while ((peekc == '\t' || peekc == ' ') && // (j < string.length)) { while (peekc == '\t' || peekc == ' ') { string[j++] = peekc; peek = -1; peekc = '`'; peekc = getchr(); peek = 1; } peek = 1; if (peekc == '/') { peek = -1; peekc = '`'; peekc = getchr(); if (peekc == '*') { string[j++] = '/'; string[j++] = '*'; peek = -1; peekc = '`'; comment(); } else if (peekc == '/') { string[j++] = '/'; string[j++] = '/'; peek = -1; peekc = '`'; cpp_comment(); return (1); } else { string[j++] = '/'; peek = 1; } } peekc = getchr(); if(peekc == '\n') { lineNumber++; peek = -1; peekc = '`'; tabs = save_s_flg; return(1); } else { peek = 1; } return 0; } public int lookup (String keyword) { char r; int l,kk; //,k,i; String j_string = new String(string); if (j<1) return (0); kk=0; while(string[kk] == ' ')kk++; l=0; l = j_string.indexOf(keyword); if (l<0 || l!=kk) { return 0; } r = string[kk+keyword.length()]; if(r >= 'a' && r <= 'z') return(0); if(r >= 'A' && r <= 'Z') return(0); if(r >= '0' && r <= '9') return(0); if(r == '_' || r == '&') return(0); return (1); } public int lookup_com (String keyword) { //char r; int l,kk; //,k,i; String j_string = new String(string); if (j<1) return (0); kk=0; while(string[kk] == ' ')kk++; l=0; l = j_string.indexOf(keyword); if (l<0 || l!=kk) { return 0; } return (1); } public void run() { StringBuffer onechar; // Adding an additional newline as a hack around other errors String originalText = editor.getText() + "\n"; strOut = new StringBuffer(); indentValue = Preferences.getInteger("editor.tabs.size"); indentChar = new String(" "); lineNumber = 0; c_level = if_lev = level = e_flg = paren = 0; a_flg = q_flg = j = tabs = 0; if_flg = peek = -1; peekc = '`'; s_flg = 1; jdoc = 0; s_level = new int[10]; sp_flg = new int[20][10]; s_ind = new int[20][10]; s_if_lev = new int[10]; s_if_flg = new int[10]; ind = new int[10]; p_flg = new int[10]; s_tabs = new int[20][10]; w_else = new String ("else"); w_if_ = new String ("if"); w_for = new String ("for"); w_ds = new String ("default"); w_case = new String ("case"); w_cpp_comment = new String ("//"); w_jdoc = new String ("/**"); line_feed = new String ("\n"); // read as long as there is something to read EOF = 0; // = 1 set in getchr when EOF chars = new char[BLOCK_MAXLEN]; string = new char[BLOCK_MAXLEN]; try { // the whole process // open for input reader = new CharArrayReader(originalText.toCharArray()); // add buffering to that InputStream // bin = new BufferedInputStream(in); for (int ib = 0; ib < BLOCK_MAXLEN; ib++) chars[ib] = '\0'; lineLength = readCount = 0; // read up a block - remember how many bytes read readCount = reader.read(chars); strBlock = new String(chars); lineLength = readCount; lineNumber = 1; indexBlock = -1; j = 0; while (EOF == 0) { c = getchr(); switch(c) { default: string[j++] = c; if(c != ',') { l_char = c; } break; case ' ': case '\t': if(lookup(w_else) == 1) { gotelse(); if(s_flg == 0 || j > 0)string[j++] = c; indent_puts(); s_flg = 0; break; } if(s_flg == 0 || j > 0)string[j++] = c; break; case '\r': // <CR> for MS Windows 95 case '\n': lineNumber++; if (EOF==1) { break; } //String j_string = new String(string); e_flg = lookup(w_else); if(e_flg == 1) gotelse(); if (lookup_com(w_cpp_comment) == 1) { if (string[j] == '\n') { string[j] = '\0'; j--; } } indent_puts(); //fprintf(outfil, line_feed); fprintf(line_feed); s_flg = 1; if(e_flg == 1) { p_flg[level]++; tabs++; } else if(p_char == l_char) { a_flg = 1; } break; case '{': if(lookup(w_else) == 1)gotelse(); if (s_if_lev.length == c_level) { s_if_lev = PApplet.expand(s_if_lev); s_if_flg = PApplet.expand(s_if_flg); } s_if_lev[c_level] = if_lev; s_if_flg[c_level] = if_flg; if_lev = if_flg = 0; c_level++; if(s_flg == 1 && p_flg[level] != 0) { p_flg[level]--; tabs--; } string[j++] = c; indent_puts(); getnl() ; indent_puts(); //fprintf(outfil,"\n"); fprintf("\n"); tabs++; s_flg = 1; if(p_flg[level] > 0) { ind[level] = 1; level++; s_level[level] = c_level; } break; case '}': c_level--; if (c_level < 0) { EOF = 1; //System.out.println("eof b"); string[j++] = c; indent_puts(); break; } if ((if_lev = s_if_lev[c_level]-1) < 0) if_lev = 0; if_flg = s_if_flg[c_level]; indent_puts(); tabs--; p_tabs(); peekc = getchr(); if( peekc == ';') { onechar = new StringBuffer(); onechar.append(c); // the } onechar.append(';'); //fprintf(outfil, onechar.toString()); fprintf(onechar.toString()); peek = -1; peekc = '`'; } else { onechar = new StringBuffer(); onechar.append(c); //fprintf(outfil, onechar.toString()); fprintf(onechar.toString()); peek = 1; } getnl(); indent_puts(); //fprintf(outfil,"\n"); fprintf("\n"); s_flg = 1; if(c_level < s_level[level]) if(level > 0) level--; if(ind[level] != 0) { tabs -= p_flg[level]; p_flg[level] = 0; ind[level] = 0; } break; case '"': case '\'': string[j++] = c; cc = getchr(); while(cc != c) { // max. length of line should be 256 string[j++] = cc; if(cc == '\\') { cc = string[j++] = getchr(); } if(cc == '\n') { lineNumber++; indent_puts(); s_flg = 1; } cc = getchr(); } string[j++] = cc; if(getnl() == 1) { l_char = cc; peek = 1; peekc = '\n'; } break; case ';': string[j++] = c; indent_puts(); if(p_flg[level] > 0 && ind[level] == 0) { tabs -= p_flg[level]; p_flg[level] = 0; } getnl(); indent_puts(); //fprintf(outfil,"\n"); fprintf("\n"); s_flg = 1; if(if_lev > 0) if(if_flg == 1) { if_lev--; if_flg = 0; } else if_lev = 0; break; case '\\': string[j++] = c; string[j++] = getchr(); break; case '?': q_flg = 1; string[j++] = c; break; case ':': string[j++] = c; peekc = getchr(); if(peekc == ':') { indent_puts(); //fprintf (outfil,":"); fprintf(":"); peek = -1; peekc = '`'; break; } else { //int double_colon = 0; peek = 1; } if(q_flg == 1) { q_flg = 0; break; } if(lookup(w_ds) == 0 && lookup(w_case) == 0) { s_flg = 0; indent_puts(); } else { tabs--; indent_puts(); tabs++; } peekc = getchr(); if(peekc == ';') { fprintf(";"); peek = -1; peekc = '`'; } else { peek = 1; } getnl(); indent_puts(); fprintf("\n"); s_flg = 1; break; case '/': string[j++] = c; peekc = getchr(); if(peekc == '/') { string[j++] = peekc; peekc = '`'; peek = -1; cpp_comment(); //fprintf(outfil,"\n"); fprintf("\n"); break; } else { peek = 1; } if(peekc != '*') { break; } else { if (j > 0) string[j--] = '\0'; if (j > 0) indent_puts(); string[j++] = '/'; string[j++] = '*'; peek = -1; peekc = '`'; comment(); break; } case '#': string[j++] = c; cc = getchr(); while(cc != '\n') { string[j++] = cc; cc = getchr(); } string[j++] = cc; s_flg = 0; indent_puts(); s_flg = 1; break; case ')': paren--; if (paren < 0) { EOF = 1; //System.out.println("eof c"); } string[j++] = c; indent_puts(); if(getnl() == 1) { peekc = '\n'; peek = 1; if(paren != 0) { a_flg = 1; } else if(tabs > 0) { p_flg[level]++; tabs++; ind[level] = 0; } } break; case '(': string[j++] = c; paren++; if ((lookup(w_for) == 1)) { c = get_string(); while(c != ';') c = get_string(); ct=0; int for_done = 0; while (for_done==0) { c = get_string(); while(c != ')') { if(c == '(') ct++; c = get_string(); } if(ct != 0) { ct--; } else for_done = 1; } // endwhile for_done paren--; if (paren < 0) { EOF = 1; //System.out.println("eof d"); } indent_puts(); if(getnl() == 1) { peekc = '\n'; peek = 1; p_flg[level]++; tabs++; ind[level] = 0; } break; } if(lookup(w_if_) == 1) { indent_puts(); s_tabs[c_level][if_lev] = tabs; sp_flg[c_level][if_lev] = p_flg[level]; s_ind[c_level][if_lev] = ind[level]; if_lev++; if_flg = 1; } } // end switch //System.out.println("string len is " + string.length); //if (EOF == 1) System.out.println(string); //String j_string = new String(string); } // end while not EOF /* int bad; while ((bad = bin.read()) != -1) { System.out.print((char) bad); } */ /* char bad; //while ((bad = getchr()) != 0) { while (true) { getchr(); if (peek != -1) { System.out.print(last_char); } else { break; } } */ // save current (rough) selection point int selectionEnd = editor.getSelectionStop(); // make sure the caret would be past the end of the text if (strOut.length() < selectionEnd - 1) { selectionEnd = strOut.length() - 1; } reader.close(); // close buff String formattedText = strOut.toString(); if (formattedText.equals(originalText)) { editor.statusNotice("No changes necessary for Auto Format."); } else if (paren != 0) { // warn user if there are too many parens in either direction editor.statusError("Auto Format Canceled: Too many " + ((paren < 0) ? "right" : "left") + " parentheses."); } else if (c_level != 0) { // check braces only if parens are ok editor.statusError("Auto Format Canceled: Too many " + ((c_level < 0) ? "right" : "left") + " curly braces."); } else { // replace with new bootiful text // selectionEnd hopefully at least in the neighborhood editor.setText(formattedText); editor.setSelection(selectionEnd, selectionEnd); editor.getSketch().setModified(true); // mark as finished editor.statusNotice("Auto Format finished."); } } catch (Exception e) { editor.statusError(e); } } }