/* * 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 mGeneralErrors = new ArrayList(); /** A list of files we wish to have tested. */ private List 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 * 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 "); 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 findLayoutFiles(File directory) { List layoutFiles = new ArrayList(); for (File file : directory.listFiles()) { // The file is a directory; recurse on the file. if (file.isDirectory()) { List 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 getGeneralErrors() { return mGeneralErrors; } /** * Sets the files to be tested. */ public void setLayoutFiles(List layoutFiles) { this.mLayoutFiles = layoutFiles; } /** * Gets the files to be tested. */ public List getLayoutFiles() { return mLayoutFiles; } /** * Gets the total number of test validation errors across all files. */ public int getTotalValidationErrors() { return mTotalValidationErrors; } }