• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt.internal.build;
18 
19 import com.android.ide.eclipse.adt.AndroidConstants;
20 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
21 
22 import org.eclipse.core.resources.IMarker;
23 import org.eclipse.core.resources.IProject;
24 import org.eclipse.core.resources.IResource;
25 import org.eclipse.core.runtime.CoreException;
26 
27 import java.io.File;
28 import java.util.ArrayList;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 
32 public class AaptParser {
33 
34     // TODO: rename the pattern to something that makes sense + javadoc comments.
35     /**
36      * Single line aapt warning for skipping files.<br>
37      * "  (skipping hidden file '&lt;file path&gt;'"
38      */
39     private final static Pattern sPattern0Line1 = Pattern.compile(
40             "^\\s+\\(skipping hidden file\\s'(.*)'\\)$"); //$NON-NLS-1$
41 
42     /**
43      * First line of dual line aapt error.<br>
44      * "ERROR at line &lt;line&gt;: &lt;error&gt;"<br>
45      * " (Occurred while parsing &lt;path&gt;)"
46      */
47     private final static Pattern sPattern1Line1 = Pattern.compile(
48             "^ERROR\\s+at\\s+line\\s+(\\d+):\\s+(.*)$"); //$NON-NLS-1$
49     /**
50      * Second line of dual line aapt error.<br>
51      * "ERROR at line &lt;line&gt;: &lt;error&gt;"<br>
52      * " (Occurred while parsing &lt;path&gt;)"<br>
53      * @see #sPattern1Line1
54      */
55     private final static Pattern sPattern1Line2 = Pattern.compile(
56             "^\\s+\\(Occurred while parsing\\s+(.*)\\)$");  //$NON-NLS-1$
57     /**
58      * First line of dual line aapt error.<br>
59      * "ERROR: &lt;error&gt;"<br>
60      * "Defined at file &lt;path&gt; line &lt;line&gt;"
61      */
62     private final static Pattern sPattern2Line1 = Pattern.compile(
63             "^ERROR:\\s+(.+)$"); //$NON-NLS-1$
64     /**
65      * Second line of dual line aapt error.<br>
66      * "ERROR: &lt;error&gt;"<br>
67      * "Defined at file &lt;path&gt; line &lt;line&gt;"<br>
68      * @see #sPattern2Line1
69      */
70     private final static Pattern sPattern2Line2 = Pattern.compile(
71             "Defined\\s+at\\s+file\\s+(.+)\\s+line\\s+(\\d+)"); //$NON-NLS-1$
72     /**
73      * Single line aapt error<br>
74      * "&lt;path&gt; line &lt;line&gt;: &lt;error&gt;"
75      */
76     private final static Pattern sPattern3Line1 = Pattern.compile(
77             "^(.+)\\sline\\s(\\d+):\\s(.+)$"); //$NON-NLS-1$
78     /**
79      * First line of dual line aapt error.<br>
80      * "ERROR parsing XML file &lt;path&gt;"<br>
81      * "&lt;error&gt; at line &lt;line&gt;"
82      */
83     private final static Pattern sPattern4Line1 = Pattern.compile(
84             "^Error\\s+parsing\\s+XML\\s+file\\s(.+)$"); //$NON-NLS-1$
85     /**
86      * Second line of dual line aapt error.<br>
87      * "ERROR parsing XML file &lt;path&gt;"<br>
88      * "&lt;error&gt; at line &lt;line&gt;"<br>
89      * @see #sPattern4Line1
90      */
91     private final static Pattern sPattern4Line2 = Pattern.compile(
92             "^(.+)\\s+at\\s+line\\s+(\\d+)$"); //$NON-NLS-1$
93 
94     /**
95      * Single line aapt warning<br>
96      * "&lt;path&gt;:&lt;line&gt;: &lt;error&gt;"
97      */
98     private final static Pattern sPattern5Line1 = Pattern.compile(
99             "^(.+?):(\\d+):\\s+WARNING:(.+)$"); //$NON-NLS-1$
100 
101     /**
102      * Single line aapt error<br>
103      * "&lt;path&gt;:&lt;line&gt;: &lt;error&gt;"
104      */
105     private final static Pattern sPattern6Line1 = Pattern.compile(
106             "^(.+?):(\\d+):\\s+(.+)$"); //$NON-NLS-1$
107 
108     /**
109      * 4 line aapt error<br>
110      * "ERROR: 9-path image &lt;path&gt; malformed"<br>
111      * Line 2 and 3 are taken as-is while line 4 is ignored (it repeats with<br>
112      * 'ERROR: failure processing &lt;path&gt;)
113      */
114     private final static Pattern sPattern7Line1 = Pattern.compile(
115             "^ERROR:\\s+9-patch\\s+image\\s+(.+)\\s+malformed\\.$"); //$NON-NLS-1$
116 
117     private final static Pattern sPattern8Line1 = Pattern.compile(
118             "^(invalid resource directory name): (.*)$"); //$NON-NLS-1$
119 
120     /**
121      * 2 line aapt error<br>
122      * "ERROR: Invalid configuration: foo"<br>
123      * "                              ^^^"<br>
124      * There's no need to parse the 2nd line.
125      */
126     private final static Pattern sPattern9Line1 = Pattern.compile(
127             "^Invalid configuration: (.+)$"); //$NON-NLS-1$
128 
129 
130     /**
131      * Parse the output of aapt and mark the incorrect file with error markers
132      *
133      * @param results the output of aapt
134      * @param project the project containing the file to mark
135      * @return true if the parsing failed, false if success.
136      */
parseOutput(ArrayList<String> results, IProject project)137     static final boolean parseOutput(ArrayList<String> results,
138             IProject project) {
139         // nothing to parse? just return false;
140         if (results.size() == 0) {
141             return false;
142         }
143 
144         // get the root of the project so that we can make IFile from full
145         // file path
146         String osRoot = project.getLocation().toOSString();
147 
148         Matcher m;
149 
150         for (int i = 0; i < results.size(); i++) {
151             String p = results.get(i);
152 
153             m = sPattern0Line1.matcher(p);
154             if (m.matches()) {
155                 // we ignore those (as this is an ignore message from aapt)
156                 continue;
157             }
158 
159             m = sPattern1Line1.matcher(p);
160             if (m.matches()) {
161                 String lineStr = m.group(1);
162                 String msg = m.group(2);
163 
164                 // get the matcher for the next line.
165                 m = getNextLineMatcher(results, ++i, sPattern1Line2);
166                 if (m == null) {
167                     return true;
168                 }
169 
170                 String location = m.group(1);
171 
172                 // check the values and attempt to mark the file.
173                 if (checkAndMark(location, lineStr, msg, osRoot, project,
174                         AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
175                     return true;
176                 }
177                 continue;
178             }
179 
180             // this needs to be tested before Pattern2 since they both start with 'ERROR:'
181             m = sPattern7Line1.matcher(p);
182             if (m.matches()) {
183                 String location = m.group(1);
184                 String msg = p; // default msg is the line in case we don't find anything else
185 
186                 if (++i < results.size()) {
187                     msg = results.get(i).trim();
188                     if (++i < results.size()) {
189                         msg = msg + " - " + results.get(i).trim(); //$NON-NLS-1$
190 
191                         // skip the next line
192                         i++;
193                     }
194                 }
195 
196                 // display the error
197                 if (checkAndMark(location, null, msg, osRoot, project,
198                         AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
199                     return true;
200                 }
201 
202                 // success, go to the next line
203                 continue;
204             }
205 
206             m =  sPattern2Line1.matcher(p);
207             if (m.matches()) {
208                 // get the msg
209                 String msg = m.group(1);
210 
211                 // get the matcher for the next line.
212                 m = getNextLineMatcher(results, ++i, sPattern2Line2);
213                 if (m == null) {
214                     return true;
215                 }
216 
217                 String location = m.group(1);
218                 String lineStr = m.group(2);
219 
220                 // check the values and attempt to mark the file.
221                 if (checkAndMark(location, lineStr, msg, osRoot, project,
222                         AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
223                     return true;
224                 }
225                 continue;
226             }
227 
228             m = sPattern3Line1.matcher(p);
229             if (m.matches()) {
230                 String location = m.group(1);
231                 String lineStr = m.group(2);
232                 String msg = m.group(3);
233 
234                 // check the values and attempt to mark the file.
235                 if (checkAndMark(location, lineStr, msg, osRoot, project,
236                         AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
237                     return true;
238                 }
239 
240                 // success, go to the next line
241                 continue;
242             }
243 
244             m = sPattern4Line1.matcher(p);
245             if (m.matches()) {
246                 // get the filename.
247                 String location = m.group(1);
248 
249                 // get the matcher for the next line.
250                 m = getNextLineMatcher(results, ++i, sPattern4Line2);
251                 if (m == null) {
252                     return true;
253                 }
254 
255                 String msg = m.group(1);
256                 String lineStr = m.group(2);
257 
258                 // check the values and attempt to mark the file.
259                 if (checkAndMark(location, lineStr, msg, osRoot, project,
260                         AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
261                     return true;
262                 }
263 
264                 // success, go to the next line
265                 continue;
266             }
267 
268             m = sPattern5Line1.matcher(p);
269             if (m.matches()) {
270                 String location = m.group(1);
271                 String lineStr = m.group(2);
272                 String msg = m.group(3);
273 
274                 // check the values and attempt to mark the file.
275                 if (checkAndMark(location, lineStr, msg, osRoot, project,
276                         AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) {
277                     return true;
278                 }
279 
280                 // success, go to the next line
281                 continue;
282             }
283 
284             m = sPattern6Line1.matcher(p);
285             if (m.matches()) {
286                 String location = m.group(1);
287                 String lineStr = m.group(2);
288                 String msg = m.group(3);
289 
290                 // check the values and attempt to mark the file.
291                 if (checkAndMark(location, lineStr, msg, osRoot, project,
292                         AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
293                     return true;
294                 }
295 
296                 // success, go to the next line
297                 continue;
298             }
299 
300             m = sPattern8Line1.matcher(p);
301             if (m.matches()) {
302                 String location = m.group(2);
303                 String msg = m.group(1);
304 
305                 // check the values and attempt to mark the file.
306                 if (checkAndMark(location, null, msg, osRoot, project,
307                         AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
308                     return true;
309                 }
310 
311                 // success, go to the next line
312                 continue;
313             }
314 
315             m = sPattern9Line1.matcher(p);
316             if (m.matches()) {
317                 String badConfig = m.group(1);
318                 String msg = String.format("APK Configuration filter '%1$s' is invalid", badConfig);
319 
320                 // skip the next line
321                 i++;
322 
323                 // check the values and attempt to mark the file.
324                 if (checkAndMark(null /*location*/, null, msg, osRoot, project,
325                         AndroidConstants.MARKER_AAPT_PACKAGE, IMarker.SEVERITY_ERROR) == false) {
326                     return true;
327                 }
328 
329                 // success, go to the next line
330                 continue;
331             }
332 
333             // invalid line format, flag as error, and bail
334             return true;
335         }
336 
337         return false;
338     }
339 
340     /**
341      * Check if the parameters gotten from the error output are valid, and mark
342      * the file with an AAPT marker.
343      * @param location the full OS path of the error file. If null, the project is marked
344      * @param lineStr
345      * @param message
346      * @param root The root directory of the project, in OS specific format.
347      * @param project
348      * @param markerId The marker id to put.
349      * @param severity The severity of the marker to put (IMarker.SEVERITY_*)
350      * @return true if the parameters were valid and the file was marked successfully.
351      *
352      * @see IMarker
353      */
checkAndMark(String location, String lineStr, String message, String root, IProject project, String markerId, int severity)354     private static final  boolean checkAndMark(String location, String lineStr,
355             String message, String root, IProject project, String markerId, int severity) {
356         // check this is in fact a file
357         if (location != null) {
358             File f = new File(location);
359             if (f.exists() == false) {
360                 return false;
361             }
362         }
363 
364         // get the line number
365         int line = -1; // default value for error with no line.
366 
367         if (lineStr != null) {
368             try {
369                 line = Integer.parseInt(lineStr);
370             } catch (NumberFormatException e) {
371                 // looks like the string we extracted wasn't a valid
372                 // file number. Parsing failed and we return true
373                 return false;
374             }
375         }
376 
377         // add the marker
378         IResource f2 = project;
379         if (location != null) {
380             f2 = getResourceFromFullPath(location, root, project);
381             if (f2 == null) {
382                 return false;
383             }
384         }
385 
386         // check if there's a similar marker already, since aapt is launched twice
387         boolean markerAlreadyExists = false;
388         try {
389             IMarker[] markers = f2.findMarkers(markerId, true, IResource.DEPTH_ZERO);
390 
391             for (IMarker marker : markers) {
392                 int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1);
393                 if (tmpLine != line) {
394                     break;
395                 }
396 
397                 int tmpSeverity = marker.getAttribute(IMarker.SEVERITY, -1);
398                 if (tmpSeverity != severity) {
399                     break;
400                 }
401 
402                 String tmpMsg = marker.getAttribute(IMarker.MESSAGE, null);
403                 if (tmpMsg == null || tmpMsg.equals(message) == false) {
404                     break;
405                 }
406 
407                 // if we're here, all the marker attributes are equals, we found it
408                 // and exit
409                 markerAlreadyExists = true;
410                 break;
411             }
412 
413         } catch (CoreException e) {
414             // if we couldn't get the markers, then we just mark the file again
415             // (since markerAlreadyExists is initialized to false, we do nothing)
416         }
417 
418         if (markerAlreadyExists == false) {
419             BaseProjectHelper.markResource(f2, markerId, message, line, severity);
420         }
421 
422         return true;
423     }
424 
425     /**
426      * Returns a matching matcher for the next line
427      * @param lines The array of lines
428      * @param nextIndex The index of the next line
429      * @param pattern The pattern to match
430      * @return null if error or no match, the matcher otherwise.
431      */
getNextLineMatcher(ArrayList<String> lines, int nextIndex, Pattern pattern)432     private static final Matcher getNextLineMatcher(ArrayList<String> lines,
433             int nextIndex, Pattern pattern) {
434         // unless we can't, because we reached the last line
435         if (nextIndex == lines.size()) {
436             // we expected a 2nd line, so we flag as error
437             // and we bail
438             return null;
439         }
440 
441         Matcher m = pattern.matcher(lines.get(nextIndex));
442         if (m.matches()) {
443            return m;
444         }
445 
446         return null;
447     }
448 
getResourceFromFullPath(String filename, String root, IProject project)449     private static IResource getResourceFromFullPath(String filename, String root,
450             IProject project) {
451         if (filename.startsWith(root)) {
452             String file = filename.substring(root.length());
453 
454             // get the resource
455             IResource r = project.findMember(file);
456 
457             // if the resource is valid, we add the marker
458             if (r.exists()) {
459                 return r;
460             }
461         }
462 
463         return null;
464     }
465 
466 }
467