diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java | 783 |
1 files changed, 0 insertions, 783 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java deleted file mode 100644 index 1f17fb7c6..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptParser.java +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.build; - -import com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IMarker; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jface.text.FindReplaceDocumentAdapter; -import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.Region; -import org.eclipse.ui.editors.text.TextFileDocumentProvider; -import org.eclipse.ui.texteditor.IDocumentProvider; - -import java.io.File; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public final class AaptParser { - - // TODO: rename the pattern to something that makes sense + javadoc comments. - /** - * Single line aapt warning for skipping files.<br> - * " (skipping hidden file '<file path>'" - */ - private final static Pattern sPattern0Line1 = Pattern.compile( - "^\\s+\\(skipping hidden file\\s'(.*)'\\)$"); //$NON-NLS-1$ - - /** - * First line of dual line aapt error.<br> - * "ERROR at line <line>: <error>"<br> - * " (Occurred while parsing <path>)" - */ - private final static Pattern sPattern1Line1 = Pattern.compile( - "^ERROR\\s+at\\s+line\\s+(\\d+):\\s+(.*)$"); //$NON-NLS-1$ - /** - * Second line of dual line aapt error.<br> - * "ERROR at line <line>: <error>"<br> - * " (Occurred while parsing <path>)"<br> - * @see #sPattern1Line1 - */ - private final static Pattern sPattern1Line2 = Pattern.compile( - "^\\s+\\(Occurred while parsing\\s+(.*)\\)$"); //$NON-NLS-1$ - /** - * First line of dual line aapt error.<br> - * "ERROR: <error>"<br> - * "Defined at file <path> line <line>" - */ - private final static Pattern sPattern2Line1 = Pattern.compile( - "^ERROR:\\s+(.+)$"); //$NON-NLS-1$ - /** - * Second line of dual line aapt error.<br> - * "ERROR: <error>"<br> - * "Defined at file <path> line <line>"<br> - * @see #sPattern2Line1 - */ - private final static Pattern sPattern2Line2 = Pattern.compile( - "Defined\\s+at\\s+file\\s+(.+)\\s+line\\s+(\\d+)"); //$NON-NLS-1$ - /** - * Single line aapt error<br> - * "<path> line <line>: <error>" - */ - private final static Pattern sPattern3Line1 = Pattern.compile( - "^(.+)\\sline\\s(\\d+):\\s(.+)$"); //$NON-NLS-1$ - /** - * First line of dual line aapt error.<br> - * "ERROR parsing XML file <path>"<br> - * "<error> at line <line>" - */ - private final static Pattern sPattern4Line1 = Pattern.compile( - "^Error\\s+parsing\\s+XML\\s+file\\s(.+)$"); //$NON-NLS-1$ - /** - * Second line of dual line aapt error.<br> - * "ERROR parsing XML file <path>"<br> - * "<error> at line <line>"<br> - * @see #sPattern4Line1 - */ - private final static Pattern sPattern4Line2 = Pattern.compile( - "^(.+)\\s+at\\s+line\\s+(\\d+)$"); //$NON-NLS-1$ - - /** - * Single line aapt warning<br> - * "<path>:<line>: <error>" - */ - private final static Pattern sPattern5Line1 = Pattern.compile( - "^(.+?):(\\d+):\\s+WARNING:(.+)$"); //$NON-NLS-1$ - - /** - * Single line aapt error<br> - * "<path>:<line>: <error>" - */ - private final static Pattern sPattern6Line1 = Pattern.compile( - "^(.+?):(\\d+):\\s+(.+)$"); //$NON-NLS-1$ - - /** - * 4 line aapt error<br> - * "ERROR: 9-path image <path> malformed"<br> - * Line 2 and 3 are taken as-is while line 4 is ignored (it repeats with<br> - * 'ERROR: failure processing <path>) - */ - private final static Pattern sPattern7Line1 = Pattern.compile( - "^ERROR:\\s+9-patch\\s+image\\s+(.+)\\s+malformed\\.$"); //$NON-NLS-1$ - - private final static Pattern sPattern8Line1 = Pattern.compile( - "^(invalid resource directory name): (.*)$"); //$NON-NLS-1$ - - /** - * Portion of the error message which states the context in which the error occurred, - * such as which property was being processed and what the string value was that - * caused the error. - * <p> - * Example: - * error: No resource found that matches the given name (at 'text' with value '@string/foo') - */ - private static final Pattern sValueRangePattern = - Pattern.compile("\\(at '(.+)' with value '(.*)'\\)"); //$NON-NLS-1$ - - - /** - * Portion of error message which points to the second occurrence of a repeated resource - * definition. - * <p> - * Example: - * error: Resource entry repeatedStyle1 already has bag item android:gravity. - */ - private static final Pattern sRepeatedRangePattern = - Pattern.compile("Resource entry (.+) already has bag item (.+)\\."); //$NON-NLS-1$ - - /** - * Error message emitted when aapt skips a file because for example it's name is - * invalid, such as a layout file name which starts with _. - * <p> - * This error message is used by AAPT in Tools 19 and earlier. - */ - private static final Pattern sSkippingPattern = - Pattern.compile(" \\(skipping (.+) .+ '(.*)'\\)"); //$NON-NLS-1$ - - /** - * Error message emitted when aapt skips a file because for example it's name is - * invalid, such as a layout file name which starts with _. - * <p> - * This error message is used by AAPT in Tools 20 and later. - */ - private static final Pattern sNewSkippingPattern = - Pattern.compile(" \\(skipping .+ '(.+)' due to ANDROID_AAPT_IGNORE pattern '.+'\\)"); //$NON-NLS-1$ - - /** - * Suffix of error message which points to the first occurrence of a repeated resource - * definition. - * Example: - * Originally defined here. - */ - private static final String ORIGINALLY_DEFINED_MSG = "Originally defined here."; //$NON-NLS-1$ - - /** - * Portion of error message which points to the second occurrence of a repeated resource - * definition. - * <p> - * Example: - * error: Resource entry repeatedStyle1 already has bag item android:gravity. - */ - private static final Pattern sNoResourcePattern = - Pattern.compile("No resource found that matches the given name: attr '(.+)'\\."); //$NON-NLS-1$ - - /** - * Portion of error message which points to a missing required attribute in a - * resource definition. - * <p> - * Example: - * error: error: A 'name' attribute is required for <style> - */ - private static final Pattern sRequiredPattern = - Pattern.compile("A '(.+)' attribute is required for <(.+)>"); //$NON-NLS-1$ - - /** - * 2 line aapt error<br> - * "ERROR: Invalid configuration: foo"<br> - * " ^^^"<br> - * There's no need to parse the 2nd line. - */ - private final static Pattern sPattern9Line1 = Pattern.compile( - "^Invalid configuration: (.+)$"); //$NON-NLS-1$ - - private final static Pattern sXmlBlockPattern = Pattern.compile( - "W/ResourceType\\(.*\\): Bad XML block: no root element node found"); //$NON-NLS-1$ - - /** - * Parse the output of aapt and mark the incorrect file with error markers - * - * @param results the output of aapt - * @param project the project containing the file to mark - * @return true if the parsing failed, false if success. - */ - public static boolean parseOutput(List<String> results, IProject project) { - int size = results.size(); - if (size > 0) { - return parseOutput(results.toArray(new String[size]), project); - } - - return false; - } - - /** - * Parse the output of aapt and mark the incorrect file with error markers - * - * @param results the output of aapt - * @param project the project containing the file to mark - * @return true if the parsing failed, false if success. - */ - public static boolean parseOutput(String[] results, IProject project) { - // nothing to parse? just return false; - if (results.length == 0) { - return false; - } - - // get the root of the project so that we can make IFile from full - // file path - String osRoot = project.getLocation().toOSString(); - - Matcher m; - - for (int i = 0; i < results.length ; i++) { - String p = results[i]; - - m = sPattern0Line1.matcher(p); - if (m.matches()) { - // we ignore those (as this is an ignore message from aapt) - continue; - } - - m = sPattern1Line1.matcher(p); - if (m.matches()) { - String lineStr = m.group(1); - String msg = m.group(2); - - // get the matcher for the next line. - m = getNextLineMatcher(results, ++i, sPattern1Line2); - if (m == null) { - return true; - } - - String location = m.group(1); - - // check the values and attempt to mark the file. - if (checkAndMark(location, lineStr, msg, osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { - return true; - } - continue; - } - - // this needs to be tested before Pattern2 since they both start with 'ERROR:' - m = sPattern7Line1.matcher(p); - if (m.matches()) { - String location = m.group(1); - String msg = p; // default msg is the line in case we don't find anything else - - if (++i < results.length) { - msg = results[i].trim(); - if (++i < results.length) { - msg = msg + " - " + results[i].trim(); //$NON-NLS-1$ - - // skip the next line - i++; - } - } - - // display the error - if (checkAndMark(location, null, msg, osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { - return true; - } - - // success, go to the next line - continue; - } - - m = sPattern2Line1.matcher(p); - if (m.matches()) { - // get the msg - String msg = m.group(1); - - // get the matcher for the next line. - m = getNextLineMatcher(results, ++i, sPattern2Line2); - if (m == null) { - return true; - } - - String location = m.group(1); - String lineStr = m.group(2); - - // check the values and attempt to mark the file. - if (checkAndMark(location, lineStr, msg, osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { - return true; - } - continue; - } - - m = sPattern3Line1.matcher(p); - if (m.matches()) { - String location = m.group(1); - String lineStr = m.group(2); - String msg = m.group(3); - - // check the values and attempt to mark the file. - if (checkAndMark(location, lineStr, msg, osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { - return true; - } - - // success, go to the next line - continue; - } - - m = sPattern4Line1.matcher(p); - if (m.matches()) { - // get the filename. - String location = m.group(1); - - // get the matcher for the next line. - m = getNextLineMatcher(results, ++i, sPattern4Line2); - if (m == null) { - return true; - } - - String msg = m.group(1); - String lineStr = m.group(2); - - // check the values and attempt to mark the file. - if (checkAndMark(location, lineStr, msg, osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { - return true; - } - - // success, go to the next line - continue; - } - - m = sPattern5Line1.matcher(p); - if (m.matches()) { - String location = m.group(1); - String lineStr = m.group(2); - String msg = m.group(3); - - // check the values and attempt to mark the file. - if (checkAndMark(location, lineStr, msg, osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) { - return true; - } - - // success, go to the next line - continue; - } - - m = sPattern6Line1.matcher(p); - if (m.matches()) { - String location = m.group(1); - String lineStr = m.group(2); - String msg = m.group(3); - - // check the values and attempt to mark the file. - if (checkAndMark(location, lineStr, msg, osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { - return true; - } - - // success, go to the next line - continue; - } - - m = sPattern8Line1.matcher(p); - if (m.matches()) { - String location = m.group(2); - String msg = m.group(1); - - // check the values and attempt to mark the file. - if (checkAndMark(location, null, msg, osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) { - return true; - } - - // success, go to the next line - continue; - } - - m = sPattern9Line1.matcher(p); - if (m.matches()) { - String badConfig = m.group(1); - String msg = String.format("APK Configuration filter '%1$s' is invalid", badConfig); - - // skip the next line - i++; - - // check the values and attempt to mark the file. - if (checkAndMark(null /*location*/, null, msg, osRoot, project, - AdtConstants.MARKER_AAPT_PACKAGE, IMarker.SEVERITY_ERROR) == false) { - return true; - } - - // success, go to the next line - continue; - } - - m = sNewSkippingPattern.matcher(p); - if (m.matches()) { - String location = m.group(1); - - if (location.startsWith(".") //$NON-NLS-1$ - || location.endsWith("~")) { //$NON-NLS-1$ - continue; - } - - // check the values and attempt to mark the file. - if (checkAndMark(location, null, p.trim(), osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) { - return true; - } - - // success, go to the next line - continue; - } - - m = sSkippingPattern.matcher(p); - if (m.matches()) { - String location = m.group(2); - - // Certain files can safely be skipped without marking the project - // as having errors. See isHidden() in AaptAssets.cpp: - String type = m.group(1); - if (type.equals("backup") //$NON-NLS-1$ // main.xml~, etc - || type.equals("hidden") //$NON-NLS-1$ // .gitignore, etc - || type.equals("index")) { //$NON-NLS-1$ // thumbs.db, etc - continue; - } - - // check the values and attempt to mark the file. - if (checkAndMark(location, null, p.trim(), osRoot, project, - AdtConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) { - return true; - } - - // success, go to the next line - continue; - } - - m = sXmlBlockPattern.matcher(p); - if (m.matches()) { - // W/ResourceType(12345): Bad XML block: no root element node found - // Sadly there's NO filename reference; this error typically describes the - // error *after* this line. - if (results.length == 1) { - // This is the only error message: dump to console and quit - return true; - } - // Continue: the real culprit is displayed next and should get a marker - continue; - } - - return true; - } - - return false; - } - - /** - * Check if the parameters gotten from the error output are valid, and mark - * the file with an AAPT marker. - * @param location the full OS path of the error file. If null, the project is marked - * @param lineStr - * @param message - * @param root The root directory of the project, in OS specific format. - * @param project - * @param markerId The marker id to put. - * @param severity The severity of the marker to put (IMarker.SEVERITY_*) - * @return true if the parameters were valid and the file was marked successfully. - * - * @see IMarker - */ - private static final boolean checkAndMark(String location, String lineStr, - String message, String root, IProject project, String markerId, int severity) { - // check this is in fact a file - if (location != null) { - File f = new File(location); - if (f.exists() == false) { - return false; - } - } - - // get the line number - int line = -1; // default value for error with no line. - - if (lineStr != null) { - try { - line = Integer.parseInt(lineStr); - } catch (NumberFormatException e) { - // looks like the string we extracted wasn't a valid - // file number. Parsing failed and we return true - return false; - } - } - - // add the marker - IResource f2 = project; - if (location != null) { - f2 = getResourceFromFullPath(location, root, project); - if (f2 == null) { - return false; - } - } - - // Attempt to determine the exact range of characters affected by this error. - // This will look up the actual text of the file, go to the particular error line - // and scan for the specific string mentioned in the error. - int startOffset = -1; - int endOffset = -1; - if (f2 instanceof IFile) { - IRegion region = findRange((IFile) f2, line, message); - if (region != null) { - startOffset = region.getOffset(); - endOffset = startOffset + region.getLength(); - } - } - - // check if there's a similar marker already, since aapt is launched twice - boolean markerAlreadyExists = false; - try { - IMarker[] markers = f2.findMarkers(markerId, true, IResource.DEPTH_ZERO); - - for (IMarker marker : markers) { - if (startOffset != -1) { - int tmpBegin = marker.getAttribute(IMarker.CHAR_START, -1); - if (tmpBegin != startOffset) { - break; - } - int tmpEnd = marker.getAttribute(IMarker.CHAR_END, -1); - if (tmpEnd != startOffset) { - break; - } - } - - int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1); - if (tmpLine != line) { - break; - } - - int tmpSeverity = marker.getAttribute(IMarker.SEVERITY, -1); - if (tmpSeverity != severity) { - break; - } - - String tmpMsg = marker.getAttribute(IMarker.MESSAGE, null); - if (tmpMsg == null || tmpMsg.equals(message) == false) { - break; - } - - // if we're here, all the marker attributes are equals, we found it - // and exit - markerAlreadyExists = true; - break; - } - - } catch (CoreException e) { - // if we couldn't get the markers, then we just mark the file again - // (since markerAlreadyExists is initialized to false, we do nothing) - } - - if (markerAlreadyExists == false) { - BaseProjectHelper.markResource(f2, markerId, message, line, - startOffset, endOffset, severity); - } - - return true; - } - - /** - * Given an aapt error message in a given file and a given (initial) line number, - * return the corresponding offset range for the error, or null. - */ - private static IRegion findRange(IFile file, int line, String message) { - Matcher matcher = sValueRangePattern.matcher(message); - if (matcher.find()) { - String property = matcher.group(1); - String value = matcher.group(2); - - // First find the property. We can't just immediately look for the - // value, because there could be other attributes in this element - // earlier than the one in error, and we might accidentally pick - // up on a different occurrence of the value in a context where - // it is valid. - if (value.length() > 0) { - return findRange(file, line, property, value); - } else { - // Find first occurrence of property followed by '' or "" - IRegion region1 = findRange(file, line, property, "\"\""); //$NON-NLS-1$ - IRegion region2 = findRange(file, line, property, "''"); //$NON-NLS-1$ - if (region1 == null) { - if (region2 == null) { - // Highlight the property instead - return findRange(file, line, property, null); - } - return region2; - } else if (region2 == null) { - return region1; - } else if (region1.getOffset() < region2.getOffset()) { - return region1; - } else { - return region2; - } - } - } - - matcher = sRepeatedRangePattern.matcher(message); - if (matcher.find()) { - String property = matcher.group(2); - return findRange(file, line, property, null); - } - - matcher = sNoResourcePattern.matcher(message); - if (matcher.find()) { - String property = matcher.group(1); - return findRange(file, line, property, null); - } - - matcher = sRequiredPattern.matcher(message); - if (matcher.find()) { - String elementName = matcher.group(2); - IRegion region = findRange(file, line, '<' + elementName, null); - if (region != null && region.getLength() > 1) { - // Skip the opening < - region = new Region(region.getOffset() + 1, region.getLength() - 1); - } - return region; - } - - if (message.endsWith(ORIGINALLY_DEFINED_MSG)) { - return findLineTextRange(file, line); - } - - return null; - } - - /** - * Given a file and line number, return the range of the first match starting on the - * given line. If second is non null, also search for the second string starting at he - * location of the first string. - */ - private static IRegion findRange(IFile file, int line, String first, - String second) { - IRegion region = null; - IDocumentProvider provider = new TextFileDocumentProvider(); - try { - provider.connect(file); - IDocument document = provider.getDocument(file); - if (document != null) { - IRegion lineInfo = document.getLineInformation(line - 1); - int lineStartOffset = lineInfo.getOffset(); - // The aapt errors will be anchored on the line where the - // element starts - which means that with formatting where - // attributes end up on subsequent lines we don't find it on - // the error line indicated by aapt. - // Therefore, search forwards in the document. - FindReplaceDocumentAdapter adapter = - new FindReplaceDocumentAdapter(document); - - region = adapter.find(lineStartOffset, first, - true /*forwardSearch*/, true /*caseSensitive*/, - false /*wholeWord*/, false /*regExSearch*/); - if (region != null && second != null) { - region = adapter.find(region.getOffset() + first.length(), second, - true /*forwardSearch*/, true /*caseSensitive*/, - false /*wholeWord*/, false /*regExSearch*/); - } - } - } catch (Exception e) { - AdtPlugin.log(e, "Can't find range information for %1$s", file.getName()); - } finally { - provider.disconnect(file); - } - return region; - } - - /** Returns the non-whitespace line range at the given line number. */ - private static IRegion findLineTextRange(IFile file, int line) { - IDocumentProvider provider = new TextFileDocumentProvider(); - try { - provider.connect(file); - IDocument document = provider.getDocument(file); - if (document != null) { - IRegion lineInfo = document.getLineInformation(line - 1); - String lineContents = document.get(lineInfo.getOffset(), lineInfo.getLength()); - int lineBegin = 0; - int lineEnd = lineContents.length()-1; - - for (; lineEnd >= 0; lineEnd--) { - char c = lineContents.charAt(lineEnd); - if (!Character.isWhitespace(c)) { - break; - } - } - lineEnd++; - for (; lineBegin < lineEnd; lineBegin++) { - char c = lineContents.charAt(lineBegin); - if (!Character.isWhitespace(c)) { - break; - } - } - if (lineBegin < lineEnd) { - return new Region(lineInfo.getOffset() + lineBegin, lineEnd - lineBegin); - } - } - } catch (Exception e) { - AdtPlugin.log(e, "Can't find range information for %1$s", file.getName()); - } finally { - provider.disconnect(file); - } - - return null; - } - - /** - * Returns a matching matcher for the next line - * @param lines The array of lines - * @param nextIndex The index of the next line - * @param pattern The pattern to match - * @return null if error or no match, the matcher otherwise. - */ - private static final Matcher getNextLineMatcher(String[] lines, - int nextIndex, Pattern pattern) { - // unless we can't, because we reached the last line - if (nextIndex == lines.length) { - // we expected a 2nd line, so we flag as error - // and we bail - return null; - } - - Matcher m = pattern.matcher(lines[nextIndex]); - if (m.matches()) { - return m; - } - - return null; - } - - private static IResource getResourceFromFullPath(String filename, String root, - IProject project) { - if (filename.startsWith(root)) { - String file = filename.substring(root.length()); - - // get the resource - IResource r = project.findMember(file); - - // if the resource is valid, we add the marker - if (r != null && r.exists()) { - return r; - } - } - - return null; - } - -} |