summaryrefslogtreecommitdiff
path: root/AccessCheck/src/com/android/accessibility/AccessibilityValidator.java
blob: 5f3b031fc42520825862c28d1fa3bb7e67de0340 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
 * 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;
    }
}