XmlValidator tool

This tool can be used to validate a set of xml files against w3c
schemas.

It scans a directory recursively and checks for .xml files in it.
For each file, it extracts the current schema file based on a convention
(the corresponding schema file must have the same name as the root node)
which is used in the parameter-framework.

Change-Id: I27e32028e39a450ed18f86cb095348f73d59ade5
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ffe6955..4bde584 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,3 +19,4 @@
 add_subdirectory(test/test-platform)
 
 add_subdirectory(tools/xmlGenerator)
+add_subdirectory(tools/xmlValidator)
diff --git a/tools/xmlValidator/CMakeLists.txt b/tools/xmlValidator/CMakeLists.txt
new file mode 100644
index 0000000..78519f4
--- /dev/null
+++ b/tools/xmlValidator/CMakeLists.txt
@@ -0,0 +1 @@
+INSTALL(PROGRAMS xmlValidator.py DESTINATION bin)
diff --git a/tools/xmlValidator/README.md b/tools/xmlValidator/README.md
new file mode 100644
index 0000000..adc7e12
--- /dev/null
+++ b/tools/xmlValidator/README.md
@@ -0,0 +1,73 @@
+# xmlValidator tool
+
+This tool can be used to check if the `.xml` files you have written are
+following the `.xsd` schemas we provided you.
+By doing so, you are *ensured* that your configuration is *fully compatible* with the `parameter-framework`.
+
+It scans all directories and subdirectories for `.xml` files and checks them
+with `.xsd` from a *schemas* directory you specified for the script.
+
+## Usage
+To run xmlValidator, just start it from the commandline with:
+
+    python xmlValidator.py <xmlRootDirectory> <xsdDirectory>
+
+where:
+
+* `<xmlRootDirectory>` is a path to a directory containing:
+    - `.xml` files
+    - subdirectories containing `.xml` files
+* `<xsdDirectory>` is a path to a directory containing:
+    - `.xsd` files (also called *schemas*)
+
+## Example of usage
+
+### File structure
+
+In the example, we have the following files:
+
+    ├── ParameterFrameworkConfiguration.xml
+    ├── Schemas
+    │   ├── ComponentLibrary.xsd
+    │   ├── ComponentTypeSet.xsd
+    │   ├── ConfigurableDomains.xsd
+    │   ├── FileIncluder.xsd
+    │   ├── ParameterFrameworkConfiguration.xsd
+    │   ├── ParameterSettings.xsd
+    │   ├── Parameter.xsd
+    │   ├── Subsystem.xsd
+    │   └── SystemClass.xsd
+    ├── Settings
+    │   └── FS
+    │       └── Genres.xml
+    └── Structure
+        └── FS
+            ├── MusicLibraries.xml
+            └── my_music.xml
+
+### Command
+We are in the directory which contains the structure detailed previously.
+To check the validity, we just run:
+
+    ../../tools/xmlValidator/xmlValidator.py . Schemas
+
+### Results
+And we will get the following output on the commandline:
+
+    [*] Validate xml files in /home/lab/MusicLibrary/ with /home/lab/MusicLibrary/Schemas
+    Attempt to validate ParameterFrameworkConfiguration.xml with ParameterFrameworkConfiguration.xsd
+    ParameterFrameworkConfiguration.xml is valid
+    Attempt to validate my_music.xml with Subsystem.xsd
+    my_music.xml is valid
+    Attempt to validate MusicLibraries.xml with SystemClass.xsd
+    MusicLibraries.xml is valid
+    Attempt to validate Genres.xml with ConfigurableDomains.xsd
+    Genres.xml is valid
+
+
+## Install requirements
+In order to use this tool, you must have the following packages installed:
+
+* `python`
+* `python-lxml`
+* `libpython2.7`
diff --git a/tools/xmlValidator/xmlValidator.py b/tools/xmlValidator/xmlValidator.py
new file mode 100755
index 0000000..ccf4bd0
--- /dev/null
+++ b/tools/xmlValidator/xmlValidator.py
@@ -0,0 +1,109 @@
+#! /usr/bin/python
+
+# Copyright (c) 2014, Intel Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation and/or
+# other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors
+# may be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from lxml import etree
+from os import path
+from os import walk
+from sys import argv
+
+class PrintColor():
+    @staticmethod
+    def success(stringToPrint):
+        green=32
+        PrintColor._printColor(green, stringToPrint)
+
+    @staticmethod
+    def error(stringToPrint):
+        red=31
+        PrintColor._printColor(red, stringToPrint)
+
+    @staticmethod
+    def _printColor(color, stringToPrint):
+        """prints strings in color via ascii escape sequence"""
+        print("\033[%sm%s\033[0m" % (str(color), stringToPrint))
+
+def getSchemaFilenameFromXmlFile(xmlFilePath):
+    """getSchemaFileNameFromXmlFile
+
+    The pfw considers that the .xsd file has the same name as the
+    root element name of the .xml.
+    With of this knowledge, we may easily find the
+    schema file we need.
+
+    Args:
+        xmlFilePath: the xml file.
+
+    Returns:
+        str: the corresponding .schema name
+    """
+    xmlTree = etree.parse(xmlFilePath)
+    rootElement = xmlTree.getroot()
+    return rootElement.tag + '.xsd'
+
+def validateXmlWithSchema(xmlFilePath, schemaFilePath):
+    """validateXmlWithSchema
+
+    Validates an .xml file based on his corresponding schema.
+
+    Args:
+        xmlFilePath (str): the absolute path to the xml file.
+        schemaFilePath (str): the absolute path to the schema.
+    """
+    baseXmlName = path.basename(xmlFilePath)
+    baseSchemaName = path.basename(schemaFilePath)
+    print 'Attempt to validate', baseXmlName, 'with', baseSchemaName
+
+    schemaContent = etree.parse(schemaFilePath)
+    schema = etree.XMLSchema(schemaContent)
+    xmlContent = etree.parse(xmlFilePath)
+    xmlContent.xinclude()
+
+    if schema.validate(xmlContent):
+        PrintColor.success('%s is valid' % str(baseXmlName))
+    else:
+        PrintColor.error('Error: %s' % str(schema.error_log))
+
+# handle main arguments
+if len(argv) != 3:
+    PrintColor.error('Error: usage %s xmlDirectory schemaDirectory' % str(argv[0]))
+    exit(1)
+
+xmlDirectory = argv[1]
+schemaDirectory = argv[2]
+
+print('[*] Validate xml files in %s with %s' % (xmlDirectory, schemaDirectory))
+
+for rootPath, _, files in walk(xmlDirectory):
+    for filename in files:
+        if filename.endswith('.xml'):
+            xmlFilePath = path.join(rootPath, filename)
+            schemaFileName = getSchemaFilenameFromXmlFile(xmlFilePath)
+            schemaFilePath = path.join(schemaDirectory, schemaFileName)
+            validateXmlWithSchema(xmlFilePath, schemaFilePath)