Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright 2010 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.accessibility;
     18 
     19 import org.xml.sax.InputSource;
     20 import org.xml.sax.SAXException;
     21 import org.xml.sax.XMLReader;
     22 import org.xml.sax.helpers.XMLReaderFactory;
     23 
     24 import java.io.*;
     25 import java.util.ArrayList;
     26 import java.util.List;
     27 import java.util.logging.Logger;
     28 
     29 /**
     30  * An object that fetches all Android layout files and manages the testing of
     31  * the files with the use of the AccessibilityValidationContentHandler. This
     32  * object also reports on any errors encountered during the testing.
     33  *
     34  * @author dtseng (at) google.com (David Tseng)
     35  */
     36 public class AccessibilityValidator {
     37     /** The root path to scan for Android layout files. */
     38     private final File mRootFilePath;
     39     /** Errors generated by thrown exceptions (and not by validation errors). */
     40     private final List<String> mGeneralErrors = new ArrayList<String>();
     41     /** A list of files we wish to have tested. */
     42     private List<InputSource> mLayoutFiles;
     43     /** The total number of validation test errors across all files. */
     44     private int mTotalValidationErrors = 0;
     45     /** The path to the Android sdk jar. */
     46     private final File mAndroidSdkPath;
     47 
     48     /** The object that handles our logging. */
     49     private static final Logger sLogger = Logger.getLogger("android.accessibility");
     50 
     51     /**
     52      * The entry point to this tool.
     53      *
     54      * @param 
     55      *            path on which to search for layout xml files that need
     56      *            validation
     57      */
     58     public static void main(String[] args) {
     59         sLogger.info("AccessibilityValidator");
     60         if (args.length == 2) {
     61             sLogger.info("Validating classes using android jar for subclasses of ImageView");
     62             new AccessibilityValidator(args[0], args[1]).run();
     63         } else {
     64             sLogger.info("Usage: java AccessibilityValidator <path> <Android jar path>");
     65             return;
     66         }
     67     }
     68 
     69     /**
     70      * Constructs an AccessibilityValidator object using the root path and the
     71      * android jar path.
     72      */
     73     public AccessibilityValidator(String rootPath, String androidSdkPath) {
     74         mRootFilePath = new File(rootPath);
     75         mAndroidSdkPath = new File(androidSdkPath);
     76 
     77         if (!mRootFilePath.exists()) {
     78             throw new IllegalArgumentException("Invalid root path specified "
     79                     + rootPath);
     80         } else if (!mAndroidSdkPath.exists()) {
     81             throw new IllegalArgumentException(
     82                     "Invalid android sdk path specified " + androidSdkPath);
     83         }
     84     }
     85 
     86     /**
     87      * Performs validation of Android layout files and logs errors that have
     88      * been encountered during the validation. Returns true if the validation
     89      * passes.
     90      */
     91     private boolean run() {
     92         sLogger.info("Validating files under " + mRootFilePath);
     93         mLayoutFiles = findLayoutFiles(mRootFilePath);
     94         validateFiles();
     95         for (String error : mGeneralErrors) {
     96             sLogger.info(error);
     97         }
     98         sLogger.info("done with validation");
     99         return mGeneralErrors.size() == 0;
    100     }
    101 
    102     /**
    103      * Accumulates a list of files under the path that meet two constraints.
    104      * Firstly, it has a containing (parent) directory of "layout". Secondly, it
    105      * has an xml extension
    106      */
    107     private List<InputSource> findLayoutFiles(File directory) {
    108         List<InputSource> layoutFiles = new ArrayList<InputSource>();
    109 
    110         for (File file : directory.listFiles()) {
    111             // The file is a directory; recurse on the file.
    112             if (file.isDirectory()) {
    113                 List<InputSource> directoryFiles = findLayoutFiles(file);
    114                 layoutFiles.addAll(directoryFiles);
    115                 // Does the containing directory and filename meet our
    116                 // constraints?
    117             } else if (directory.getName().toLowerCase().contains("layout")
    118                     && file.getName().toLowerCase().endsWith(".xml")) {
    119                 InputSource addition;
    120                 try {
    121                     addition = new InputSource(new FileReader(file));
    122                     // Store this explicitly for logging.
    123                     addition.setPublicId(file.toString());
    124                     layoutFiles.add(addition);
    125                 } catch (FileNotFoundException fileNotFoundException) {
    126                     mGeneralErrors.add("File not found "
    127                             + fileNotFoundException);
    128                 }
    129             }
    130         }
    131 
    132         return layoutFiles;
    133     }
    134 
    135     /*
    136      * Processes a list of files via an AccessibilityValidationContentHandler.
    137      * The caller will only be notified of errors via logging.
    138      */
    139     public void validateFiles() {
    140         sLogger.info("Validating " + getLayoutFiles().size());
    141         XMLReader reader;
    142         try {
    143             reader = XMLReaderFactory.createXMLReader();
    144         } catch (SAXException saxExp) {
    145             mGeneralErrors.add("Error " + saxExp);
    146             return;
    147         }
    148         for (InputSource file : getLayoutFiles()) {
    149             try {
    150                 AccessibilityValidationContentHandler contentHandler
    151                 = new AccessibilityValidationContentHandler(
    152                         file.getPublicId(), mAndroidSdkPath);
    153                 reader.setContentHandler(contentHandler);
    154                 reader.parse(file);
    155                 mTotalValidationErrors += contentHandler.getValidationErrors();
    156             } catch (IOException ioExp) {
    157                 mGeneralErrors.add("Error reading file " + ioExp);
    158             } catch (SAXException saxExp) {
    159                 mGeneralErrors.add("Error " + saxExp);
    160             }
    161         }
    162     }
    163 
    164     /**
    165      * Returns the number of general errors (considered caught exceptions).
    166      */
    167     public List<String> getGeneralErrors() {
    168         return mGeneralErrors;
    169     }
    170 
    171     /**
    172      * Sets the files to be tested.
    173      */
    174     public void setLayoutFiles(List<InputSource> layoutFiles) {
    175         this.mLayoutFiles = layoutFiles;
    176     }
    177 
    178     /**
    179      * Gets the files to be tested.
    180      */
    181     public List<InputSource> getLayoutFiles() {
    182         return mLayoutFiles;
    183     }
    184 
    185     /**
    186      * Gets the total number of test validation errors across all files.
    187      */
    188     public int getTotalValidationErrors() {
    189         return mTotalValidationErrors;
    190     }
    191 }
    192