110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright/*
210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright * Copyright (C) 2012 The Android Open Source Project
310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright *
410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright * Licensed under the Eclipse Public License, Version 1.0 (the "License");
510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright * you may not use this file except in compliance with the License.
610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright * You may obtain a copy of the License at
710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright *
810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright *      http://www.eclipse.org/org/documents/epl-v10.php
910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright *
1010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright * Unless required by applicable law or agreed to in writing, software
1110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright * distributed under the License is distributed on an "AS IS" BASIS,
1210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright * See the License for the specific language governing permissions and
1410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright * limitations under the License.
1510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright */
1610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
1710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightpackage com.android.dvlib;
1810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
1910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport java.io.File;
2010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport java.io.IOException;
2110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport java.io.InputStream;
2210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport java.io.OutputStream;
2310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport java.io.PrintWriter;
2410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
2510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport javax.xml.XMLConstants;
2610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport javax.xml.parsers.ParserConfigurationException;
2710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport javax.xml.parsers.SAXParser;
2810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport javax.xml.parsers.SAXParserFactory;
2910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport javax.xml.transform.stream.StreamSource;
3010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport javax.xml.validation.Schema;
3110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport javax.xml.validation.SchemaFactory;
3210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport javax.xml.validation.Validator;
3310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
3410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport org.xml.sax.Attributes;
3510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport org.xml.sax.SAXException;
3610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport org.xml.sax.SAXParseException;
3710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightimport org.xml.sax.helpers.DefaultHandler;
3810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
3910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wrightpublic class DeviceSchema {
4010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
4110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NS_DEVICES_XSD = "http://schemas.android.com/sdk/devices/1";
4210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
4310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    /**
4410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * The "devices" element is the root element of this schema.
4510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     *
4610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * It must contain one or more "device" elements that each define the
4710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * hardware, software, and states for a given device.
4810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     */
4910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_DEVICES = "devices";
5010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
5110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    /**
5210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * A "device" element contains a "hardware" element, a "software" element
5310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * for each API version it supports, and a "state" element for each possible
5410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * state the device could be in.
5510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     */
5610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_DEVICE = "device";
5710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
5810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    /**
5910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * The "hardware" element contains all of the hardware information for a
6010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * given device.
6110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     */
6210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_HARDWARE = "hardware";
6310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
6410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    /**
6510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * The "software" element contains all of the software information for an
6610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * API version of the given device.
6710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     */
6810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_SOFTWARE = "software";
6910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
7010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    /**
7110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * The "state" element contains all of the parameters for a given state of
7210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * the device. It's also capable of redefining hardware configurations if
7310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * they change based on state.
7410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     */
7510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_STATE = "state";
7610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
7710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_KEYBOARD = "keyboard";
7810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
7910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_TOUCH = "touch";
8010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
8110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_GL_EXTENSIONS = "gl-extensions";
8210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
8310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_GL_VERSION = "gl-version";
8410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
8510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_NETWORKING = "networking";
8610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
8710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_REMOVABLE_STORAGE = "removable-storage";
8810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
8910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_FLASH = "flash";
9010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
9110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_LIVE_WALLPAPER_SUPPORT = "live-wallpaper-support";
9210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
93128d49cc721b1b7bbbfa3add74d0d7c3a9726d72Michael Wright    public static final String NODE_STATUS_BAR = "status-bar";
94128d49cc721b1b7bbbfa3add74d0d7c3a9726d72Michael Wright
9510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_BUTTONS = "buttons";
9610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
9710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_CAMERA = "camera";
9810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
9910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_LOCATION = "location";
10010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
10110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_GPU = "gpu";
10210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
10310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_DOCK = "dock";
10410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
10510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_YDPI = "ydpi";
10610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
1076ebc2c45b01705c2619f11d3bd308ffe51f69758Michael Wright    public static final String NODE_POWER_TYPE= "power-type";
10810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
10910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_Y_DIMENSION = "y-dimension";
11010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
11110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_SCREEN_RATIO = "screen-ratio";
11210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
11310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_NAV_STATE = "nav-state";
11410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
1156ebc2c45b01705c2619f11d3bd308ffe51f69758Michael Wright    public static final String NODE_MIC = "mic";
11610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
11710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_RAM = "ram";
11810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
11910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_XDPI = "xdpi";
12010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
12110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_DIMENSIONS = "dimensions";
12210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
12310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_ABI = "abi";
12410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
12510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_MECHANISM = "mechanism";
12610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
12710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_MULTITOUCH = "multitouch";
12810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
12910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_NAV = "nav";
13010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
13110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_PIXEL_DENSITY = "pixel-density";
13210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
13310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_SCREEN_ORIENTATION = "screen-orientation";
13410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
13510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_AUTOFOCUS = "autofocus";
13610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
13710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_SCREEN_SIZE = "screen-size";
13810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
13910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_DESCRIPTION = "description";
14010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
14110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_BLUETOOTH_PROFILES = "bluetooth-profiles";
14210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
14310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_SCREEN = "screen";
14410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
14510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_SENSORS = "sensors";
14610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
14710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_DIAGONAL_LENGTH = "diagonal-length";
14810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
14910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_SCREEN_TYPE = "screen-type";
15010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
15110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_KEYBOARD_STATE = "keyboard-state";
15210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
15310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_X_DIMENSION = "x-dimension";
15410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
15510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_CPU = "cpu";
15610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
15710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_INTERNAL_STORAGE = "internal-storage";
15810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
15910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_META = "meta";
16010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
16110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_ICONS = "icons";
16210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
16310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_SIXTY_FOUR = "sixty-four";
16410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
16510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_SIXTEEN = "sixteen";
16610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
16710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_FRAME = "frame";
16810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
16910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_PATH = "path";
17010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
17110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_PORTRAIT_X_OFFSET = "portrait-x-offset";
17210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
17310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_PORTRAIT_Y_OFFSET = "portrait-y-offset";
17410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
17510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_LANDSCAPE_X_OFFSET = "landscape-x-offset";
17610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
17710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_LANDSCAPE_Y_OFFSET = "landscape-y-offset";
17810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
17910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_NAME = "name";
18010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
18110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_API_LEVEL = "api-level";
18210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
18310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String NODE_MANUFACTURER = "manufacturer";
18410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
18510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String ATTR_DEFAULT = "default";
18610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
18710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static final String ATTR_UNIT = "unit";
18810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
1896ebc2c45b01705c2619f11d3bd308ffe51f69758Michael Wright    public static final String ATTR_NAME = "name";
1906ebc2c45b01705c2619f11d3bd308ffe51f69758Michael Wright
19110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    /**
19210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * Validates the input stream.
19310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     *
19410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * @param deviceXml
19510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     *            The XML InputStream to validate.
19610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * @param out
19710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     *            The OutputStream for error messages.
19810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * @param parent
19910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     *            The parent directory of the input stream.
20010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * @return Whether the given input constitutes a valid devices file.
20110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     */
20210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static boolean validate(InputStream deviceXml, OutputStream out, File parent) {
20310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        Schema s;
20410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        SAXParserFactory factory = SAXParserFactory.newInstance();
20510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        PrintWriter writer = new PrintWriter(out);
20610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        try {
20710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            s = DeviceSchema.getSchema();
20810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            factory.setValidating(false);
20910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            factory.setNamespaceAware(true);
21010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            factory.setSchema(s);
21110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            ValidationHandler validator = new ValidationHandler(parent, writer);
21210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            SAXParser parser = factory.newSAXParser();
21310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            parser.parse(deviceXml, validator);
21410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            return validator.isValidDevicesFile();
21510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        } catch (SAXException e) {
21610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            writer.println(e.getMessage());
21710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            return false;
21810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        } catch (ParserConfigurationException e) {
21910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            writer.println("Error creating SAX parser:");
22010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            writer.println(e.getMessage());
22110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            return false;
22210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        } catch (IOException e) {
22310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            writer.println("Error reading file stream:");
22410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            writer.println(e.getMessage());
22510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            return false;
22610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        } finally {
22710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            writer.flush();
22810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        }
22910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    }
23010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
23110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    /**
23210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * Helper to get an input stream of the device config XML schema.
23310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     */
23410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static InputStream getXsdStream() {
23510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        return DeviceSchema.class.getResourceAsStream("devices.xsd"); //$NON-NLS-1$
23610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    }
23710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
23810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    /** Helper method that returns a {@link Validator} for our XSD */
23910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    public static Schema getSchema() throws SAXException {
24010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        InputStream xsdStream = getXsdStream();
24110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
24210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        Schema schema = factory.newSchema(new StreamSource(xsdStream));
24310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        return schema;
24410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    }
24510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
24610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    /**
24710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * A DefaultHandler that parses only to validate the XML is actually a valid
24810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * devices config, since validation can't be entirely encoded in the devices
24910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     * schema.
25010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright     */
25110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    private static class ValidationHandler extends DefaultHandler {
25210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        private boolean mValidDevicesFile = true;
25310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        private boolean mDefaultSeen = false;
25410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        private String mDeviceName;
25510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        private final File mDirectory;
25610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        private final PrintWriter mWriter;
25710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        private final StringBuilder mStringAccumulator = new StringBuilder();
25810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
25910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        public ValidationHandler(File directory, PrintWriter writer) {
26010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            mDirectory = directory; // Possibly null
26110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            mWriter = writer;
26210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        }
26310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
26410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        @Override
26510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        public void startElement(String uri, String localName, String name, Attributes attributes)
26610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                throws SAXException {
26710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            if (NODE_DEVICE.equals(localName)) {
26810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                // Reset for a new device
26910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                mDefaultSeen = false;
27010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            } else if (NODE_STATE.equals(localName)) {
27110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                // Check if the state is set to be a default state
27210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                String val = attributes.getValue(ATTR_DEFAULT);
27310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                if (val != null && ("1".equals(val) || Boolean.parseBoolean(val))) {
27410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    /*
27510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                     * If it is and we already have a default state for this
27610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                     * device, then the device configuration is invalid.
27710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                     * Otherwise, set that we've seen a default state for this
27810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                     * device and continue
27910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                     */
28010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
28110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    if (mDefaultSeen) {
28210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                        validationError("More than one default state for device " + mDeviceName);
28310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    } else {
28410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                        mDefaultSeen = true;
28510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    }
28610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                }
28710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            }
28810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            mStringAccumulator.setLength(0);
28910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        }
29010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
29110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        @Override
29210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        public void characters(char[] ch, int start, int length) {
29310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            mStringAccumulator.append(ch, start, length);
29410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        }
29510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
29610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        @Override
29710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        public void endElement(String uri, String localName, String name) throws SAXException {
29810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            // If this is the end of a device node, make sure we have at least
29910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            // one default state
30010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            if (NODE_DEVICE.equals(localName) && !mDefaultSeen) {
30110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                validationError("No default state for device " + mDeviceName);
30210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            } else if (NODE_NAME.equals(localName)) {
30310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                mDeviceName = mStringAccumulator.toString().trim();
30410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            } else if (NODE_PATH.equals(localName) || NODE_SIXTY_FOUR.equals(localName)
30510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    || NODE_SIXTEEN.equals(localName)) {
30610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                if (mDirectory == null) {
30710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    // There is no given parent directory, so this is not a
30810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    // valid devices file
30910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    validationError("No parent directory given, but relative paths exist.");
31010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    return;
31110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                }
31210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                // This is going to break on any files that end with a space,
31310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                // but that should be an incredibly rare corner case.
31410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                String relativePath = mStringAccumulator.toString().trim();
31510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                File f = new File(mDirectory, relativePath);
31610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                if (f == null || !f.isFile()) {
31710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    validationError(relativePath + " is not a valid path.");
31810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    return;
31910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                }
32010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                String fileName = f.getName();
32110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                int extensionStart = fileName.lastIndexOf(".");
32210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                if (extensionStart == -1 || !fileName.substring(extensionStart + 1).equals("png")) {
32310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                    validationError(relativePath + " is not a valid file type.");
32410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright                }
32510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            }
32610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        }
32710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
32810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        @Override
32910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        public void error(SAXParseException e) {
33010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            validationError(e.getMessage());
33110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        }
33210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
33310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        @Override
33410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        public void fatalError(SAXParseException e) {
33510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            validationError(e.getMessage());
33610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        }
33710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
33810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        public boolean isValidDevicesFile() {
33910ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            return mValidDevicesFile;
34010ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        }
34110ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
34210ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        private void validationError(String reason) {
34310ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            mWriter.println("Error: " + reason);
34410ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright            mValidDevicesFile = false;
34510ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright        }
34610ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright
34710ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright    }
34810ca09dea02aa82b2b3d5b750178b724e3454129Michael Wright}
349