dev #26

Merged
bema merged 42 commits from dev into master 2023-02-13 11:04:17 +01:00
30 changed files with 346 additions and 678 deletions

3
Backend-libXML/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.idea
__pycache**
venv

View File

@@ -0,0 +1,8 @@
FROM tiangolo/meinheld-gunicorn-flask:python3.9
COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./main.py /app/
COPY ./Parser.py /app/

69
Backend-libXML/Parser.py Normal file
View File

@@ -0,0 +1,69 @@
from lxml import etree
def xpath(source: str, xpath: str) -> str:
"""
Method used to get nodes from XML string using XPath
:param source: XML string used for selection
:type source: str
:param xpath: XPath query used for selection
:type xpath: str
:return: Nodes selected using XPath
:rtype: str
"""
root = etree.XML(source)
nsmap = root.nsmap
# LXML doesn't accept empty (None) namespace prefix,
# so it need to be deleted if exists
if None in nsmap:
nsmap.pop(None)
result = root.xpath(xpath, namespaces=nsmap)
result_string = ""
for e in result:
result_string += etree.tostring(e, pretty_print=True).decode() + "\n"
return result_string
def xsd(source: str, xsd: str) -> bool:
"""
Method used to validate XML string against XSD schema
:param source: XML string used for validation
:type source: str
:param xsd: XSD schema to validate XML against
:type xsd: str
:return: Message saying, if the validation was successful or not
:rtype: str
"""
xml_schema = etree.XMLSchema(etree.XML(xsd))
xml = etree.XML(source)
if xml_schema.validate(xml):
return "XML is valid."
else:
return "XML is NOT valid."
def xslt(source: str, xslt: str) -> str:
"""
Method used to transformate XML string using XSLT
:param source: XML string to transform
:type source: str
:param xslt: XSLT string used to transformate XML
:type xslt: str
:return: Result of transformation
:rtype: str
"""
xslt_transform = etree.XSLT(etree.XML(xslt))
xml = etree.XML(source)
transformated = xslt_transform(xml)
print(transformated)
return str(transformated)

74
Backend-libXML/main.py Normal file
View File

@@ -0,0 +1,74 @@
from flask import Flask
from flask_cors import CORS
from flask import request
from lxml import etree
import json
import time
import Parser
app = Flask(__name__)
CORS(app)
cors = CORS(app, resource={
r"/*":{
"origins":"*"
}
})
def process_xml(request: request, type: str) -> str:
"""Function to process
:param request: Received request
:type request: request
:param type: Type of needed processing: xsd, xslt or xpath
:type type: str
:raises ValueError: is raised when type is different than those provided above
:return: response JSON converted to string and response code
:rtype: str, int
"""
start = time.time_ns()
code = 200
response_json = dict()
try:
request_data = json.loads(request.get_data(as_text=True))
data = request_data['data']
process = request_data['process']
if (type == "xsd"):
response_json['result'] = Parser.xsd(data, process)
elif (type == "xslt"):
response_json['result'] = Parser.xslt(data, process)
elif (type == "xpath"):
response_json['result'] = Parser.xpath(data, process)
else:
raise ValueError("Valid operation types are: xsd, xslt, xpath")
response_json['status'] = "OK"
except KeyError as e:
response_json['result'] = "Missing key: " + str(e)
response_json['status'] = "ERR"
code = 400
except Exception as e:
response_json['result'] = str(e)
response_json['status'] = "ERR"
code = 400
finally:
exec_time = (time.time_ns() - start) / 10**6
response_json['time'] = f"{exec_time:.03f}"
response_json['processor'] = "libxml2 over lxml"
return json.dumps(response_json), code
@app.route("/xpathpost", methods=["POST"])
def xpath():
return process_xml(request, "xpath")
@app.route("/xsdpost", methods=["POST"])
def xsd():
return process_xml(request, "xsd")
@app.route("/xsltpost", methods=["POST"])
def xslt():
return process_xml(request, "xslt")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,3 @@
lxml
flask
flask_cors

View File

@@ -0,0 +1,6 @@
{
"data": "<books><book id='1'><name>Hamlet</name><date>2001-05-04</date><authorId>1</authorId><availability>false</availability></book><book id='2'><name>Macbeth</name><date>2000-12-13</date><authorId>1</authorId><availability>false</availability></book><book id='3'><name>Harry Potter and the Sorcerer's Stone</name><date>2005-04-29</date><authorId>2</authorId><availability>true</availability></book><book id='4'><name>The Long Walk</name><date>2018-07-01</date><authorId>4</authorId><availability>true</availability></book><book id='5'><name>Misery</name><date>2018-01-31</date><authorId>4</authorId><availability>true</availability></book><book id='6'><name>Think and Grow Rich</name><date>2004-09-10</date><authorId>6</authorId><availability>true</availability></book><book id='7'><name>The Law of Success</name><date>1982-05-09</date><authorId>6</authorId><availability>false</availability></book><book id='8'><name>Patriot Games</name><date>1995-10-21</date><authorId>5</authorId><availability>false</availability></book><book id='9'><name>The Sum of All Fears</name><date>1992-09-19</date><authorId>5</authorId><availability>false</availability></book><book id='10'><name>The Alchemist</name><date>2017-02-20</date><authorId>3</authorId><availability>false</availability></book><book id='11'><name>Hamlet</name><date>1994-06-01</date><authorId>1</authorId><availability>false</availability></book><book id='12'><name>Measure for Measure</name><date>1990-03-23</date><authorId>1</authorId><availability>false</availability></book><book id='13'><name>Hamlet</name><date>1989-05-05</date><authorId>1</authorId><availability>true</availability></book><book id='14'><name>Hamlet</name><date>1999-05-30</date><authorId>1</authorId><availability>true</availability></book><book id='15'><name>The Law of Success</name><date>2004-11-26</date><authorId>6</authorId><availability>true</availability></book><book id='16'><name>Romeo and Juliet</name><date>1997-02-08</date><authorId>1</authorId><availability>true</availability></book><book id='17'><name>The Alchemist</name><date>2009-08-21</date><authorId>3</authorId><availability>true</availability></book></books>",
"process": "/books/book[name = 'The Law of Success']",
"processor": "saxon",
"version": "2.0"
}

