added XML handler and unit tests

This commit is contained in:
paul-loedige 2020-12-26 00:24:05 +01:00
parent 6dde46c676
commit 313a87bd12
6 changed files with 427 additions and 0 deletions

View File

@ -9,6 +9,7 @@ import android.widget.Toast;
import com.example.aped.communication.IIO; import com.example.aped.communication.IIO;
import com.example.aped.utils.IXML; import com.example.aped.utils.IXML;
import com.example.aped.utils.TestXML; import com.example.aped.utils.TestXML;
import com.example.aped.utils.XMLHandler;
import com.google.android.material.navigation.NavigationView; import com.google.android.material.navigation.NavigationView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;

View File

@ -0,0 +1,160 @@
package com.example.aped.utils;
import android.util.Log;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Documented;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathException;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import kotlin.NotImplementedError;
public class XMLHandler implements IXML{
/** the root of the XMl file **/
private Document root;
/**
* constructor for the XMLHandler
* @param xmlPath the path to the XML file
* @param xsdPath the path to the XSD file
* @throws ParserConfigurationException the XML parser configuration failed
* @throws IOException the XML file could not be accessed
* @throws SAXException the XML parse failed
*/
public XMLHandler(String xmlPath, String xsdPath)
throws ParserConfigurationException, IOException, SAXException
{
if(!validate(xmlPath,xsdPath)){
throw new VerifyError("the XML file is invalid");
}
//parse the root document fromt the XML file
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
root = builder.parse(new File(xmlPath));
}
/**
* validates a XML file against a XSD file
* @param xmlPath the path to the XML file
* @param xsdPath the path to the XSD file
* @return true if XML is valid
*/
private boolean validate(String xmlPath, String xsdPath){
try {
SchemaFactory factory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI
);
Schema schema = factory.newSchema(new File(xsdPath));
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new File(xmlPath)));
}catch (IOException | SAXException e){
Log.e("XMLHandler",
"Error while validating the XML file" +
e.getMessage());
return false;
}
return true;
}
@Override
public int download() {
throw new NotImplementedError();
}
@Override
public int upload() {
throw new NotImplementedError();
}
@Override
public List<String> getDeviceNames() {
List<String> returnList = new ArrayList<>();
NodeList devices = root.getElementsByTagName("Device");
for(int i = 0; i < devices.getLength();i++){
returnList.add(((Element) devices.item(i)).getAttribute("name"));
}
return returnList;
}
@Override
public Dictionary<String, Object> getValueInfo(String deviceName) {
Dictionary<String, Object> returnDictionary = new Hashtable<>();
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
try{
XPathExpression xPathExpression = xPath.compile("//Device[@name='" + deviceName + "']/ValueInfo");
Element result = (Element) xPathExpression.evaluate(root, XPathConstants.NODE);
returnDictionary.put("type",result.getAttribute("type"));
returnDictionary.put("unit",result.getAttribute("unit"));
NodeList childNodes = result.getChildNodes();
float offset = 0.0f;
float factor = 1.0f;
for(int i = 0; i < childNodes.getLength(); i++){
switch (childNodes.item(i).getNodeName()){
case "Offset":
offset = Float.parseFloat(childNodes.item(i).getTextContent());
break;
case "Factor":
factor = Float.parseFloat(childNodes.item(i).getTextContent());
break;
default:
break;
}
}
returnDictionary.put("offset",offset);
returnDictionary.put("factor",factor);
}catch (XPathExpressionException e){
Log.e("XMLHandler","the XPath for getting the value info has errors:" + e.getMessage());
}
return returnDictionary;
}
@Override
public Dictionary<String, Object> getPort(String deviceName) {
Dictionary<String, Object> returnDictionary = new Hashtable<>();
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
try{
XPathExpression xPathExpression = xPath.compile("//Device[@name='" + deviceName + "']/Port");
Element result = (Element) xPathExpression.evaluate(root, XPathConstants.NODE);
returnDictionary.put("protocol",result.getAttribute("protocol"));
NodeList childNodes = result.getChildNodes();
List<String> pins = new ArrayList<>();
for(int i = 0; i < childNodes.getLength(); i++){
Node childNode = childNodes.item(i);
if(childNode.getNodeName().equals("Pin")){
pins.add(childNode.getTextContent());
}
}
returnDictionary.put("pins",pins);
}catch (XPathExpressionException e){
Log.e("XMLHandler","the XPath for getting the value info has errors:" + e.getMessage());
}
return returnDictionary;
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<DeviceList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="config.xsd">
<Device name="example">
<ValueInfo type="boolean"/>
<Port protocol="DI">
<Pin>GPIO_2</Pin>
</Port>
</Device>
<Device name="sensorarray">
<ValueInfo type="int" unit="°C">
<Offset>1.2</Offset>
<Factor>2.5</Factor>
</ValueInfo>
<Port protocol="DI">
<Pin>GPIO_3</Pin>
<Pin>GPIO_4</Pin>
<Pin>GPIO_5</Pin>
<Pin>GPIO_6</Pin>
</Port>
</Device>
</DeviceList>

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- available protocols types -->
<xs:simpleType name="ProtocolType">
<xs:restriction base="xs:string">
<xs:enumeration value="DI"/>
<xs:enumeration value="DO"/>
<xs:enumeration value="AI"/>
<xs:enumeration value="AO"/>
</xs:restriction>
</xs:simpleType>
<!-- available value types -->
<xs:simpleType name="ValueType">
<xs:restriction base="xs:string">
<xs:enumeration value="int"/>
<xs:enumeration value="float"/>
<xs:enumeration value="boolean"/>
</xs:restriction>
</xs:simpleType>
<!-- available Pins -->
<xs:simpleType name="Pin">
<xs:restriction base="xs:string">
<xs:enumeration value="GPIO_2"/>
<xs:enumeration value="GPIO_3"/>
<xs:enumeration value="GPIO_4"/>
<xs:enumeration value="GPIO_5"/>
<xs:enumeration value="GPIO_6"/>
<xs:enumeration value="GPIO_7"/>
<xs:enumeration value="GPIO_8"/>
<xs:enumeration value="GPIO_9"/>
<xs:enumeration value="GPIO_10"/>
<xs:enumeration value="GPIO_11"/>
<xs:enumeration value="GPIO_12"/>
<xs:enumeration value="GPIO_13"/>
<xs:enumeration value="GPIO_14"/>
<xs:enumeration value="GPIO_15"/>
<xs:enumeration value="GPIO_16"/>
<xs:enumeration value="GPIO_17"/>
<xs:enumeration value="GPIO_18"/>
<xs:enumeration value="GPIO_19"/>
<xs:enumeration value="GPIO_20"/>
<xs:enumeration value="GPIO_21"/>
<xs:enumeration value="GPIO_22"/>
<xs:enumeration value="GPIO_23"/>
<xs:enumeration value="GPIO_24"/>
<xs:enumeration value="GPIO_25"/>
<xs:enumeration value="GPIO_26"/>
<xs:enumeration value="GPIO_27"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="DeviceList">
<xs:complexType>
<xs:sequence>
<xs:element ref="Device"
minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:unique name="UniqueDevice">
<xs:selector xpath="Device"/>
<xs:field xpath="@name"/>
</xs:unique>
<xs:unique name="UniquePin">
<xs:selector xpath="Device"/>
<xs:field xpath="Protocol/Port/Pin"/>
</xs:unique>
</xs:element>
<xs:element name="Device">
<xs:complexType>
<xs:sequence>
<xs:element ref="ValueInfo"
minOccurs="1"
maxOccurs="1"/>
<xs:element ref="Port"
minOccurs="1"
maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="name"
type="xs:string"
use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="ValueInfo">
<xs:complexType>
<xs:sequence>
<xs:element name="Offset"
type="xs:float"
minOccurs="0"
maxOccurs="1"/>
<xs:element name="Factor"
type="xs:float"
minOccurs="0"
maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="type"
type="ValueType"
use="required"/>
<xs:attribute name="unit"
type="xs:string"
use="optional"/>
</xs:complexType>
</xs:element>
<xs:element name="Port">
<xs:complexType>
<xs:sequence>
<xs:element name="Pin"
type="Pin"
minOccurs="1"
maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="protocol" type="ProtocolType" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -0,0 +1,92 @@
package com.example.aped;
import com.example.aped.utils.XMLHandler;
import org.junit.Test;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.util.Dictionary;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import static org.junit.Assert.*;
public class XMLHandlerUnitTest {
private String xmlPath="src/test/java/com/example/aped/Test.xml";
private String xsdPath="src/test/java/com/example/aped/Test.xsd";
@Test
public void TestFiles_AreValid(){
try{
XMLHandler xmlHandler = new XMLHandler(xmlPath,xsdPath);
}catch(IOException | ParserConfigurationException | SAXException e){
System.out.println("XMLHandler failed");
assert(false);
}catch(VerifyError e){
System.out.println("XML not valid");
assert(false);
}
}
@Test
public void Test_getDeviceNames(){
try{
XMLHandler xmlHandler = new XMLHandler(xmlPath,xsdPath);
List<String> deviceNames = xmlHandler.getDeviceNames();
assertArrayEquals(new String[]{"example","sensorarray"},deviceNames.toArray());
}catch(IOException | ParserConfigurationException | SAXException e){
System.out.println("XMLHandler failed");
assert(false);
}
}
@Test
public void TestInput_SimpleValueInfo(){
try{
XMLHandler xmlHandler = new XMLHandler(xmlPath,xsdPath);
Dictionary<String, Object> valueInfo = xmlHandler.getValueInfo("example");
assertEquals("{factor=1.0, type=boolean, unit=, offset=0.0}",valueInfo.toString());
}catch(IOException | ParserConfigurationException | SAXException e){
System.out.println("XMLHandler failed");
assert(false);
}
}
@Test
public void TestInput_ComplexValueInfo(){
try{
XMLHandler xmlHandler = new XMLHandler(xmlPath,xsdPath);
Dictionary<String, Object> valueInfo = xmlHandler.getValueInfo("sensorarray");
assertEquals("{factor=2.5, type=int, unit=°C, offset=1.2}",valueInfo.toString());
}catch(IOException | ParserConfigurationException | SAXException e){
System.out.println("XMLHandler failed");
assert(false);
}
}
@Test
public void TestInput_SimplePort(){
try{
XMLHandler xmlHandler = new XMLHandler(xmlPath,xsdPath);
Dictionary<String, Object> port = xmlHandler.getPort("example");
assertEquals("{pins=[GPIO_2], protocol=DI}",port.toString());
}catch(IOException | ParserConfigurationException | SAXException e){
System.out.println("XMLHandler failed");
assert(false);
}
}
@Test
public void TestInput_ComplexPort(){
try{
XMLHandler xmlHandler = new XMLHandler(xmlPath,xsdPath);
Dictionary<String, Object> port = xmlHandler.getPort("sensorarray");
assertEquals("{pins=[GPIO_3, GPIO_4, GPIO_5, GPIO_6], protocol=DI}",port.toString());
}catch(IOException | ParserConfigurationException | SAXException e){
System.out.println("XMLHandler failed");
assert(false);
}
}
}

View File

@ -0,0 +1,29 @@
package android.util;
public class Log {
public static int d(String tag, String msg) {
System.out.println("DEBUG: " + tag + ": " + msg);
return 0;
}
public static int i(String tag, String msg) {
System.out.println("INFO: " + tag + ": " + msg);
return 0;
}
public static int w(String tag, String msg) {
System.out.println("WARN: " + tag + ": " + msg);
return 0;
}
public static int e(String tag, String msg) {
System.out.println("ERROR: " + tag + ": " + msg);
return 0;
}
public static int v(String tag, String msg) {
System.out.println("ERROR: " + tag + ": " + msg);
return 0;
}
// add other methods if required...
}