Debug session thread and socket leak fixed:
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / actions / ExternalPHPParser.java
1 package net.sourceforge.phpeclipse.actions;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.text.MessageFormat;
6 import java.util.Hashtable;
7
8 import net.sourceforge.phpdt.internal.ui.util.StringUtil;
9 import net.sourceforge.phpeclipse.PHPConsole;
10 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
11
12 import org.eclipse.core.resources.IFile;
13 import org.eclipse.core.resources.IMarker;
14 import org.eclipse.core.runtime.CoreException;
15 import org.eclipse.jface.dialogs.MessageDialog;
16 import org.eclipse.jface.preference.IPreferenceStore;
17 import org.eclipse.ui.texteditor.MarkerUtilities;
18
19 /**
20  * Calls the external parser and generates problem markers if necessary
21  */
22 public class ExternalPHPParser {
23   private final static String PROBLEM_ID = "net.sourceforge.phpeclipse.problem";
24
25   // strings for external parser call
26   private static final String PARSE_ERROR_STRING = "Parse error"; //$NON-NLS-1$
27
28   private static final String PARSE_WARNING_STRING = "Warning"; //$NON-NLS-1$
29
30   public static final int ERROR = 2;
31
32   public static final int WARNING = 1;
33
34   public static final int INFO = 0;
35
36   public static final int TASK = 3;
37
38   // TODO design error? Analyze why fileToParse must be static ???
39   final protected IFile fFileToParse;
40
41   public ExternalPHPParser(IFile file) {
42     fFileToParse = file;
43   }
44
45   /**
46    * Call the php parse command ( php -l -f <filename> ) and create markers according to the external parser output.
47    * 
48    * @param file
49    *          the file that will be parsed
50    */
51   public void phpExternalParse() {
52     //IFile file = (IFile) resource;
53     //  final IPath path = file.getFullPath();
54     final IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
55     final String filename = fFileToParse.getLocation().toString();
56
57     final String[] arguments = { filename };
58     final MessageFormat form = new MessageFormat(store.getString(PHPeclipsePlugin.EXTERNAL_PARSER_PREF));
59     final String command = form.format(arguments);
60
61     final String parserResult = getParserOutput(command, "External parser: ");
62
63     try {
64       // parse the buffer to find the errors and warnings
65       createMarkers(parserResult, fFileToParse);
66     } catch (CoreException e) {
67     }
68   }
69
70   /**
71    * Create markers according to the external parser output.
72    * 
73    * @param output
74    *          the external parser output
75    * @param file
76    *          the file that was parsed.
77    */
78   protected void createMarkers(final String output, final IFile file) throws CoreException {
79     // delete all markers
80     file.deleteMarkers(PROBLEM_ID, false, 0);
81
82     int indx = 0;
83     int brIndx;
84     boolean flag = true;
85     while ((brIndx = output.indexOf("<br />", indx)) != -1) {
86       // newer php error output (tested with 4.2.3)
87       scanLine(output, file, indx, brIndx);
88       indx = brIndx + 6;
89       flag = false;
90     }
91     if (flag) {
92       while ((brIndx = output.indexOf("<br>", indx)) != -1) {
93         // older php error output (tested with 4.2.3)
94         scanLine(output, file, indx, brIndx);
95         indx = brIndx + 4;
96       }
97     }
98   }
99
100   private void scanLine(final String output, final IFile file, final int indx, final int brIndx) throws CoreException {
101     String current;
102     //  String outLineNumberString; never used
103     final StringBuffer lineNumberBuffer = new StringBuffer(10);
104     char ch;
105     current = output.substring(indx, brIndx);
106
107     if (current.indexOf(PARSE_WARNING_STRING) != -1 || current.indexOf(PARSE_ERROR_STRING) != -1) {
108       final int onLine = current.indexOf("on line <b>");
109       if (onLine != -1) {
110         lineNumberBuffer.delete(0, lineNumberBuffer.length());
111         for (int i = onLine; i < current.length(); i++) {
112           ch = current.charAt(i);
113           if ('0' <= ch && '9' >= ch) {
114             lineNumberBuffer.append(ch);
115           }
116         }
117
118         final int lineNumber = Integer.parseInt(lineNumberBuffer.toString());
119
120         final Hashtable attributes = new Hashtable();
121
122         current = StringUtil.replaceAll(current, "\n", "");
123         current = StringUtil.replaceAll(current, "<b>", "");
124         current = StringUtil.replaceAll(current, "</b>", "");
125         MarkerUtilities.setMessage(attributes, current);
126
127         if (current.indexOf(PARSE_ERROR_STRING) != -1)
128           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
129         else if (current.indexOf(PARSE_WARNING_STRING) != -1)
130           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
131         else
132           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
133         MarkerUtilities.setLineNumber(attributes, lineNumber);
134         MarkerUtilities.createMarker(file, attributes, PROBLEM_ID);
135       }
136     }
137   }
138
139   /**
140    * This will set a marker.
141    * 
142    * @param file
143    *          the file that generated the marker
144    * @param message
145    *          the message
146    * @param charStart
147    *          the starting character
148    * @param charEnd
149    *          the end character
150    * @param errorLevel
151    *          the error level ({@link ExternalPHPParser#ERROR},{@link ExternalPHPParser#INFO},{@link ExternalPHPParser#WARNING}),
152    *          {@link ExternalPHPParser#TASK})
153    * @throws CoreException
154    *           an exception throwed by the MarkerUtilities
155    */
156   private void setMarker(final IFile file, final String message, final int charStart, final int charEnd, final int errorLevel)
157       throws CoreException {
158     if (file != null) {
159       final Hashtable attributes = new Hashtable();
160       MarkerUtilities.setMessage(attributes, message);
161       switch (errorLevel) {
162       case ERROR:
163         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
164         break;
165       case WARNING:
166         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
167         break;
168       case INFO:
169         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
170         break;
171       case TASK:
172         attributes.put(IMarker.SEVERITY, new Integer(IMarker.TASK));
173         break;
174       }
175       MarkerUtilities.setCharStart(attributes, charStart);
176       MarkerUtilities.setCharEnd(attributes, charEnd);
177       MarkerUtilities.createMarker(file, attributes, PROBLEM_ID);
178     }
179   }
180
181   /**
182    * This will set a marker.
183    * 
184    * @param file
185    *          the file that generated the marker
186    * @param message
187    *          the message
188    * @param line
189    *          the line number
190    * @param errorLevel
191    *          the error level ({@link ExternalPHPParser#ERROR},{@link ExternalPHPParser#INFO},{@link ExternalPHPParser#WARNING})
192    * @throws CoreException
193    *           an exception throwed by the MarkerUtilities
194    */
195   private void setMarker(final IFile file, final String message, final int line, final int errorLevel, final String location)
196       throws CoreException {
197     if (file != null) {
198       String markerKind = PROBLEM_ID;
199       final Hashtable attributes = new Hashtable();
200       MarkerUtilities.setMessage(attributes, message);
201       switch (errorLevel) {
202       case ERROR:
203         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
204         break;
205       case WARNING:
206         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
207         break;
208       case INFO:
209         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
210         break;
211       case TASK:
212         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
213         markerKind = IMarker.TASK;
214         break;
215       }
216       attributes.put(IMarker.LOCATION, location);
217       MarkerUtilities.setLineNumber(attributes, line);
218       MarkerUtilities.createMarker(file, attributes, markerKind);
219     }
220   }
221
222   /**
223    * This will set a marker.
224    * 
225    * @param message
226    *          the message
227    * @param charStart
228    *          the starting character
229    * @param charEnd
230    *          the end character
231    * @param errorLevel
232    *          the error level ({@link ExternalPHPParser#ERROR},{@link ExternalPHPParser#INFO},{@link ExternalPHPParser#WARNING})
233    * @throws CoreException
234    *           an exception throwed by the MarkerUtilities
235    */
236   private void setMarker(final String message, final int charStart, final int charEnd, final int errorLevel, final String location)
237       throws CoreException {
238     if (fFileToParse != null) {
239       setMarker(fFileToParse, message, charStart, charEnd, errorLevel, location);
240     }
241   }
242
243   /**
244    * This will set a marker.
245    * 
246    * @param file
247    *          the file that generated the marker
248    * @param message
249    *          the message
250    * @param charStart
251    *          the starting character
252    * @param charEnd
253    *          the end character
254    * @param errorLevel
255    *          the error level ({@link ExternalPHPParser#ERROR},{@link ExternalPHPParser#INFO},{@link ExternalPHPParser#WARNING})
256    * @param location
257    *          the location of the error
258    * @throws CoreException
259    *           an exception throwed by the MarkerUtilities
260    */
261   private void setMarker(final IFile file, final String message, final int charStart, final int charEnd, final int errorLevel,
262       final String location) throws CoreException {
263     if (file != null) {
264       final Hashtable attributes = new Hashtable();
265       MarkerUtilities.setMessage(attributes, message);
266       switch (errorLevel) {
267       case ERROR:
268         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
269         break;
270       case WARNING:
271         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
272         break;
273       case INFO:
274         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
275         break;
276       case TASK:
277         attributes.put(IMarker.SEVERITY, new Integer(IMarker.TASK));
278         break;
279       }
280       attributes.put(IMarker.LOCATION, location);
281       MarkerUtilities.setCharStart(attributes, charStart);
282       MarkerUtilities.setCharEnd(attributes, charEnd);
283       MarkerUtilities.createMarker(file, attributes, PROBLEM_ID); //IMarker.PROBLEM);
284     }
285   }
286
287   private String getParserOutput(String command, String consoleMessage) {
288     try {
289       PHPConsole console = new PHPConsole();
290       try {
291         console.println(consoleMessage + command);
292       } catch (Throwable th) {
293
294       }
295
296       Runtime runtime = Runtime.getRuntime();
297
298       // runs the command
299       Process p = runtime.exec(command);
300
301       // gets the input stream to have the post-compile-time information
302       InputStream stream = p.getInputStream();
303
304       // get the string from Stream
305       String consoleOutput = PHPConsole.getStringFromStream(stream);
306
307       // prints out the information
308       if (console != null) {
309         console.print(consoleOutput);
310       }
311       return consoleOutput;
312
313     } catch (IOException e) {
314       MessageDialog.openInformation(null, "IOException: ", e.getMessage());
315     }
316     return "";
317   }
318 }