View File

@@ -0,0 +1,6 @@
{
"data": "<b:books xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://www.demo.com' xmlns:b='http://www.book.com' xmlns:a='http://www.author.com'><b:book id='1'><b:name>Hamlet</b:name><b:date>2001-05-04</b:date><a:authorId>1</a:authorId><b:availability>false</b:availability></b:book><b:book id='2'><b:name>Macbeth</b:name><b:date>2000-12-13</b:date><a:authorId>1</a:authorId><b:availability>false</b:availability></b:book><b:book id='3'><b:name>Harry Potter and the Sorcerer's Stone</b:name><b:date>2005-04-29</b:date><a:authorId>2</a:authorId><b:availability>true</b:availability></b:book><b:book id='4'><b:name>The Long Walk</b:name><b:date>2018-07-01</b:date><a:authorId>4</a:authorId><b:availability>true</b:availability></b:book><b:book id='5'><b:name>Misery</b:name><b:date>2018-01-31</b:date><a:authorId>4</a:authorId><b:availability>true</b:availability></b:book><b:book id='6'><b:name>Think and Grow Rich</b:name><b:date>2004-09-10</b:date><a:authorId>6</a:authorId><b:availability>true</b:availability></b:book><b:book id='7'><b:name>The Law of Success</b:name><b:date>1982-05-09</b:date><a:authorId>6</a:authorId><b:availability>false</b:availability></b:book><b:book id='8'><b:name>Patriot Games</b:name><b:date>1995-10-21</b:date><a:authorId>5</a:authorId><b:availability>false</b:availability></b:book><b:book id='9'><b:name>The Sum of All Fears</b:name><b:date>1992-09-19</b:date><a:authorId>5</a:authorId><b:availability>false</b:availability></b:book><b:book id='10'><b:name>The Alchemist</b:name><b:date>2017-02-20</b:date><a:authorId>3</a:authorId><b:availability>false</b:availability></b:book><b:book id='11'><b:name>Hamlet</b:name><b:date>1994-06-01</b:date><a:authorId>1</a:authorId><b:availability>false</b:availability></b:book><b:book id='12'><b:name>Measure for Measure</b:name><b:date>1990-03-23</b:date><a:authorId>1</a:authorId><b:availability>false</b:availability></b:book><b:book id='13'><b:name>Hamlet</b:name><b:date>1989-05-05</b:date><a:authorId>1</a:authorId><b:availability>true</b:availability></b:book><b:book id='14'><b:name>Hamlet</b:name><b:date>1999-05-30</b:date><a:authorId>1</a:authorId><b:availability>true</b:availability></b:book><b:book id='15'><b:name>The Law of Success</b:name><b:date>2004-11-26</b:date><a:authorId>6</a:authorId><b:availability>true</b:availability></b:book><b:book id='16'><b:name>Romeo and Juliet</b:name><b:date>1997-02-08</b:date><a:authorId>1</a:authorId><b:availability>true</b:availability></b:book><b:book id='17'><b:name>The Alchemist</b:name><b:date>2009-08-21</b:date><a:authorId>3</a:authorId><b:availability>true</b:availability></b:book></b:books>",
"process": "/b:books/b:book[b:name = 'The Law of Success']",
"processor": "saxon",
"version": "2.0"
}

View File

@@ -0,0 +1,4 @@
#url = "localhost:8081/xpathpost"
url = "localhost:5000/xpath"
request = "POST"
data = "@data.json"

View File

@@ -0,0 +1,4 @@
#url = "localhost:8081/xpathpost"
url = "localhost:5000/xpath"
request = "POST"
data = "@dataNS.json"

View File

@@ -0,0 +1,4 @@
#url = "http://localhost:8082/xsd"
url = "http://localhost:5000/xsd"
data = "@xsd.json"
request = POST

View File

