| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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.common.resources; |
| |
| import com.android.annotations.NonNull; |
| import com.google.common.io.Closeables; |
| |
| import org.kxml2.io.KXmlParser; |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import java.io.BufferedInputStream; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| /** |
| * Parser for scanning an XML resource file and validating all framework |
| * attribute references in it. If an error is found, the associated context |
| * is marked as needing a full AAPT run. |
| */ |
| public class ValidatingResourceParser { |
| private final boolean mIsFramework; |
| private ScanningContext mContext; |
| |
| /** |
| * Creates a new {@link ValidatingResourceParser} |
| * |
| * @param context a context object with state for the current update, such |
| * as a place to stash errors encountered |
| * @param isFramework true if scanning a framework resource |
| */ |
| public ValidatingResourceParser( |
| @NonNull ScanningContext context, |
| boolean isFramework) { |
| mContext = context; |
| mIsFramework = isFramework; |
| } |
| |
| /** |
| * Parse the given input and return false if it contains errors, <b>or</b> if |
| * the context is already tagged as needing a full aapt run. |
| * |
| * @param path the full OS path to the file being parsed |
| * @param input the input stream of the XML to be parsed (will be closed by this method) |
| * @return true if parsing succeeds and false if it fails |
| * @throws IOException if reading the contents fails |
| */ |
| public boolean parse(final String path, InputStream input) |
| throws IOException { |
| // No need to validate framework files |
| if (mIsFramework) { |
| Closeables.closeQuietly(input); |
| return true; |
| } |
| if (mContext.needsFullAapt()) { |
| Closeables.closeQuietly(input); |
| return false; |
| } |
| |
| KXmlParser parser = new KXmlParser(); |
| try { |
| parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); |
| |
| if (input instanceof FileInputStream) { |
| input = new BufferedInputStream(input); |
| } |
| parser.setInput(input, "UTF-8"); //$NON-NLS-1$ |
| |
| return parse(path, parser); |
| } catch (XmlPullParserException e) { |
| String message = e.getMessage(); |
| |
| // Strip off position description |
| int index = message.indexOf("(position:"); //$NON-NLS-1$ (Hardcoded in KXml) |
| if (index != -1) { |
| message = message.substring(0, index); |
| } |
| |
| String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ |
| path, parser.getLineNumber(), message); |
| mContext.addError(error); |
| return false; |
| } catch (RuntimeException e) { |
| // Some exceptions are thrown by the KXmlParser that are not XmlPullParserExceptions, |
| // such as this one: |
| // java.lang.RuntimeException: Undefined Prefix: w in org.kxml2.io.KXmlParser@... |
| // at org.kxml2.io.KXmlParser.adjustNsp(Unknown Source) |
| // at org.kxml2.io.KXmlParser.parseStartTag(Unknown Source) |
| String message = e.getMessage(); |
| String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ |
| path, parser.getLineNumber(), message); |
| mContext.addError(error); |
| return false; |
| } finally { |
| Closeables.closeQuietly(input); |
| } |
| } |
| |
| private boolean parse(String path, KXmlParser parser) |
| throws XmlPullParserException, IOException { |
| boolean checkForErrors = !mIsFramework && !mContext.needsFullAapt(); |
| |
| while (true) { |
| int event = parser.next(); |
| if (event == XmlPullParser.START_TAG) { |
| for (int i = 0, n = parser.getAttributeCount(); i < n; i++) { |
| String attribute = parser.getAttributeName(i); |
| String value = parser.getAttributeValue(i); |
| assert value != null : attribute; |
| |
| if (checkForErrors) { |
| String uri = parser.getAttributeNamespace(i); |
| if (!mContext.checkValue(uri, attribute, value)) { |
| mContext.requestFullAapt(); |
| return false; |
| } |
| } |
| } |
| } else if (event == XmlPullParser.END_DOCUMENT) { |
| break; |
| } |
| } |
| |
| return true; |
| } |
| } |