From 313a87bd12c47d60cedbcbca560c630a8a88267f Mon Sep 17 00:00:00 2001 From: paul-loedige Date: Sat, 26 Dec 2020 00:24:05 +0100 Subject: [PATCH] added XML handler and unit tests --- .../java/com/example/aped/MainActivity.java | 1 + .../com/example/aped/utils/XMLHandler.java | 160 ++++++++++++++++++ .../src/test/java/com/example/aped/Test.xml | 23 +++ .../src/test/java/com/example/aped/Test.xsd | 122 +++++++++++++ .../com/example/aped/XMLHandlerUnitTest.java | 92 ++++++++++ .../example/aped/java/android/util/Log.java | 29 ++++ 6 files changed, 427 insertions(+) create mode 100644 APED/app/src/main/java/com/example/aped/utils/XMLHandler.java create mode 100644 APED/app/src/test/java/com/example/aped/Test.xml create mode 100644 APED/app/src/test/java/com/example/aped/Test.xsd create mode 100644 APED/app/src/test/java/com/example/aped/XMLHandlerUnitTest.java create mode 100644 APED/app/src/test/java/com/example/aped/java/android/util/Log.java diff --git a/APED/app/src/main/java/com/example/aped/MainActivity.java b/APED/app/src/main/java/com/example/aped/MainActivity.java index c82a256..e01aff0 100644 --- a/APED/app/src/main/java/com/example/aped/MainActivity.java +++ b/APED/app/src/main/java/com/example/aped/MainActivity.java @@ -9,6 +9,7 @@ import android.widget.Toast; import com.example.aped.communication.IIO; import com.example.aped.utils.IXML; import com.example.aped.utils.TestXML; +import com.example.aped.utils.XMLHandler; import com.google.android.material.navigation.NavigationView; import androidx.annotation.NonNull; diff --git a/APED/app/src/main/java/com/example/aped/utils/XMLHandler.java b/APED/app/src/main/java/com/example/aped/utils/XMLHandler.java new file mode 100644 index 0000000..260337c --- /dev/null +++ b/APED/app/src/main/java/com/example/aped/utils/XMLHandler.java @@ -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 getDeviceNames() { + List 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 getValueInfo(String deviceName) { + Dictionary 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 getPort(String deviceName) { + Dictionary 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 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; + } +} diff --git a/APED/app/src/test/java/com/example/aped/Test.xml b/APED/app/src/test/java/com/example/aped/Test.xml new file mode 100644 index 0000000..244ae6b --- /dev/null +++ b/APED/app/src/test/java/com/example/aped/Test.xml @@ -0,0 +1,23 @@ + + + + + + GPIO_2 + + + + + 1.2 + 2.5 + + + GPIO_3 + GPIO_4 + GPIO_5 + GPIO_6 + + + + \ No newline at end of file diff --git a/APED/app/src/test/java/com/example/aped/Test.xsd b/APED/app/src/test/java/com/example/aped/Test.xsd new file mode 100644 index 0000000..800b53e --- /dev/null +++ b/APED/app/src/test/java/com/example/aped/Test.xsd @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/APED/app/src/test/java/com/example/aped/XMLHandlerUnitTest.java b/APED/app/src/test/java/com/example/aped/XMLHandlerUnitTest.java new file mode 100644 index 0000000..ea98a9c --- /dev/null +++ b/APED/app/src/test/java/com/example/aped/XMLHandlerUnitTest.java @@ -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 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 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 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 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 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); + } + } +} diff --git a/APED/app/src/test/java/com/example/aped/java/android/util/Log.java b/APED/app/src/test/java/com/example/aped/java/android/util/Log.java new file mode 100644 index 0000000..fe992ba --- /dev/null +++ b/APED/app/src/test/java/com/example/aped/java/android/util/Log.java @@ -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... +}