@@ -0,0 +1,6 @@
{
"data": "<ns0:values xmlns:ns0 = \"http://www.tibco.com/schemas/test/Test/Resources/Schema.xsd\"><ns0:value>Test</ns0:value><ns0:value>Test3</ns0:value></ns0:values>",
"process": "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://www.tibco.com/schemas/test/Test/Resources/Schema.xsd\" targetNamespace=\"http://www.tibco.com/schemas/test/Test/Resources/Schema.xsd\" elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\"> <xs:element name=\"values\"><xs:complexType><xs:sequence><xs:element name=\"value\" type=\"xs:string\" minOccurs=\"0\" maxOccurs=\"unbounded\"/></xs:sequence></xs:complexType></xs:element></xs:schema>",
"processor": "saxon",
"version": "1.0"
}

View File

@@ -0,0 +1,3 @@
url = "http://localhost:5000/xslt"
data = "@xslt.json"
request = POST

View File

@@ -0,0 +1,6 @@
{
"data": "<b:books xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://www.demo.com' xmlns:b='http://www.book.com' xmlns:a='http://www.author.com'><b:book id='1'><b:name>Hamlet</b:name><b:date>2001-05-04</b:date><a:authorId>1</a:authorId><b:availability>false</b:availability></b:book><b:book id='2'><b:name>Macbeth</b:name><b:date>2000-12-13</b:date><a:authorId>1</a:authorId><b:availability>false</b:availability></b:book><b:book id='3'><b:name>Harry Potter and the Sorcerer's Stone</b:name><b:date>2005-04-29</b:date><a:authorId>2</a:authorId><b:availability>true</b:availability></b:book><b:book id='4'><b:name>The Long Walk</b:name><b:date>2018-07-01</b:date><a:authorId>4</a:authorId><b:availability>true</b:availability></b:book><b:book id='5'><b:name>Misery</b:name><b:date>2018-01-31</b:date><a:authorId>4</a:authorId><b:availability>true</b:availability></b:book><b:book id='6'><b:name>Think and Grow Rich</b:name><b:date>2004-09-10</b:date><a:authorId>6</a:authorId><b:availability>true</b:availability></b:book><b:book id='7'><b:name>The Law of Success</b:name><b:date>1982-05-09</b:date><a:authorId>6</a:authorId><b:availability>false</b:availability></b:book><b:book id='8'><b:name>Patriot Games</b:name><b:date>1995-10-21</b:date><a:authorId>5</a:authorId><b:availability>false</b:availability></b:book><b:book id='9'><b:name>The Sum of All Fears</b:name><b:date>1992-09-19</b:date><a:authorId>5</a:authorId><b:availability>false</b:availability></b:book><b:book id='10'><b:name>The Alchemist</b:name><b:date>2017-02-20</b:date><a:authorId>3</a:authorId><b:availability>false</b:availability></b:book><b:book id='11'><b:name>Hamlet</b:name><b:date>1994-06-01</b:date><a:authorId>1</a:authorId><b:availability>false</b:availability></b:book><b:book id='12'><b:name>Measure for Measure</b:name><b:date>1990-03-23</b:date><a:authorId>1</a:authorId><b:availability>false</b:availability></b:book><b:book id='13'><b:name>Hamlet</b:name><b:date>1989-05-05</b:date><a:authorId>1</a:authorId><b:availability>true</b:availability></b:book><b:book id='14'><b:name>Hamlet</b:name><b:date>1999-05-30</b:date><a:authorId>1</a:authorId><b:availability>true</b:availability></b:book><b:book id='15'><b:name>The Law of Success</b:name><b:date>2004-11-26</b:date><a:authorId>6</a:authorId><b:availability>true</b:availability></b:book><b:book id='16'><b:name>Romeo and Juliet</b:name><b:date>1997-02-08</b:date><a:authorId>1</a:authorId><b:availability>true</b:availability></b:book><b:book id='17'><b:name>The Alchemist</b:name><b:date>2009-08-21</b:date><a:authorId>3</a:authorId><b:availability>true</b:availability></b:book></b:books>",
"process": "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:b='http://www.demo.com'><xsl:template match='b:books'><Library><BookCount><xsl:value-of select='count(b:book)' /></BookCount></Library></xsl:template></xsl:stylesheet>",
"processor": "saxon",
"version": "1.0"
}

View File

@@ -3,12 +3,12 @@
<head> <head>
<title>R11 MockedServices</title> <title>R11 MockedServices</title>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" href="http://localhost:8086/assets/css/common/fontello.css" type="text/css"> <link rel="stylesheet" href="../css/fontello.css" type="text/css">
<link rel="stylesheet" href="http://localhost:8086/assets/css/mock-service/main.css" type="text/css"> <link rel="stylesheet" href="../css/main.css" type="text/css">
<!-- <link rel="stylesheet" href="css/common.css" type="text/css"> --> <!-- <link rel="stylesheet" href="css/common.css" type="text/css"> -->
<link rel="stylesheet" href="http://localhost:8086/assets/css/mock-service/common.css" type="text/css"> <link rel="stylesheet" href="../css/common.css" type="text/css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="../js/dyn_host.js"></script> <!-- <script src="../js/dyn_host.js"></script> -->
</head> </head>
<body> <body>
<div class="container"> <div class="container">
@@ -37,15 +37,15 @@
<!-- status --> <!-- status -->
<div class="max-width small-vertical-margin"> <div class="max-width small-vertical-margin">
<label for="httpStatus">Http Status</label> <label for="httpStatus">Http Status</label>
<input id="httpStatus" class="bordered-field max-width data-field" type="text" value="200" list="httpStatusSuggestion"> <!-- <input id="httpStatus" class="bordered-field max-width data-field" type="text" value="200" list="httpStatusSuggestion"> -->
<datalist id="httpStatusSuggestion"> <select id="httpStatus" class="bordered-field max-width data-field" value="200">
<option value="200"> <option value="200">200</option>
<option value="300"> <option value="300">300</option>
<option value="400"> <option value="400">400</option>
<option value="403"> <option value="403">403</option>
<option value="404"> <option value="404">404</option>
<option value="500"> <option value="500">500</option>
</datalist> </select>
</div> </div>
<!-- content type --> <!-- content type -->
<div class="max-width small-vertical-margin"> <div class="max-width small-vertical-margin">

