001: package tide.compiler;
002:
003: import java.io.*;
004: import snow.texteditor.SimpleDocument;
005: import java.util.*;
006: import tide.editor.linemessages.*;
007: import snow.utils.StringUtils;
008: import java.util.regex.*;
009:
010: /** Reads the error and input stream of the process that runs the compiler.
011: * Fill them in the LineMessagesManager and the compiler output tab.
012: */
013: public class CompilerProcessGobbler {
014: final SimpleDocument doc;
015: //final Process proc;
016:
017: public int numberOfWarnings = 0;
018: private int numberOfErrors = 0;
019:
020: // used to detect warnings and errors
021: boolean inWarning = false;
022:
023: public final Map<String, Integer> warningCategoryCount = new Hashtable<String, Integer>();
024:
025: private final StringBuilder currentMessage = new StringBuilder();
026: private boolean isCurrentMessageAnError = false;
027:
028: /** Reads the whole process input and error streams and WAIT until completion
029: */
030: public CompilerProcessGobbler(Process proc,
031: SimpleDocument compilerDoc) throws Exception {
032: // the messages are removed for each new compiled javaName, in the JavaCompilerCall
033: //LineMessagesManager.getInstance().removeMessages( CompilationMessage.class );
034:
035: this .doc = compilerDoc;
036: //this.proc = proc;
037:
038: Gobbler gin = new Gobbler(proc.getInputStream(), false);
039: gin.start();
040: Gobbler gout = new Gobbler(proc.getErrorStream(), true);
041: gout.start();
042:
043: gin.join();
044: gout.join(); // WAITS
045:
046: // don't forget !
047: addLastError();
048:
049: // manually refresh
050: LineMessagesManager.getInstance().refreshView();
051: }
052:
053: public final int getNumberOfErrors() {
054: return numberOfErrors;
055: }
056:
057: private String currentErrorJavaName = null;
058: private int currentErrorLine = -1;
059:
060: //private Vector vector;
061:
062: /** All the normal compiler ouputs occurs on stderr.
063: A warning is from ": warning:" up to a line with "^".
064: TODO: errors preceding warnings are erroneous marked as warnings.
065: */
066: public synchronized void errLineRead(String line) {
067: boolean isCountLine = false;
068:
069: if (line.indexOf(": warning:") >= 0) {
070: inWarning = true;
071: isCurrentMessageAnError = false;
072:
073: numberOfWarnings++;
074:
075: int pos = line.indexOf(": [");
076: if (pos > 0) {
077: String type = line.substring(pos + 3);
078: pos = type.indexOf(']');
079: if (pos > 0) {
080: type = type.substring(0, pos);
081: addWarningType(type);
082: }
083: } else {
084: //[March2008]: example: "warning: sun.net.ftp.FtpClient is Sun proprietary API and may be"
085: if (line.indexOf("is Sun proprietary API") > 0) {
086: addWarningType("Sun proprietary API");
087: }
088: }
089: } else {
090: // only present at the end...
091: int ne = parseNumberOfErrors(line);
092: if (ne > 0) {
093: isCountLine = true;
094: numberOfErrors += ne;
095: } else {
096: int nw = parseNumberOfWarnings(line);
097: if (nw > 0) {
098: isCountLine = true;
099: }
100: }
101: }
102:
103: if (line.indexOf(".java:") >= 0) // start of a new warning or error
104: {
105: // if not the first...
106: addLastError(); // or warning !
107:
108: isCurrentMessageAnError = !line.contains(": warning:");
109:
110: if (isCurrentMessageAnError)
111: inWarning = false;
112:
113: // the new error
114: int pos = line.indexOf(".java:");
115: String javaName = line.substring(0, pos).replace('/', '.');
116: javaName = javaName.replace('\\', '.');
117: currentErrorJavaName = javaName;
118:
119: String errLine = line.substring(pos + 6);
120: errLine = StringUtils.extractFromStartUpToFirstExcluded(
121: errLine, ":");
122: try {
123: currentErrorLine = Integer.parseInt(errLine);
124: } catch (Exception ex) {
125: ex.printStackTrace();
126: }
127: }
128:
129: if (!isCountLine) {
130: currentMessage.append(line);
131: currentMessage.append("\n");
132: }
133:
134: if (doc == null) {
135: System.out.println(line);
136: return;
137: }
138:
139: if (inWarning) {
140: doc.appendLine(line);
141: } else {
142: doc.appendErrorLine(line); //red
143: }
144: /*
145: if(line.trim().equals("^")) // end or warning (deprecation, ...) or error.
146: {
147: // must be reset at the next warning.
148: //inWarning = false; // false => in error
149: }*/
150:
151: }
152:
153: private void addWarningType(final String type) {
154: if (!warningCategoryCount.containsKey(type)) {
155: warningCategoryCount.put(type, 0);
156: }
157: warningCategoryCount.put(type,
158: warningCategoryCount.get(type) + 1);
159: }
160:
161: private void addLastError() {
162: if (this .currentErrorJavaName == null)
163: return;
164:
165: //System.out.println("Error at "+ currentErrorJavaName+":"+currentErrorLine); //\n>"+currentMessage+"<");
166: String mess = currentMessage.toString();
167: //boolean isErr = mess.toLowerCase().startsWith("warning:");
168:
169: CompilationMessage.createAndAdd(currentErrorJavaName,
170: currentErrorLine, mess);
171:
172: currentErrorLine = -1;
173: currentErrorJavaName = null;
174:
175: currentMessage.setLength(0);
176: }
177:
178: public synchronized void inLineRead(String line) {
179: if (doc == null) {
180: System.err.println(line);
181: return;
182: }
183: doc.appendLine(line);
184: }
185:
186: /** Two instances used, one for err one for out.
187: */
188: class Gobbler extends Thread {
189: private final InputStream input;
190: private final boolean isErrorStream;
191:
192: public Gobbler(InputStream input, boolean isErrorStream) {
193: this .input = input;
194: this .isErrorStream = isErrorStream;
195: }
196:
197: @Override
198: public void run() {
199: try {
200: final InputStreamReader isr = new InputStreamReader(
201: this .input);
202: final BufferedReader br = new BufferedReader(isr);
203: String line;
204: while ((line = br.readLine()) != null) {
205: if (isErrorStream) {
206: errLineRead(line);
207: } else {
208: inLineRead(line);
209: }
210: }
211: } catch (IOException ioe) {
212: ioe.printStackTrace();
213: }
214: }
215: }
216:
217: final private Pattern erPat = Pattern.compile("\\d+\\serror.*$");
218: final private Pattern warPat = Pattern.compile("\\d+\\swarning.*$");
219:
220: /** @return -1 if not correct format.
221: * Corrrect are "1 error*" and "XX errors*"
222: */
223: private int parseNumberOfErrors(String line) {
224: if (line.startsWith("1 error"))
225: return 1;
226: if (erPat.matcher(line).matches()) {
227: int pos = line.indexOf(' ');
228: return Integer.parseInt(line.substring(0, pos));
229: }
230:
231: return -1;
232: }
233:
234: private int parseNumberOfWarnings(String line) {
235: if (line.startsWith("1 warning"))
236: return 1;
237: if (warPat.matcher(line).matches()) {
238: int pos = line.indexOf(' ');
239: return Integer.parseInt(line.substring(0, pos));
240: }
241:
242: return -1;
243: }
244: }
|