blob: 5f3b031fc42520825862c28d1fa3bb7e67de0340 [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* 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.accessibility;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* An object that fetches all Android layout files and manages the testing of
* the files with the use of the AccessibilityValidationContentHandler. This
* object also reports on any errors encountered during the testing.
*
* @author dtseng@google.com (David Tseng)
*/
public class AccessibilityValidator {
/** The root path to scan for Android layout files. */
private final File mRootFilePath;
/** Errors generated by thrown exceptions (and not by validation errors). */
private final List<String> mGeneralErrors = new ArrayList<String>();
/** A list of files we wish to have tested. */
private List<InputSource> mLayoutFiles;
/** The total number of validation test errors across all files. */
private int mTotalValidationErrors = 0;
/** The path to the Android sdk jar. */
private final File mAndroidSdkPath;
/** The object that handles our logging. */
private static final Logger sLogger = Logger.getLogger("android.accessibility");
/**
* The entry point to this tool.
*
* @param <args>
* path on which to search for layout xml files that need
* validation
*/
public static void main(String[] args) {
sLogger.info("AccessibilityValidator");
if (args.length == 2) {
sLogger.info("Validating classes using android jar for subclasses of ImageView");
new AccessibilityValidator(args[0], args[1]).run();
} else {
sLogger.info("Usage: java AccessibilityValidator <path> <Android jar path>");
return;
}
}
/**
* Constructs an AccessibilityValidator object using the root path and the
* android jar path.
*/
public AccessibilityValidator(String rootPath, String androidSdkPath) {
mRootFilePath = new File(rootPath);
mAndroidSdkPath = new File(androidSdkPath);
if (!mRootFilePath.exists()) {
throw new IllegalArgumentException("Invalid root path specified "
+ rootPath);
} else if (!mAndroidSdkPath.exists()) {
throw new IllegalArgumentException(
"Invalid android sdk path specified " + androidSdkPath);
}
}
/**
* Performs validation of Android layout files and logs errors that have
* been encountered during the validation. Returns true if the validation
* passes.
*/
private boolean run() {
sLogger.info("Validating files under " + mRootFilePath);
mLayoutFiles = findLayoutFiles(mRootFilePath);
validateFiles();
for (String error : mGeneralErrors) {
sLogger.info(error);
}
sLogger.info("done with validation");
return mGeneralErrors.size() == 0;
}
/**
* Accumulates a list of files under the path that meet two constraints.
* Firstly, it has a containing (parent) directory of "layout". Secondly, it
* has an xml extension
*/
private List<InputSource> findLayoutFiles(File directory) {
List<InputSource> layoutFiles = new ArrayList<InputSource>();
for (File file : directory.listFiles()) {
// The file is a directory; recurse on the file.
if (file.isDirectory()) {
List<InputSource> directoryFiles = findLayoutFiles(file);
layoutFiles.addAll(directoryFiles);
// Does the containing directory and filename meet our
// constraints?
} else if (directory.getName().toLowerCase().contains("layout")
&& file.getName().toLowerCase().endsWith(".xml")) {
InputSource addition;
try {
addition = new InputSource(new FileReader(file));
// Store this explicitly for logging.
addition.setPublicId(file.toString());
layoutFiles.add(addition);
} catch (FileNotFoundException fileNotFoundException) {
mGeneralErrors.add("File not found "
+ fileNotFoundException);
}
}
}
return layoutFiles;
}
/*
* Processes a list of files via an AccessibilityValidationContentHandler.
* The caller will only be notified of errors via logging.
*/
public void validateFiles() {
sLogger.info("Validating " + getLayoutFiles().size());
XMLReader reader;
try {
reader = XMLReaderFactory.createXMLReader();
} catch (SAXException saxExp) {
mGeneralErrors.add("Error " + saxExp);
return;
}
for (InputSource file : getLayoutFiles()) {
try {
AccessibilityValidationContentHandler contentHandler
= new AccessibilityValidationContentHandler(
file.getPublicId(), mAndroidSdkPath);
reader.setContentHandler(contentHandler);
reader.parse(file);
mTotalValidationErrors += contentHandler.getValidationErrors();
} catch (IOException ioExp) {
mGeneralErrors.add("Error reading file " + ioExp);
} catch (SAXException saxExp) {
mGeneralErrors.add("Error " + saxExp);
}
}
}
/**
* Returns the number of general errors (considered caught exceptions).
*/
public List<String> getGeneralErrors() {
return mGeneralErrors;
}
/**
* Sets the files to be tested.
*/
public void setLayoutFiles(List<InputSource> layoutFiles) {
this.mLayoutFiles = layoutFiles;
}
/**
* Gets the files to be tested.
*/
public List<InputSource> getLayoutFiles() {
return mLayoutFiles;
}
/**
* Gets the total number of test validation errors across all files.
*/
public int getTotalValidationErrors() {
return mTotalValidationErrors;
}
}