View File

@@ -1,11 +0,0 @@
$(document).ready( function() {
console.log("Here")
let links = document.getElementsByTagName("link")
for (let i = 0; i < links.length; i++) {
let oldStr = links[i].href.split("/")
let endpoint = oldStr.slice(3).join("/")
links[i].href = window.location.protocol + "//" + window.location.hostname + ":8086/" + endpoint
}
});

View File

@@ -1,5 +1,6 @@
package com.r11.tools.xslt; package com.r11.tools.xslt;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.r11.tools.xslt.processors.Saxon; import com.r11.tools.xslt.processors.Saxon;
@@ -65,8 +66,15 @@ public class SparkInitializer {
Map<String, String> responseMap = new HashMap<>(); Map<String, String> responseMap = new HashMap<>();
try { try {
requestMap = mapper.readValue(body, Map.class); requestMap = mapper.readValue(body, Map.class);
} catch (JsonMappingException ex) { } catch (JsonMappingException | JsonParseException ex) {
ex.printStackTrace(); LOG.error("Request JSON error. " + ex);
responseMap.put("result", ex.getMessage());
responseMap.put("processor", "N/A");
responseMap.put("status", "ERR");
responseMap.put("time", "N/A");
resp.status(400);
resp.body(mapper.writeValueAsString(responseMap));
return resp;
} }
String data = requestMap.get("data"); String data = requestMap.get("data");
@@ -103,8 +111,15 @@ public class SparkInitializer {
Map<String, String> responseMap = new HashMap<>(); Map<String, String> responseMap = new HashMap<>();
try { try {
requestMap = mapper.readValue(body, Map.class); requestMap = mapper.readValue(body, Map.class);
} catch (JsonMappingException ex) { } catch (JsonMappingException | JsonParseException ex) {
LOG.error("JSON mapping error. " + ex); LOG.error("Request JSON error. " + ex);
responseMap.put("result", ex.getMessage());
responseMap.put("processor", "N/A");
responseMap.put("status", "ERR");
responseMap.put("time", "N/A");
resp.status(400);
resp.body(mapper.writeValueAsString(responseMap));
return resp;
} }
String data = requestMap.get("data"); String data = requestMap.get("data");
@@ -158,7 +173,6 @@ public class SparkInitializer {
duration = System.currentTimeMillis() - timeStart; duration = System.currentTimeMillis() - timeStart;
LOG.info("Request: " + body + " processed in " + duration + " ms."); LOG.info("Request: " + body + " processed in " + duration + " ms.");
responseMap.put("processor", Xalan.getVersion()); responseMap.put("processor", Xalan.getVersion());
responseMap.put("result", tmp);
responseMap.put("time", Long.toString(duration)); responseMap.put("time", Long.toString(duration));
resp.body(mapper.writeValueAsString(responseMap)); resp.body(mapper.writeValueAsString(responseMap));
return resp; return resp;
@@ -174,17 +188,24 @@ public class SparkInitializer {
private static final Route xsltHandler = (Request req, Response resp) -> { private static final Route xsltHandler = (Request req, Response resp) -> {
String body = req.body(); String body = req.body();
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
Map<String, String> jsonMap = new HashMap<>(); Map<String, String> requestMap = new HashMap<>();
Map<String, String> responseMap = new HashMap<>(); Map<String, String> responseMap = new HashMap<>();
try { try {
jsonMap = mapper.readValue(body, Map.class); requestMap = mapper.readValue(body, Map.class);
} catch (JsonMappingException ex) { } catch (JsonMappingException | JsonParseException ex) {
LOG.error("JSON mapping error. " + ex); LOG.error("Request JSON error. " + ex);
responseMap.put("result", ex.getMessage());
responseMap.put("processor", "N/A");
responseMap.put("status", "ERR");
responseMap.put("time", "N/A");
resp.status(400);
resp.body(mapper.writeValueAsString(responseMap));
return resp;
} }
String data = jsonMap.get("data"); String data = requestMap.get("data");
String query = jsonMap.get("process"); String query = requestMap.get("process");
String processor = jsonMap.get("processor"); String processor = requestMap.get("processor");
String version = jsonMap.get("version"); String version = requestMap.get("version");
if (processor == null) { if (processor == null) {
return "saxon, xalan"; return "saxon, xalan";

View File

@@ -1,8 +1,12 @@
package com.r11.tools.xslt.processors; package com.r11.tools.xslt.processors;
import net.sf.saxon.lib.RawResult;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import javax.xml.XMLConstants; import javax.xml.XMLConstants;
@@ -19,8 +23,7 @@ import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import java.io.StringReader; import java.io.*;
import java.io.StringWriter;
/** /**
* Handler for Xalan engine * Handler for Xalan engine
@@ -53,25 +56,55 @@ public class Xalan {
return sw.toString(); return sw.toString();
} }
private static boolean isTextNode(Node n) {
if (n == null)
return false;
short nodeType = n.getNodeType();
return nodeType == Node.CDATA_SECTION_NODE || nodeType == Node.TEXT_NODE;
}
/** /**
* Process xpath and return either node or wrapped atomic value * Process xpath and return either node or wrapped atomic value
* @deprecated
* Xalan needs assumption of the outcome, which is not implemented. Therefore method is deprecated
* @param data xml * @param data xml
* @param transform xpath * @param transform xpath
* @return xml processed using given xpath * @return xml processed using given xpath
* @throws Exception thrown on node building errors or invalid xpath * @throws Exception thrown on node building errors or invalid xpath
*/ */
public static String processXPath(String data, String transform) throws Exception{ public static String processXPath(String data, String transform) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
XPath xpath = XPathFactory.newInstance().newXPath(); // Set up a DOM tree to query.
InputSource in = new InputSource(new StringReader(data));
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
dfactory.setNamespaceAware(true);
Document doc = dfactory.newDocumentBuilder().parse(in);
xpath.setNamespaceContext(new XalanNamespaceResolver(builder.parse(new InputSource(new StringReader(data))), true)); // Set up an identity transformer to use as serializer.
XPathExpression exp = xpath.compile(transform); Transformer serializer = TransformerFactory.newInstance().newTransformer();
exp.evaluate(new InputSource(new StringReader(data)), XPathConstants.NODESET); serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
return exp.evaluate(new InputSource(new StringReader(data)));
// Use the simple XPath API to select a nodeIterator.
NodeIterator nl = XPathAPI.selectNodeIterator(doc, transform);
// Serialize the found nodes to result object.
StringBuilder result = new StringBuilder();
Node n;
while ((n = nl.nextNode())!= null) {
StringBuilder sb;
if (isTextNode(n)) {
// DOM may have more than one node corresponding to a
// single XPath text node. Coalesce all contiguous text nodes
// at this level
for (Node nn = n.getNextSibling(); isTextNode(nn); nn = nn.getNextSibling()) {
result.append(nn.getNodeValue());
}
} else {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
serializer.transform(new DOMSource(n), new StreamResult(new OutputStreamWriter(outputStream)));
result.append(outputStream);
}
result.append("\n");
}
return result.toString();
} }
/** /**

View File

@@ -1,6 +0,0 @@
@import url('https://necolas.github.io/normalize.css/8.0.1/normalize.css');
@import url('r11addons.css');
@import url('r11tables.css');
@import url('r11tool.css');
@import url('r11tooltip.css');
@import url('r11modal.css');

View File

@@ -1,4 +0,0 @@
.overflowedTableContent {
max-height: 750px;
overflow: scroll;
}

View File

@@ -1,59 +0,0 @@
.modification-button.btn-tile:hover {
color: #ca1111;
}
.modification-button.btn-tile {
width: 10%;
margin: 20% 0 0 0;
font-size: 14px;
color: #00000020
}
.modification-button.btn-addtile {
font-size: 38px;
color: #00000030;
}
.modification-button.btn-addtile:hover {
color: #58ac43;
}
.tile {
width: 100%;
padding-top: 40%;
border: 1px solid gray;
border-radius: 3px;
position: relative;
background: #f0f0f095;
margin-bottom: 10px;
cursor: default;
}
.tile:hover {
filter: brightness(110%);
}
.tile.active {
background: #00000070;
color: white;
filter: none;
}
.tile.active .btn-tile {
opacity: 0;
}
.tile .content {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
padding: 0 2% 0 7%;
display: flex;
}
.content p {
margin: 0;
padding: 0;
}

View File

@@ -1,104 +0,0 @@
#overlay {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
opacity: 0;
background: rgba(0, 0 , 0, 0.5);
pointer-events: none;
}
#overlay.active {
pointer-events: all;
opacity: 1;
}
.modal {
display: none;
width: 390px;
min-height: 71px;
max-height: 700px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 5px;
border: 1px solid #f0f0f0;
}
.modal.active {
display: block;
}
.modal div.header {
width: 384px;
height: 24px;
background: #2e3133;
color: white;
font-size: 24px;
font-weight: 700;
padding: 3px;
margin-bottom: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal div.header button {
font-size: 100%;
font-family: inherit;
border: 0;
padding: 0;
background: 0;
color: inherit;
cursor: pointer;
}
.modal div.header button:hover {
color: white;
font-weight: 700;
}
.modal div.body {
width: 370px;
padding: 10px;
background: #f0f0f0;
color: #2e3133;
min-height: 16px;
text-align: justify;
font-size: 16px;
}
.modal div.function {
width: 385px;
min-height: 30px;
padding-top: 5px;
display: flex;
justify-content: space-evenly;
background: inherit;
}
.modal div.function button {
min-height: 22px;
min-width: 34px;
max-width: 74px;
padding: 3px 20px;
outline: none;
border: 1px solid #f0f0f0;
background: rgba(205,205,205,1);
font-size: 16px;
text-align: center;
cursor: pointer;
}
.modal div.function button:hover {
filter: brightness(110%);
}
.r-exclamation:before {
content: '!';
color: #3bc4f1;
font-style: normal;
}

View File

@@ -1,71 +0,0 @@
.table-map {
width: 60%;
}
.table-map input{
font-size: 16px;
padding: 7px;
border: 1px solid rgba(145, 146, 146, 0.849);
border-radius: 5px;
}
.table-map input.key {
background: #f0f0f0;
}
.modification-button.btn-add {
font-size: 16px;
color: #00000030;
margin: auto 0 auto 0;
}
.modification-button.btn-add:hover {
color:#58ac43;
}
.modification-button.btn-hashmap {
font-size: 16px;
color: #00000030;
margin: auto 0 auto 0;
}
.modification-button.btn-hashmap:hover {
color: #ca1111;
}
.table-default {
width: 80%;
border-collapse: collapse;
border-spacing: 0;
}
.table-default tr {
background: #f0f0f02d;
}
.table-default tr.bottom-border {
border-bottom: 1px solid black;
}
.table-default th {
background: #ffffff;
}
.table-default tr.even {
background: #f0f0f0;
}
.table-doc td, .table-doc th{
border-spacing: 0px;
padding: 0px 10px;
}
.table-doc td {
background-color: rgba(155, 165, 160, 0.342);
}
.table-doc th {
background-color: #3bc4f1;
text-align: left;
color: white;
}

View File

@@ -1,300 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;700&display=swap');
.hyperlink, .hyperlink:visited, .hyperlink:active {
color: rgb(47, 125, 146);
cursor: pointer;
}
.hyperlink:hover {
filter: brightness(120%);
}
.bordered-field {
border: 2px solid rgba(93, 99, 96, 0.705);
border-radius: 5px;
padding: 8px;
}
.bordered-field:focus {
outline: none;
box-shadow: 0 0 5px rgba(81, 203, 238);
border: 2px solid #00000070;
}
.bordered-field:disabled {
background: #eeeeeed2;
}
.vertically-resizeable {
resize: vertical;
}
body {
font-family: 'Nunito', sans-serif;
}
.container {
display: flex;
justify-content: left;
width: 100%;
}
.tool {
width: 55%;
display: flex;
justify-content: space-evenly;
}
.tool.extended {
width: 65%;
}
.tool .tool-context {
width: 90%;
}
.tool.extended .tool-context {
width: 75%;
}
.tool.extended .tool-extention {
width: 20%;
padding-top: 2%;
display: block;
}
.tool .tool-extention {
display: none;
}
.tool-extention {
opacity: 0;
pointer-events: none;
}
.tool-extention.active {
opacity: 100%;
pointer-events: all;
}
.clickable-text {
padding: 0;
outline: none;
background: none;
border: none;
font-weight: 300;
cursor: pointer;
}
.clickable-text.highlight:hover {
color: #3bc4f1;
}
.clickable-text.switch {
font-size: 18px;
font-weight: 700;
}
.clickable-text.switch span.toggleIndicator:before {
content: '>';
}
.clickable-text.switch span.toggleIndicator.active:before {
content: 'v';
}
.modification-button {
padding: 0;
outline: none;
background: none;
border: none;
font-weight: 300;
}
.text-aligned-to-right {
text-align: right;
}
.centered-vertically {
margin-top: auto;
margin-bottom: auto;
}
.display-space-between {
width: 100%;
display: flex;
justify-content: space-between;
}
.display-space-evenly {
display: flex;
justify-content: space-evenly;
}
.float-left {
display: flex;
justify-content: left;
width: 100%;
}
.version-span {
font-size: 13px;
font-weight: 400;
color: rgba(85,85,85,0.555);
}
.block-display {
display: block;
}
.block-label {
display: block;
margin: 0 0 0 5px;
}
.tabmenu {
display: flex;
flex-direction: row;
text-align: center;
border-bottom: 1px solid rgba(185, 185, 185, 0.5);
}
.tabitem {
flex-grow: 1;
cursor: pointer;
padding: 5px 0;
}
.tabitem:hover {
font-weight: 700;
}
.tabitem.active {
background: rgba(33, 34, 34, 0.705);
color: white;
font-weight: 700;
cursor:default;
flex-grow: 1;
}
.big-font {
font-size: 20px;
}
.action-button.active {
background: #3bc4f1;
border: 1px solid #7ed0eb;
cursor: pointer;
}
.action-button.active:hover {
filter: brightness(110%);
}
.action-button {
background: rgba(155, 165, 160, 0.507);
border:1px solid rgba(186, 197, 191, 0.507);
color: white;
padding: 10px 20px;
font-weight: 700;
margin: 3px 0;
}
.quater-width {
width: 25%;
}
.half-width {
width: 50%;
}
.tree-fourth-width {
width: 75%;
}
.half-width.with-padding {
width: 45%;
}
.max-width {
width: 100%;
}
.max-width.with-padding {
width: 94%;
}
.max-height {
height: 100%;
}
.height-300 {
height: 300px;
}
.max-height.with-padding {
height: 90%;
}
.small-margins {
margin: 3%;
}
.small-vertical-margin {
margin-top: 10px;
margin-bottom: 10px;
}
.medium-vertical-margin {
margin-top: 30px;
margin-bottom: 30px;
}
.large-vertical-margin {
margin-top: 50px;
margin-bottom: 50px;
}
.textarea-300 {
height: 300px;
}
.centered-content {
display: flex;
justify-content: center;
}
.tabcontent {
display: none;
}
.tabcontent.active {
display: flex;
justify-content: center;
}
.hiddable {
display: none;
}
.hiddable.active {
display: inherit;
}
/* In case of collision with classes that use 'active' */
.hidden {
display: none;
}
/* TODO: Add proper class */
/* textarea {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
} */
/* TODO: Add proper class */
/* code{
line-height: 150%;
} */

View File

@@ -1,77 +0,0 @@
.tooltip-window {
position: fixed;
right: 0;
filter: drop-shadow(-2px 0px 2px darkgray);
background: #e8f3f7;
padding: 15px 30px;
font-family: 'Nunito', sans-serif;
width: 40%;
height: 100%;
overflow: scroll;
}
.tooltip-window.lite {
width: 30%;
}
.tip {
display: none;
}
.tip.active {
display: block;
}
/* TODO: Remove !important. It's bad practice and it can cause errors in future */
.section-button {
width: 100%;
padding: 15px 0;
font-size: 18px;
background: #b4b4b4c5;
cursor: pointer;
border-bottom: darkgray 2px solid !important;
}
.section-button:hover {
backdrop-filter: brightness(110%);
}
.section-button .active {
background: #00000030;
}
.List .collapsibleContent {
border-left: #bdc5c9 2px solid;
overflow: hidden;
background: #ffffff50;
}
/* TODO: .section class is to generic. It should be renamed */
.section{
padding: 10px 0px 20px 0px ;
}
/* TODO: content subclass already in use. Creating content class overrides the subclass.
Make .content a subclass of .content */
/* .content {
padding: 0px 15px 0px 15px ;
text-align: justify;
overflow: hidden;
transition: max-height .2s ease-out;
max-height: 0px;
border-left: #c0c2c3 2px solid;
} */
.collapsibleMini::before{
content: "►";
}
.collapsibleMini.active::before{
content: "▼";
}
/* TODO: Add proper class */
/* button:hover{
filter: brightness(110%);
} */

View File

@@ -105,8 +105,11 @@ function performRequest(text, checkXML, checkTransform){
//Form REST request, send, receive and display in resultArea //Form REST request, send, receive and display in resultArea
async function restRequest(text) { async function restRequest(text) {
const escapeChar = "specialEscapeChar"; const escapeChar = "specialEscapeChar";
// const addr = "http://localhost:8081/" + text; var port = ":8081/"
const addr = window.location.protocol + "//" + window.location.hostname + ":8081/" + text; if (getProcessor() == "libxml") {
port = ":8082/"
}
const addr = window.location.protocol + "//" + window.location.hostname + port + text;
var xmlData = document.getElementById("xmlArea").value.trim(); var xmlData = document.getElementById("xmlArea").value.trim();
var transformData = document.getElementById("transformArea").value.trim(); var transformData = document.getElementById("transformArea").value.trim();

View File

@@ -24,13 +24,15 @@
<label for="processors">Select XPath processor:</label> <label for="processors">Select XPath processor:</label>
<select name="processors" id="processors"> <select name="processors" id="processors">
<option value="saxon">Saxon</option> <option value="saxon">Saxon</option>
<!-- <option value="xalan">Xalan</option> --> <option value="xalan">Xalan</option>
<option value="libxml">libXML</option>
</select> </select>
<label for="versions">XPath version:</label> <label for="versions">XPath version:</label>
<select name="versions" id="versions"> <select name="versions" id="versions">
<option value="2.0">1.0/2.0</option> <option class="hideable libxml xalan"value="1.0">1.0</option>
<option value="3.0">3.0</option> <option class="hideable saxon" value="2.0">2.0</option>
<option value="3.1">3.1</option> <option class="hideable saxon" value="3.0">3.0</option>
<option class="hideable saxon" value="3.1">3.1</option>
</select> </select>
</div> </div>
<button class="action-button active" id="defaultXMLButton" style="padding: 3px 10px;" <button class="action-button active" id="defaultXMLButton" style="padding: 3px 10px;"
@@ -3066,6 +3068,31 @@
<script> <script>
function processVersionSelector() {
var processor = getProcessor();
var hideableOptions = document.getElementsByClassName("hideable");
for (let i = 0; i < hideableOptions.length; i++) {
hideableOptions[i].style = "display: none;";
}
if (processor == "xalan" || processor == "libxml") {
var xalanOptions = document.getElementsByClassName("xalan");
for (let i = 0; i < xalanOptions.length; i++) {
xalanOptions[i].style = "";
}
document.getElementById("versions").selectedIndex = 0;
}
else {
var saxonOptions = document.getElementsByClassName("saxon");
for (let i = 0; i < saxonOptions.length; i++) {
saxonOptions[i].style = "";
}
document.getElementById("versions").selectedIndex = 3;
}
processTooltip();
}
function processTooltip() { function processTooltip() {
var filter = "collapse" + getVersion(); var filter = "collapse" + getVersion();
var collList; var collList;
@@ -3086,6 +3113,9 @@
hideList(document.getElementsByName("collapse30")); hideList(document.getElementsByName("collapse30"));
hideList(document.getElementsByName("collapse31")); hideList(document.getElementsByName("collapse31"));
} }
// if (checkDefault(document.getElementById("xmlArea").value.trim()) || document.getElementById("xmlArea").value.trim() == "") { // if (checkDefault(document.getElementById("xmlArea").value.trim()) || document.getElementById("xmlArea").value.trim() == "") {
// document.getElementById("defaultXMLButton").classList.toggle("active", true); // document.getElementById("defaultXMLButton").classList.toggle("active", true);
// } else { // } else {
@@ -3175,6 +3205,7 @@
setDefaultContent(document.getElementById("transformArea"), 'Insert XPath expression here'); setDefaultContent(document.getElementById("transformArea"), 'Insert XPath expression here');
console.log("init"); console.log("init");
processTooltip(); processTooltip();
processVersionSelector();
tool.addEventListener('change', event => { tool.addEventListener('change', event => {
//Check if script was called from textarea or selector //Check if script was called from textarea or selector
var targetID = event.target.getAttribute('id'); var targetID = event.target.getAttribute('id');
@@ -3182,6 +3213,7 @@
return; return;
} }
processTooltip(); processTooltip();
processVersionSelector();
}) })
tool.addEventListener('click', event => { tool.addEventListener('click', event => {
//Check if script was called from textarea or selector //Check if script was called from textarea or selector
@@ -3190,6 +3222,7 @@
return; return;
} }
processTooltip(); processTooltip();
processVersionSelector();
}) })
tool.addEventListener('change', event => { tool.addEventListener('change', event => {
//Check if script was called from textarea or selector //Check if script was called from textarea or selector
@@ -3198,6 +3231,7 @@
return; return;
} }
processTooltip(); processTooltip();
processVersionSelector();
}) })
} }
</script> </script>

View File

@@ -18,8 +18,8 @@
</div> </div>
<label for="processors">Select XSLT processor:</label> <label for="processors">Select XSLT processor:</label>
<select name="processors" id="processors"> <select name="processors" id="processors">
<!-- <option value="saxon">Saxon</option> -->
<option value="xalan">Xalan</option> <option value="xalan">Xalan</option>
<option value="libxml">libXML</option>
</select> </select>
<!-- <span id="processorTooltipInfo">procInfo</span><br> --> <!-- <span id="processorTooltipInfo">procInfo</span><br> -->
<br> <br>

View File

@@ -19,6 +19,7 @@
<select name="processors" id="processors"> <select name="processors" id="processors">
<option value="saxon">Saxon</option> <option value="saxon">Saxon</option>
<option value="xalan">Xalan</option> <option value="xalan">Xalan</option>
<option value="libxml">libXML</option>
</select> </select>
<span id="processorTooltipInfo">procInfo</span><br> <span id="processorTooltipInfo">procInfo</span><br>
<br> <br>
@@ -1130,7 +1131,7 @@
console.log("processTooltip"); console.log("processTooltip");
if (getProcInfo() == "xalan") { if (getProcInfo() == "xalan" || getProcInfo() == "libxml") {
document.getElementById("tooltipFunctionInfo").innerText = "XSLT 1.0 functions"; document.getElementById("tooltipFunctionInfo").innerText = "XSLT 1.0 functions";
document.getElementById("processorTooltipInfo").innerText = "Supports XSLT 1.0"; document.getElementById("processorTooltipInfo").innerText = "Supports XSLT 1.0";
hideList(document.getElementsByName("collapse30")); hideList(document.getElementsByName("collapse30"));

View File

@@ -19,6 +19,13 @@ services:
ports: ports:
- 8081:8081 - 8081:8081
xmltools-libxml-backend:
build: ./Backend-libXML
container_name: xmltools-libxml-backend
image: xmltools-libxml-backend
ports:
- 8082:80
xmltools-mocked-services: xmltools-mocked-services:
build: build:
context: ./Backend/mocked-services context: ./Backend/mocked-services

View File

@@ -17,6 +17,11 @@ openapi.yml
Rest API accepts xml documents, given querry and processor version to call requested xml engine to perform given Rest API accepts xml documents, given querry and processor version to call requested xml engine to perform given
operation and finally returns outcome in response body. operation and finally returns outcome in response body.
## Flask Python backend
This is module providing support for processing XMLs using libxml library. It consumes same JSON as Java backend.
## Mocked services ## Mocked services
MockedServices is a tool that allows developer to create, in easy and simple way, http server mocked endpoints for integration tests MockedServices is a tool that allows developer to create, in easy and simple way, http server mocked endpoints for integration tests