• 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.AdtConstants;
20 import com.android.ide.eclipse.adt.AdtPlugin;
21 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
22 
23 import org.eclipse.core.resources.IFile;
24 import org.eclipse.core.resources.IMarker;
25 import org.eclipse.core.resources.IProject;
26 import org.eclipse.core.resources.IResource;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.jface.text.FindReplaceDocumentAdapter;
29 import org.eclipse.jface.text.IDocument;
30 import org.eclipse.jface.text.IRegion;
31 import org.eclipse.jface.text.Region;
32 import org.eclipse.ui.editors.text.TextFileDocumentProvider;
33 import org.eclipse.ui.texteditor.IDocumentProvider;
34 
35 import java.io.File;
36 import java.util.List;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39 
40 public final class AaptParser {
41 
42     // TODO: rename the pattern to something that makes sense + javadoc comments.
43     /**
44      * Single line aapt warning for skipping files.<br>
45      * "  (skipping hidden file '&lt;file path&gt;'"
46      */
47     private final static Pattern sPattern0Line1 = Pattern.compile(
48             "^\\s+\\(skipping hidden file\\s'(.*)'\\)$"); //$NON-NLS-1$
49 
50     /**
51      * First line of dual line aapt error.<br>
52      * "ERROR at line &lt;line&gt;: &lt;error&gt;"<br>
53      * " (Occurred while parsing &lt;path&gt;)"
54      */
55     private final static Pattern sPattern1Line1 = Pattern.compile(
56             "^ERROR\\s+at\\s+line\\s+(\\d+):\\s+(.*)$"); //$NON-NLS-1$
57     /**
58      * Second line of dual line aapt error.<br>
59      * "ERROR at line &lt;line&gt;: &lt;error&gt;"<br>
60      * " (Occurred while parsing &lt;path&gt;)"<br>
61      * @see #sPattern1Line1
62      */
63     private final static Pattern sPattern1Line2 = Pattern.compile(
64             "^\\s+\\(Occurred while parsing\\s+(.*)\\)$");  //$NON-NLS-1$
65     /**
66      * First line of dual line aapt error.<br>
67      * "ERROR: &lt;error&gt;"<br>
68      * "Defined at file &lt;path&gt; line &lt;line&gt;"
69      */
70     private final static Pattern sPattern2Line1 = Pattern.compile(
71             "^ERROR:\\s+(.+)$"); //$NON-NLS-1$
72     /**
73      * Second line of dual line aapt error.<br>
74      * "ERROR: &lt;error&gt;"<br>
75      * "Defined at file &lt;path&gt; line &lt;line&gt;"<br>
76      * @see #sPattern2Line1
77      */
78     private final static Pattern sPattern2Line2 = Pattern.compile(
79             "Defined\\s+at\\s+file\\s+(.+)\\s+line\\s+(\\d+)"); //$NON-NLS-1$
80     /**
81      * Single line aapt error<br>
82      * "&lt;path&gt; line &lt;line&gt;: &lt;error&gt;"
83      */
84     private final static Pattern sPattern3Line1 = Pattern.compile(
85             "^(.+)\\sline\\s(\\d+):\\s(.+)$"); //$NON-NLS-1$
86     /**
87      * First line of dual line aapt error.<br>
88      * "ERROR parsing XML file &lt;path&gt;"<br>
89      * "&lt;error&gt; at line &lt;line&gt;"
90      */
91     private final static Pattern sPattern4Line1 = Pattern.compile(
92             "^Error\\s+parsing\\s+XML\\s+file\\s(.+)$"); //$NON-NLS-1$
93     /**
94      * Second line of dual line aapt error.<br>
95      * "ERROR parsing XML file &lt;path&gt;"<br>
96      * "&lt;error&gt; at line &lt;line&gt;"<br>
97      * @see #sPattern4Line1
98      */
99     private final static Pattern sPattern4Line2 = Pattern.compile(
100             "^(.+)\\s+at\\s+line\\s+(\\d+)$"); //$NON-NLS-1$
101 
102     /**
103      * Single line aapt warning<br>
104      * "&lt;path&gt;:&lt;line&gt;: &lt;error&gt;"
105      */
106     private final static Pattern sPattern5Line1 = Pattern.compile(
107             "^(.+?):(\\d+):\\s+WARNING:(.+)$"); //$NON-NLS-1$
108 
109     /**
110      * Single line aapt error<br>
111      * "&lt;path&gt;:&lt;line&gt;: &lt;error&gt;"
112      */
113     private final static Pattern sPattern6Line1 = Pattern.compile(
114             "^(.+?):(\\d+):\\s+(.+)$"); //$NON-NLS-1$
115 
116     /**
117      * 4 line aapt error<br>
118      * "ERROR: 9-path image &lt;path&gt; malformed"<br>
119      * Line 2 and 3 are taken as-is while line 4 is ignored (it repeats with<br>
120      * 'ERROR: failure processing &lt;path&gt;)
121      */
122     private final static Pattern sPattern7Line1 = Pattern.compile(
123             "^ERROR:\\s+9-patch\\s+image\\s+(.+)\\s+malformed\\.$"); //$NON-NLS-1$
124 
125     private final static Pattern sPattern8Line1 = Pattern.compile(
126             "^(invalid resource directory name): (.*)$"); //$NON-NLS-1$
127 
128     /**
129      * Portion of the error message which states the context in which the error occurred,
130      * such as which property was being processed and what the string value was that
131      * caused the error.
132      * <p>
133      * Example:
134      * error: No resource found that matches the given name (at 'text' with value '@string/foo')
135      */
136     private static final Pattern sValueRangePattern =
137         Pattern.compile("\\(at '(.+)' with value '(.*)'\\)"); //$NON-NLS-1$
138 
139 
140     /**
141      * Portion of error message which points to the second occurrence of a repeated resource
142      * definition.
143      * <p>
144      * Example:
145      * error: Resource entry repeatedStyle1 already has bag item android:gravity.
146      */
147     private static final Pattern sRepeatedRangePattern =
148         Pattern.compile("Resource entry (.+) already has bag item (.+)\\."); //$NON-NLS-1$
149 
150     /**
151      * Suffix of error message which points to the first occurrence of a repeated resource
152      * definition.
153      * Example:
154      * Originally defined here.
155      */
156     private static final String ORIGINALLY_DEFINED_MSG = "Originally defined here."; //$NON-NLS-1$
157 
158     /**
159      * Portion of error message which points to the second occurrence of a repeated resource
160      * definition.
161      * <p>
162      * Example:
163      * error: Resource entry repeatedStyle1 already has bag item android:gravity.
164      */
165     private static final Pattern sNoResourcePattern =
166         Pattern.compile("No resource found that matches the given name: attr '(.+)'\\."); //$NON-NLS-1$
167 
168     /**
169      * Portion of error message which points to a missing required attribute in a
170      * resource definition.
171      * <p>
172      * Example:
173      * error: error: A 'name' attribute is required for <style>
174      */
175     private static final Pattern sRequiredPattern =
176         Pattern.compile("A '(.+)' attribute is required for <(.+)>"); //$NON-NLS-1$
177 
178     /**
179      * 2 line aapt error<br>
180      * "ERROR: Invalid configuration: foo"<br>
181      * "                              ^^^"<br>
182      * There's no need to parse the 2nd line.
183      */
184     private final static Pattern sPattern9Line1 = Pattern.compile(
185             "^Invalid configuration: (.+)$"); //$NON-NLS-1$
186 
187     /**
188      * Parse the output of aapt and mark the incorrect file with error markers
189      *
190      * @param results the output of aapt
191      * @param project the project containing the file to mark
192      * @return true if the parsing failed, false if success.
193      */
parseOutput(List<String> results, IProject project)194     public static boolean parseOutput(List<String> results, IProject project) {
195         int size = results.size();
196         if (size > 0) {
197             return parseOutput(results.toArray(new String[size]), project);
198         }
199 
200         return true;
201     }
202 
203     /**
204      * Parse the output of aapt and mark the incorrect file with error markers
205      *
206      * @param results the output of aapt
207      * @param project the project containing the file to mark
208      * @return true if the parsing failed, false if success.
209      */
parseOutput(String[] results, IProject project)210     public static boolean parseOutput(String[] results, IProject project) {
211         // nothing to parse? just return false;
212         if (results.length == 0) {
213             return false;
214         }
215 
216         // get the root of the project so that we can make IFile from full
217         // file path
218         String osRoot = project.getLocation().toOSString();
219 
220         Matcher m;
221 
222         for (int i = 0; i < results.length ; i++) {
223             String p = results[i];
224 
225             m = sPattern0Line1.matcher(p);
226             if (m.matches()) {
227                 // we ignore those (as this is an ignore message from aapt)
228                 continue;
229             }
230 
231             m = sPattern1Line1.matcher(p);
232             if (m.matches()) {
233                 String lineStr = m.group(1);
234                 String msg = m.group(2);
235 
236                 // get the matcher for the next line.
237                 m = getNextLineMatcher(results, ++i, sPattern1Line2);
238                 if (m == null) {
239                     return true;
240                 }
241 
242                 String location = m.group(1);
243 
244                 // check the values and attempt to mark the file.
245                 if (checkAndMark(location, lineStr, msg, osRoot, project,
246                         AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
247                     return true;
248                 }
249                 continue;
250             }
251 
252             // this needs to be tested before Pattern2 since they both start with 'ERROR:'
253             m = sPattern7Line1.matcher(p);
254             if (m.matches()) {
255                 String location = m.group(1);
256                 String msg = p; // default msg is the line in case we don't find anything else
257 
258                 if (++i < results.length) {
259                     msg = results[i].trim();
260                     if (++i < results.length) {
261                         msg = msg + " - " + results[i].trim(); //$NON-NLS-1$
262 
263                         // skip the next line
264                         i++;
265                     }
266                 }
267 
268                 // display the error
269                 if (checkAndMark(location, null, msg, osRoot, project,
270                         AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
271                     return true;
272                 }
273 
274                 // success, go to the next line
275                 continue;
276             }
277 
278             m =  sPattern2Line1.matcher(p);
279             if (m.matches()) {
280                 // get the msg
281                 String msg = m.group(1);
282 
283                 // get the matcher for the next line.
284                 m = getNextLineMatcher(results, ++i, sPattern2Line2);
285                 if (m == null) {
286                     return true;
287                 }
288 
289                 String location = m.group(1);
290                 String lineStr = m.group(2);
291 
292                 // check the values and attempt to mark the file.
293                 if (checkAndMark(location, lineStr, msg, osRoot, project,
294                         AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
295                     return true;
296                 }
297                 continue;
298             }
299 
300             m = sPattern3Line1.matcher(p);
301             if (m.matches()) {
302                 String location = m.group(1);
303                 String lineStr = m.group(2);
304                 String msg = m.group(3);
305 
306                 // check the values and attempt to mark the file.
307                 if (checkAndMark(location, lineStr, msg, osRoot, project,
308                         AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
309                     return true;
310                 }
311 
312                 // success, go to the next line
313                 continue;
314             }
315 
316             m = sPattern4Line1.matcher(p);
317             if (m.matches()) {
318                 // get the filename.
319                 String location = m.group(1);
320 
321                 // get the matcher for the next line.
322                 m = getNextLineMatcher(results, ++i, sPattern4Line2);
323                 if (m == null) {
324                     return true;
325                 }
326 
327                 String msg = m.group(1);
328                 String lineStr = m.group(2);
329 
330                 // check the values and attempt to mark the file.
331                 if (checkAndMark(location, lineStr, msg, osRoot, project,
332                         AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
333                     return true;
334                 }
335 
336                 // success, go to the next line
337                 continue;
338             }
339 
340             m = sPattern5Line1.matcher(p);
341             if (m.matches()) {
342                 String location = m.group(1);
343                 String lineStr = m.group(2);
344                 String msg = m.group(3);
345 
346                 // check the values and attempt to mark the file.
347                 if (checkAndMark(location, lineStr, msg, osRoot, project,
348                         AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) {
349                     return true;
350                 }
351 
352                 // success, go to the next line
353                 continue;
354             }
355 
356             m = sPattern6Line1.matcher(p);
357             if (m.matches()) {
358                 String location = m.group(1);
359                 String lineStr = m.group(2);
360                 String msg = m.group(3);
361 
362                 // check the values and attempt to mark the file.
363                 if (checkAndMark(location, lineStr, msg, osRoot, project,
364                         AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
365                     return true;
366                 }
367 
368                 // success, go to the next line
369                 continue;
370             }
371 
372             m = sPattern8Line1.matcher(p);
373             if (m.matches()) {
374                 String location = m.group(2);
375                 String msg = m.group(1);
376 
377                 // check the values and attempt to mark the file.
378                 if (checkAndMark(location, null, msg, osRoot, project,
379                         AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
380                     return true;
381                 }
382 
383                 // success, go to the next line
384                 continue;
385             }
386 
387             m = sPattern9Line1.matcher(p);
388             if (m.matches()) {
389                 String badConfig = m.group(1);
390                 String msg = String.format("APK Configuration filter '%1$s' is invalid", badConfig);
391 
392                 // skip the next line
393                 i++;
394 
395                 // check the values and attempt to mark the file.
396                 if (checkAndMark(null /*location*/, null, msg, osRoot, project,
397                         AdtConstants.MARKER_AAPT_PACKAGE, IMarker.SEVERITY_ERROR) == false) {
398                     return true;
399                 }
400 
401                 // success, go to the next line
402                 continue;
403             }
404 
405             // invalid line format, flag as error, and bail
406             return true;
407         }
408 
409         return false;
410     }
411 
412     /**
413      * Check if the parameters gotten from the error output are valid, and mark
414      * the file with an AAPT marker.
415      * @param location the full OS path of the error file. If null, the project is marked
416      * @param lineStr
417      * @param message
418      * @param root The root directory of the project, in OS specific format.
419      * @param project
420      * @param markerId The marker id to put.
421      * @param severity The severity of the marker to put (IMarker.SEVERITY_*)
422      * @return true if the parameters were valid and the file was marked successfully.
423      *
424      * @see IMarker
425      */
checkAndMark(String location, String lineStr, String message, String root, IProject project, String markerId, int severity)426     private static final  boolean checkAndMark(String location, String lineStr,
427             String message, String root, IProject project, String markerId, int severity) {
428         // check this is in fact a file
429         if (location != null) {
430             File f = new File(location);
431             if (f.exists() == false) {
432                 return false;
433             }
434         }
435 
436         // get the line number
437         int line = -1; // default value for error with no line.
438 
439         if (lineStr != null) {
440             try {
441                 line = Integer.parseInt(lineStr);
442             } catch (NumberFormatException e) {
443                 // looks like the string we extracted wasn't a valid
444                 // file number. Parsing failed and we return true
445                 return false;
446             }
447         }
448 
449         // add the marker
450         IResource f2 = project;
451         if (location != null) {
452             f2 = getResourceFromFullPath(location, root, project);
453             if (f2 == null) {
454                 return false;
455             }
456         }
457 
458         // Attempt to determine the exact range of characters affected by this error.
459         // This will look up the actual text of the file, go to the particular error line
460         // and scan for the specific string mentioned in the error.
461         int startOffset = -1;
462         int endOffset = -1;
463         if (f2 instanceof IFile) {
464             IRegion region = findRange((IFile) f2, line, message);
465             if (region != null) {
466                 startOffset = region.getOffset();
467                 endOffset = startOffset + region.getLength();
468             }
469         }
470 
471         // check if there's a similar marker already, since aapt is launched twice
472         boolean markerAlreadyExists = false;
473         try {
474             IMarker[] markers = f2.findMarkers(markerId, true, IResource.DEPTH_ZERO);
475 
476             for (IMarker marker : markers) {
477                 if (startOffset != -1) {
478                     int tmpBegin = marker.getAttribute(IMarker.CHAR_START, -1);
479                     if (tmpBegin != startOffset) {
480                         break;
481                     }
482                     int tmpEnd = marker.getAttribute(IMarker.CHAR_END, -1);
483                     if (tmpEnd != startOffset) {
484                         break;
485                     }
486                 }
487 
488                 int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1);
489                 if (tmpLine != line) {
490                     break;
491                 }
492 
493                 int tmpSeverity = marker.getAttribute(IMarker.SEVERITY, -1);
494                 if (tmpSeverity != severity) {
495                     break;
496                 }
497 
498                 String tmpMsg = marker.getAttribute(IMarker.MESSAGE, null);
499                 if (tmpMsg == null || tmpMsg.equals(message) == false) {
500                     break;
501                 }
502 
503                 // if we're here, all the marker attributes are equals, we found it
504                 // and exit
505                 markerAlreadyExists = true;
506                 break;
507             }
508 
509         } catch (CoreException e) {
510             // if we couldn't get the markers, then we just mark the file again
511             // (since markerAlreadyExists is initialized to false, we do nothing)
512         }
513 
514         if (markerAlreadyExists == false) {
515             BaseProjectHelper.markResource(f2, markerId, message, line,
516                     startOffset, endOffset, severity);
517         }
518 
519         return true;
520     }
521 
522     /**
523      * Given an aapt error message in a given file and a given (initial) line number,
524      * return the corresponding offset range for the error, or null.
525      */
findRange(IFile file, int line, String message)526     private static IRegion findRange(IFile file, int line, String message) {
527         Matcher matcher = sValueRangePattern.matcher(message);
528         if (matcher.find()) {
529             String property = matcher.group(1);
530             String value = matcher.group(2);
531 
532             // First find the property. We can't just immediately look for the
533             // value, because there could be other attributes in this element
534             // earlier than the one in error, and we might accidentally pick
535             // up on a different occurrence of the value in a context where
536             // it is valid.
537             if (value.length() > 0) {
538                 return findRange(file, line, property, value);
539             } else {
540                 // Find first occurrence of property followed by '' or ""
541                 IRegion region1 = findRange(file, line, property, "\"\""); //$NON-NLS-1$
542                 IRegion region2 = findRange(file, line, property, "''");   //$NON-NLS-1$
543                 if (region1 == null) {
544                     if (region2 == null) {
545                         // Highlight the property instead
546                         return findRange(file, line, property, null);
547                     }
548                     return region2;
549                 } else if (region2 == null) {
550                     return region1;
551                 } else if (region1.getOffset() < region2.getOffset()) {
552                     return region1;
553                 } else {
554                     return region2;
555                 }
556             }
557         }
558 
559         matcher = sRepeatedRangePattern.matcher(message);
560         if (matcher.find()) {
561             String property = matcher.group(2);
562             return findRange(file, line, property, null);
563         }
564 
565         matcher = sNoResourcePattern.matcher(message);
566         if (matcher.find()) {
567             String property = matcher.group(1);
568             return findRange(file, line, property, null);
569         }
570 
571         matcher = sRequiredPattern.matcher(message);
572         if (matcher.find()) {
573             String elementName = matcher.group(2);
574             IRegion region = findRange(file, line, '<' + elementName, null);
575             if (region != null && region.getLength() > 1) {
576                 // Skip the opening <
577                 region = new Region(region.getOffset() + 1, region.getLength() - 1);
578             }
579             return region;
580         }
581 
582         if (message.endsWith(ORIGINALLY_DEFINED_MSG)) {
583             return findLineTextRange(file, line);
584         }
585 
586         return null;
587     }
588 
589     /**
590      * Given a file and line number, return the range of the first match starting on the
591      * given line. If second is non null, also search for the second string starting at he
592      * location of the first string.
593      */
findRange(IFile file, int line, String first, String second)594     private static IRegion findRange(IFile file, int line, String first,
595             String second) {
596         IRegion region = null;
597         IDocumentProvider provider = new TextFileDocumentProvider();
598         try {
599             provider.connect(file);
600             IDocument document = provider.getDocument(file);
601             if (document != null) {
602                 IRegion lineInfo = document.getLineInformation(line - 1);
603                 int lineStartOffset = lineInfo.getOffset();
604                 // The aapt errors will be anchored on the line where the
605                 // element starts - which means that with formatting where
606                 // attributes end up on subsequent lines we don't find it on
607                 // the error line indicated by aapt.
608                 // Therefore, search forwards in the document.
609                 FindReplaceDocumentAdapter adapter =
610                     new FindReplaceDocumentAdapter(document);
611 
612                 region = adapter.find(lineStartOffset, first,
613                         true /*forwardSearch*/, true /*caseSensitive*/,
614                         false /*wholeWord*/, false /*regExSearch*/);
615                 if (region != null && second != null) {
616                     region = adapter.find(region.getOffset() + first.length(), second,
617                             true /*forwardSearch*/, true /*caseSensitive*/,
618                             false /*wholeWord*/, false /*regExSearch*/);
619                 }
620             }
621         } catch (Exception e) {
622             AdtPlugin.log(e, "Can't find range information for %1$s", file.getName());
623         } finally {
624             provider.disconnect(file);
625         }
626         return region;
627     }
628 
629     /** Returns the non-whitespace line range at the given line number. */
findLineTextRange(IFile file, int line)630     private static IRegion findLineTextRange(IFile file, int line) {
631         IDocumentProvider provider = new TextFileDocumentProvider();
632         try {
633             provider.connect(file);
634             IDocument document = provider.getDocument(file);
635             if (document != null) {
636                 IRegion lineInfo = document.getLineInformation(line - 1);
637                 String lineContents = document.get(lineInfo.getOffset(), lineInfo.getLength());
638                 int lineBegin = 0;
639                 int lineEnd = lineContents.length()-1;
640 
641                 for (; lineEnd >= 0; lineEnd--) {
642                     char c = lineContents.charAt(lineEnd);
643                     if (!Character.isWhitespace(c)) {
644                         break;
645                     }
646                 }
647                 lineEnd++;
648                 for (; lineBegin < lineEnd; lineBegin++) {
649                     char c = lineContents.charAt(lineBegin);
650                     if (!Character.isWhitespace(c)) {
651                         break;
652                     }
653                 }
654                 if (lineBegin < lineEnd) {
655                     return new Region(lineInfo.getOffset() + lineBegin, lineEnd - lineBegin);
656                 }
657             }
658         } catch (Exception e) {
659             AdtPlugin.log(e, "Can't find range information for %1$s", file.getName());
660         } finally {
661             provider.disconnect(file);
662         }
663 
664         return null;
665     }
666 
667     /**
668      * Returns a matching matcher for the next line
669      * @param lines The array of lines
670      * @param nextIndex The index of the next line
671      * @param pattern The pattern to match
672      * @return null if error or no match, the matcher otherwise.
673      */
getNextLineMatcher(String[] lines, int nextIndex, Pattern pattern)674     private static final Matcher getNextLineMatcher(String[] lines,
675             int nextIndex, Pattern pattern) {
676         // unless we can't, because we reached the last line
677         if (nextIndex == lines.length) {
678             // we expected a 2nd line, so we flag as error
679             // and we bail
680             return null;
681         }
682 
683         Matcher m = pattern.matcher(lines[nextIndex]);
684         if (m.matches()) {
685            return m;
686         }
687 
688         return null;
689     }
690 
getResourceFromFullPath(String filename, String root, IProject project)691     private static IResource getResourceFromFullPath(String filename, String root,
692             IProject project) {
693         if (filename.startsWith(root)) {
694             String file = filename.substring(root.length());
695 
696             // get the resource
697             IResource r = project.findMember(file);
698 
699             // if the resource is valid, we add the marker
700             if (r.exists()) {
701                 return r;
702             }
703         }
704 
705         return null;
706     }
707 
708 }
709