From 0e696540d5d1b61f8461bf3475a69689cf990a1c Mon Sep 17 00:00:00 2001
From: Jacob Benz <jacob.benz@h-da.de>
Date: Fri, 9 Aug 2024 11:23:31 +0200
Subject: [PATCH] Improve validity check when manually editing XML

---
 package-lock.json                             | 29 ++++++++++++++++++-
 packages/cwrc-leafwriter/package.json         |  3 +-
 .../editSource/hooks/useValidation.tsx        | 29 +++++++++++++++----
 3 files changed, 54 insertions(+), 7 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 8a84dd72..c8589ce1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33,7 +33,7 @@
     },
     "apps/commons": {
       "name": "leafwriter-commons",
-      "version": "3.3.2",
+      "version": "3.4.0",
       "license": "GPL-2.0",
       "dependencies": {
         "@analytics/cookie-utils": "^0.2.12",
@@ -10739,6 +10739,27 @@
       "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz",
       "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw=="
     },
+    "node_modules/fast-xml-parser": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz",
+      "integrity": "sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        },
+        {
+          "type": "paypal",
+          "url": "https://paypal.me/naturalintelligence"
+        }
+      ],
+      "dependencies": {
+        "strnum": "^1.0.5"
+      },
+      "bin": {
+        "fxparser": "src/cli/cli.js"
+      }
+    },
     "node_modules/fastest-levenshtein": {
       "version": "1.0.16",
       "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
@@ -20949,6 +20970,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/strnum": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
+      "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
+    },
     "node_modules/style-to-object": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.6.tgz",
@@ -24216,6 +24242,7 @@
         "css-tooltip": "^0.3.4",
         "dexie": "^4.0.4",
         "dexie-react-hooks": "^1.1.7",
+        "fast-xml-parser": "^4.4.0",
         "formik": "^2.4.5",
         "framer-motion": "^11.0.27",
         "fscreen": "^1.2.0",
diff --git a/packages/cwrc-leafwriter/package.json b/packages/cwrc-leafwriter/package.json
index 2c1a9536..4e057cb4 100644
--- a/packages/cwrc-leafwriter/package.json
+++ b/packages/cwrc-leafwriter/package.json
@@ -111,7 +111,8 @@
     "wikibase-sdk": "^10.0.2",
     "zod": "^3.22.4",
     "zod-formik-adapter": "^1.3.0",
-    "git-url-parse": "^14.0.0"
+    "git-url-parse": "^14.0.0",
+    "fast-xml-parser": "^4.4.0"
   },
   "devDependencies": {
     "@types/chroma-js": "^2.4.4",
diff --git a/packages/cwrc-leafwriter/src/dialogs/editSource/hooks/useValidation.tsx b/packages/cwrc-leafwriter/src/dialogs/editSource/hooks/useValidation.tsx
index 18c7ee89..d1968f9f 100644
--- a/packages/cwrc-leafwriter/src/dialogs/editSource/hooks/useValidation.tsx
+++ b/packages/cwrc-leafwriter/src/dialogs/editSource/hooks/useValidation.tsx
@@ -2,6 +2,7 @@ import { useActions } from '../../../overmind';
 import { useAtomValue, useSetAtom } from 'jotai';
 import { useTranslation } from 'react-i18next';
 import { currentContentAtom, xmlValidityAtom } from '../store';
+const {XMLValidator} = require('fast-xml-parser');
 
 export type XMLValidity =
   | { valid: true }
@@ -23,6 +24,12 @@ export const useValidation = () => {
   const setXmlValidity = useSetAtom(xmlValidityAtom);
 
   const checkValidity = () => {
+    // Use fast-xml-parser to determine validty of XML document instead of canadian solution using DOM-Parser because
+    // the return values of DOMParser are not cross-browser compatible, the canadian way of interpreting them works only
+    // on chrom(e/ium)-based browser, we, however, test against Firefox first and foremost.
+    // One cavecat of using fast-xml-parser is, that the error message is not translated. However, persons directly editing XML
+    // should understand english well enough to at least understand the error message.
+    /*
     const parser = new DOMParser();
     const doc = parser.parseFromString(currentContent, 'application/xml');
     const errorNode = doc.querySelector('parsererror');
@@ -36,16 +43,28 @@ export const useValidation = () => {
 
     const lines = [...errorString.matchAll(/line ([0-9]*)/g)];
     const column = [...errorString.matchAll(/column ([0-9]*)/g)];
+    */
+    const result = XMLValidator.validate(currentContent, {
+      allowBooleanAttributes: true
+    });
+
+    //console.log(result)
+
+    if (result === true) {
+      const validity: XMLValidity = { valid: true };
+      setXmlValidity(validity);
+      return validity;
+    }
 
-    const positions = lines.map((line, index) => ({
-      line: Number(line[1]),
-      col: Number(column[index]?.[1]) ?? 0,
-    }));
+    const positions = [{
+      line: result["err"]["line"],
+      col: result["err"]["col"],
+    }];
 
     const validity: XMLValidity = {
       valid: false,
       error: {
-        message: errorString,
+        message: result["err"]["msg"],
         positions: positions,
       },
     };
-- 
GitLab