diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /qadevOOo/runner/util/XMLTools.java | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream/1%7.0.4.tar.xz libreoffice-upstream/1%7.0.4.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | qadevOOo/runner/util/XMLTools.java | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/qadevOOo/runner/util/XMLTools.java b/qadevOOo/runner/util/XMLTools.java new file mode 100644 index 000000000..fc821b9ff --- /dev/null +++ b/qadevOOo/runner/util/XMLTools.java @@ -0,0 +1,669 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package util; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +import com.sun.star.beans.PropertyValue; +import com.sun.star.xml.sax.XAttributeList; +import com.sun.star.xml.sax.XDocumentHandler; +import com.sun.star.xml.sax.XLocator; + + +public class XMLTools { + + /** + * The implementation of <code>com.sun.star.xml.sax.XAttributeList</code> + * where attributes and their values can be added. + */ + public static class AttributeList implements XAttributeList { + private static class Attribute { + public String Name ; + public String Type ; + public String Value ; + } + private final HashMap<String, Attribute> attrByName = new HashMap<String, Attribute>() ; + private final ArrayList<Attribute> attributes = new ArrayList<Attribute>() ; + + /** + * Creates a class instance. + */ + public AttributeList() {} + + private AttributeList(XAttributeList list) { + if (list == null) return ; + for (short i = 0; i < list.getLength(); i++) { + add(list.getNameByIndex(i), list.getTypeByIndex(i), + list.getValueByIndex(i)) ; + } + } + + /** + * Adds an attribute with type and value specified. + * @param name The attribute name. + * @param type Value type (usually 'CDATA' used). + * @param value Attribute value. + */ + public void add(String name, String type, String value) { + Attribute attr = new Attribute() ; + attr.Name = name ; + attr.Type = type ; + attr.Value = value ; + attributes.add(attr) ; + attrByName.put(attr.Name, attr) ; + } + + + + + + /*************************************** + * XAttributeList methods + ****************************************/ + + public short getLength() { + return (short) attributes.size() ; + } + + public String getNameByIndex(short idx) { + String name = attributes.get(idx).Name ; + return name ; + } + + public String getTypeByIndex(short idx) { + String type = attributes.get(idx).Type ; + return type; + } + + public String getTypeByName(String name) { + String type = attrByName.get(name).Type ; + return type; + } + public String getValueByIndex(short idx) { + String value = attributes.get(idx).Value ; + return value; + } + + public String getValueByName(String name) { + String value = attrByName.get(name).Value ; + return value; + } + } + + /** + * This class writes all XML data handled into a stream specified + * in the constructor. + */ + private static class XMLWriter implements XDocumentHandler { + private PrintWriter _log = null ; + private String align = "" ; + + /** + * Creates a SAX handler which writes all XML data + * handled into a <code>log</code> stream specified. + */ + private XMLWriter(PrintWriter log) { + _log = log ; + } + + /** + * Creates a SAX handler which does nothing. + */ + public XMLWriter() { + } + + public void processingInstruction(String appl, String data) { + if (_log == null) return ; + _log.println(align + "<?" + appl + " " + data + "?>") ; + } + public void startDocument() { + if (_log == null) return ; + _log.println("START DOCUMENT:") ; + } + public void endDocument() { + if (_log == null) return ; + _log.println("END DOCUMENT:") ; + } + public void setDocumentLocator(XLocator loc) { + if (_log == null) return ; + _log.println("DOCUMENT LOCATOR: ('" + loc.getPublicId() + + "','" + loc.getSystemId() + "')") ; + } + public void startElement(String name, XAttributeList attr) { + if (_log == null) return ; + _log.print(align + "<" + name + " ") ; + if (attr != null) { + short attrLen = attr.getLength() ; + for (short i = 0; i < attrLen; i++) { + if (i != 0) _log.print(align + " ") ; + _log.print(attr.getNameByIndex(i) + "[" + + attr.getTypeByIndex(i) + "]=\"" + + attr.getValueByIndex(i) + "\"") ; + if (i+1 != attrLen) { + _log.println() ; + } + } + } + _log.println(">") ; + + align += " " ; + } + + public void endElement(String name) { + if (_log == null) return ; + align = align.substring(3) ; + _log.println(align + "</" + name + ">") ; + } + + public void characters(String chars) { + if (_log == null) return ; + _log.println(align + chars) ; + } + public void ignorableWhitespace(String sp) { + if (_log == null) return ; + _log.println(sp) ; + } + } + + /** + * Checks if the XML structure is well formed (i.e. all tags opened must be + * closed and all tags opened inside a tag must be closed + * inside the same tag). It also checks parameters passed. + * If any collisions found appropriate error message is + * output into a stream specified. No XML data output, i.e. + * no output will be performed if no errors occur.<p> + * After document is completed there is a way to check + * if the XML data and structure was valid. + */ + private static class XMLWellFormChecker extends XMLWriter { + private boolean docStarted = false ; + private boolean docEnded = false ; + ArrayList<String> tagStack = new ArrayList<String>() ; + private boolean wellFormed = true ; + private boolean noOtherErrors = true ; + PrintWriter log = null ; + private boolean printXMLData = false ; + + private XMLWellFormChecker(PrintWriter log) { + super() ; + this.log = log ; + } + + private XMLWellFormChecker(PrintWriter log_, boolean printXMLData) { + super(printXMLData ? log_ : null) ; + this.printXMLData = printXMLData ; + this.log = log_ ; + } + + /** + * Reset all values. This is important e.g. for test of XFilter + * interface, where 'filter()' method is started twice. + */ + void reset() { + docStarted = false ; + docEnded = false ; + tagStack = new ArrayList<String>() ; + wellFormed = true ; + noOtherErrors = true ; + printXMLData = false ; + } + + @Override + public void startDocument() { + super.startDocument(); + + if (docStarted) { + printError("Document is started twice.") ; + wellFormed = false ; + } + + docStarted = true ; + } + @Override + public void endDocument() { + super.endDocument(); + if (!docStarted) { + wellFormed = false ; + printError("Document ended but not started.") ; + } + docEnded = true ; + } + @Override + public void startElement(String name, XAttributeList attr) { + super.startElement(name, attr); + if (attr == null) { + printError("attribute list passed as parameter to startElement()"+ + " method has null value for tag <" + name + ">") ; + noOtherErrors = false ; + } + tagStack.add(0, name) ; + } + @Override + public void endElement(String name) { + super.endElement(name); + if (wellFormed) { + if (tagStack.isEmpty()) { + wellFormed = false ; + printError("No tags to close (bad closing tag </" + name + ">)") ; + } else { + String startTag = tagStack.get(0) ; + tagStack.remove(0) ; + if (!startTag.equals(name)) { + wellFormed = false ; + printError("Bad closing tag: </" + name + + ">; tag expected: </" + startTag + ">"); + } + } + } + } + + /** + * Checks if there were no errors during document handling. + * I.e. startDocument() and endDocument() must be called, + * XML must be well formed, parameters must be valid. + */ + public boolean isWellFormed() { + if (!docEnded) { + printError("Document was not ended.") ; + wellFormed = false ; + } + + return wellFormed && noOtherErrors ; + } + + /** + * Prints error message and all tags where error occurred inside. + * Also prints "Tag trace" in case if the full XML data isn't + * printed. + */ + void printError(String msg) { + log.println("!!! Error: " + msg) ; + if (printXMLData) return ; + log.println(" Tag trace :") ; + for (int i = 0; i < tagStack.size(); i++) { + String tag = tagStack.get(i) ; + log.println(" <" + tag + ">") ; + } + } + } + + /** + * Beside structure of XML this class also can check existence + * of tags, inner tags, and character data. After document + * completion there is a way to check if required tags and + * character data was found. If there any error occurs an + * appropriate message is output. + */ + public static class XMLTagsChecker extends XMLWellFormChecker { + private final HashMap<String,String> tags = new HashMap<String,String>() ; + private final HashMap<String,String> chars = new HashMap<String,String>() ; + private boolean allOK = true ; + + public XMLTagsChecker(PrintWriter log) { + super(log) ; + } + + /** + * Adds a tag name which must be contained in the XML data. + */ + public void addTag(String tag) { + tags.put(tag, "") ; + } + /** + * Adds a tag name which must be contained in the XML data and + * must be inside the tag with name <code>outerTag</code>. + */ + public void addTagEnclosed(String tag, String outerTag) { + tags.put(tag, outerTag) ; + } + + /** + * Adds a character data which must be contained in the XML data and + * must be inside the tag with name <code>outerTag</code>. + */ + public void addCharactersEnclosed(String ch, String outerTag) { + chars.put(ch, outerTag) ; + } + + @Override + public void startElement(String name, XAttributeList attrs) { + super.startElement(name, attrs) ; + if (tags.containsKey(name)) { + String outerTag = tags.get(name); + if (outerTag.length() != 0) { + boolean isInTag = false ; + for (int i = 0; i < tagStack.size(); i++) { + if (outerTag.equals(tagStack.get(i))) { + isInTag = true ; + break ; + } + } + if (!isInTag) { + printError("Required tag <" + name + "> found, but is not enclosed in tag <" + + outerTag + ">") ; + allOK = false ; + } + } + tags.remove(name) ; + } + } + + @Override + public void characters(String ch) { + super.characters(ch) ; + + if (chars.containsKey(ch)) { + String outerTag = chars.get(ch); + if (outerTag.length() != 0) { + boolean isInTag = false ; + for (int i = 0; i < tagStack.size(); i++) { + if (outerTag.equals(tagStack.get(i))) { + isInTag = true ; + break ; + } + } + if (!isInTag) { + printError("Required characters '" + ch + "' found, but are not enclosed in tag <" + + outerTag + ">") ; + allOK = false ; + } + } + chars.remove(ch) ; + } + } + + /** + * Checks if the XML data was valid and well formed and if + * all necessary tags and character data was found. + */ + public boolean checkTags() { + if (!isWellFormed()) + allOK = false ; + + Iterator<String> badTags = tags.keySet().iterator() ; + Iterator<String> badChars = chars.keySet().iterator() ; + + if (badTags.hasNext()) { + allOK = false ; + log.println("Required tags were not found in export :") ; + while(badTags.hasNext()) { + log.println(" <" + badTags.next() + ">") ; + } + } + if (badChars.hasNext()) { + allOK = false ; + log.println("Required characters were not found in export :") ; + while(badChars.hasNext()) { + log.println(" <" + badChars.next() + ">") ; + } + } + reset(); + return allOK ; + } + } + + /** + * Represents an XML tag which must be found in XML data written. + * This tag can contain only its name or tag name and attribute + * name, or attribute value additionally. + */ + public static class Tag { + private final String name; + private String[][] attrList = new String[0][3] ; + + /** + * Creates tag which has only a name. Attributes don't make sense. + * @param tagName The name of the tag. + */ + public Tag(String tagName) { + name = tagName ; + } + + /** + * Creates a tag with the name specified, which must have an + * attribute with the value specified. The type of value + * assumed to be 'CDATA'. + * @param tagName The name of the tag. + * @param attrName The name of attribute which must be contained + * in the tag. + * @param attrValue Attribute value. + */ + public Tag(String tagName, String attrName, String attrValue) { + name = tagName ; + attrList = new String[1][3] ; + attrList[0][0] = attrName ; + attrList[0][1] = "CDATA" ; + attrList[0][2] = attrValue ; + } + + /** + * Gets tag String description. + */ + @Override + public String toString() { + StringBuffer ret = new StringBuffer("<" + name); + for (int i = 0; i < attrList.length; i++) { + ret.append(" ").append(attrList[i][0]).append("="); + if (attrList[i][2] == null) { + ret.append("(not specified)"); + } else { + ret.append("\"").append(attrList[i][2]).append("\""); + } + } + ret.append(">"); + + return ret.toString(); + } + + private boolean checkAttr(int attrListIdx, XAttributeList list) { + short j = 0 ; + int listLen = list.getLength(); + while(j < listLen) { + if (attrList[attrListIdx][0].equals(list.getNameByIndex(j))) { + if (attrList[attrListIdx][2] == null) return true ; + return attrList[attrListIdx][2].equals(list.getValueByIndex(j)) ; + } + j++ ; + } + return false ; + } + + /** + * Checks if this tag matches tag passed in parameters. + * I.e. if tag specifies only its name it matches if names + * are equal (attributes don't make sense). If there are + * some attributes names specified in this tag method checks + * if all names present in attribute list <code>list</code> + * (attributes' values don't make sense). If attributes specified + * with values method checks if these attributes exist and + * have appropriate values. + */ + private boolean isMatchTo(String tagName, XAttributeList list) { + if (!name.equals(tagName)) return false; + boolean result = true ; + for (int i = 0; i < attrList.length; i++) { + result &= checkAttr(i, list) ; + } + return result ; + } + } + + /** + * Class realises extended XML data checking. It has possibilities + * to check if a tag exists, if it has some attributes with + * values, and if this tag is contained in another tag (which + * also can specify any attributes). It can check if some + * character data exists inside any tag specified. + */ + public static class XMLChecker extends XMLWellFormChecker { + private final HashSet<String> tagSet = new HashSet<String>() ; + private final ArrayList<Tag[]> tags = new ArrayList<Tag[]>() ; + private final ArrayList<Object[]> chars = new ArrayList<Object[]>() ; + private final ArrayList<String> tagStack = new ArrayList<String>() ; + private final ArrayList<AttributeList> attrStack = new ArrayList<AttributeList>() ; + + public XMLChecker(PrintWriter log, boolean writeXML) { + super(log, writeXML) ; + } + + public void addTag(Tag tag) { + tags.add(new Tag[] {tag, null}) ; + tagSet.add(tag.name) ; + } + + public void addTagEnclosed(Tag tag, Tag outerTag) { + tags.add(new Tag[] {tag, outerTag}) ; + tagSet.add(tag.name) ; + } + + + + public void addCharactersEnclosed(String ch, Tag outerTag) { + chars.add(new Object[] {ch.trim(), outerTag}) ; + } + + @Override + public void startElement(String name, XAttributeList attr) { + try { + super.startElement(name, attr); + + if (tagSet.contains(name)) { + for (int i = 0; i < tags.size(); i++) { + Tag[] tag = tags.get(i); + if (tag[0].isMatchTo(name, attr)) { + if (tag[1] == null) { + tags.remove(i--); + } else { + boolean isInStack = false ; + for (int j = 0; j < tagStack.size(); j++) { + if (tag[1].isMatchTo(tagStack.get(j), + attrStack.get(j))) { + + isInStack = true ; + break ; + } + } + if (isInStack) { + tags.remove(i--) ; + } + } + } + } + } + + tagStack.add(0, name) ; + attrStack.add(0, new AttributeList(attr)); + } catch (Exception e) { + e.printStackTrace(log); + } + } + + @Override + public void characters(String ch) { + super.characters(ch) ; + for (int i = 0; i < chars.size(); i++) { + Object[] chr = chars.get(i); + if (((String) chr[0]).equals(ch)) { + if (chr[1] == null) { + chars.remove(i--); + } else { + boolean isInStack = false ; + for (int j = 0; j < tagStack.size(); j++) { + if (((Tag) chr[1]).isMatchTo(tagStack.get(j), + attrStack.get(j))) { + + isInStack = true ; + break ; + } + } + if (isInStack) { + chars.remove(i--) ; + } + } + } + } + } + + @Override + public void endElement(String name) { + try { + super.endElement(name); + + if (tagStack.size() > 0) { + tagStack.remove(0) ; + attrStack.remove(0) ; + } + } catch(Exception e) { + e.printStackTrace(log) ; + } + } + + public boolean check() { + if (tags.size()> 0) { + log.println("!!! Error: Some tags were not found :") ; + for (int i = 0; i < tags.size(); i++) { + Tag[] tag = tags.get(i) ; + log.println(" Tag " + tag[0] + " was not found"); + if (tag[1] != null) + log.println(" inside tag " + tag[1]) ; + } + } + if (chars.size() > 0) { + log.println("!!! Error: Some character data blocks were not found :") ; + for (int i = 0; i < chars.size(); i++) { + Object[] ch = chars.get(i) ; + log.println(" Character data \"" + ch[0] + "\" was not found ") ; + if (ch[1] != null) + log.println(" inside tag " + ch[1]) ; + } + } + + if (!isWellFormed()) + log.println("!!! Some errors were found in XML structure") ; + + boolean result = tags.isEmpty() && chars.isEmpty() && isWellFormed(); + reset(); + return result; + } + } + + + + public static PropertyValue[] createMediaDescriptor(String[] propNames, Object[] values) { + PropertyValue[] props = new PropertyValue[propNames.length] ; + + for (int i = 0; i < props.length; i++) { + props[i] = new PropertyValue() ; + props[i].Name = propNames[i] ; + if (values != null && i < values.length) { + props[i].Value = values[i] ; + } + } + + return props ; + } + + + + +} |