Skip to content

Commit

Permalink
Reducing memory foot-print by 1) removing XPath property and creating…
Browse files Browse the repository at this point in the history
… it on the fly 2) Number of recursions can be set by default three. The higher the more possible XPaths and XsdElements are being created
  • Loading branch information
svanteschubert committed Apr 18, 2023
1 parent 6d9231c commit 5f8bca8
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 41 deletions.
27 changes: 19 additions & 8 deletions src/main/java/com/compare/xsd/comparison/XsdComparer.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,24 @@ public XsdComparer(String oldDocument, String newDocument) {
* @param reporterType determines the way the text output is provided
*/
public XsdComparer(String oldDocument, String newDocument, TextReport.implementation reporterType) {
this(oldDocument,newDocument, reporterType, 3);
}
/**
* Initialize a new instance of {@link XsdComparer}.
*
* @param oldDocument Set the old document.
* @param newDocument Set the new document.
* @param reporterType determines the way the text output is provided
* @param duplicatedAnchestorNoAllowed Set the recursion depth by limiting same anchestors
*/
public XsdComparer(String oldDocument, String newDocument, TextReport.implementation reporterType, int duplicatedAnchestorNoAllowed) {
Assert.notNull(oldDocument, "oldDocument cannot be null");
Assert.notNull(newDocument, "newDocument cannot be null");
this.textReport = getTextReporter(reporterType);
XsdLoader xsdLoader = new XsdLoader(null);
this.oldDocument = xsdLoader.load(new File(oldDocument));
this.oldDocument = xsdLoader.load(new File(oldDocument), duplicatedAnchestorNoAllowed);
log.debug("Finished loading original grammar!");
this.newDocument = xsdLoader.load(new File(newDocument));
this.newDocument = xsdLoader.load(new File(newDocument), duplicatedAnchestorNoAllowed);
log.debug("Finished loading new grammar!");
}

Expand Down Expand Up @@ -149,7 +160,7 @@ private void compareAbstractElementNodes(AbstractXsdElementNode oldNode, Abstrac
increaseAncestor_OldGrammar(oldNode);
increaseAncestor_NewGrammar(newNode);
Assert.notNull(newNode, "newNode cannot be null");
assert(oldNode.xpath.equals(newNode.xpath));
assert(oldNode.getXPath().equals(newNode.getXPath()));

List<XsdElement> oldElementChildren;
List<XsdElement> newElementChildren;
Expand All @@ -172,7 +183,7 @@ private void compareAbstractElementNodes(AbstractXsdElementNode oldNode, Abstrac
XsdElement newChildElement = newNode.findElement(oldElementChild.getName(), "1: old child in new children: ");
compareXsdElementProperties(oldElementChild, newChildElement);
compareAbstractElementNodes(oldElementChild, newChildElement);
assert(!(oldElementChild == null || newChildElement == null || !oldElementChild.xpath.equals(newChildElement.xpath)));
assert(!(oldElementChild == null || newChildElement == null || !oldElementChild.getXPath().equals(newChildElement.getXPath())));
} catch (NodeNotFoundException ex) {
assert(newNode.getXPath().equals(oldNode.getXPath()));
removed++;
Expand Down Expand Up @@ -230,7 +241,7 @@ private static Boolean alreadyCompared(AbstractXsdElementNode element, Map<Strin
if(element instanceof XsdElement){
String elementID = ((XsdElement) element).getUniqueId();
Integer ancestorCount = ancestorMap.get(elementID);
if(ancestorCount != null && ancestorCount > 2){
if(ancestorCount != null && ancestorCount == element.getDocument().duplicatedAnchestorNoAllowed){
return Boolean.TRUE;
}else{
return Boolean.FALSE;
Expand Down Expand Up @@ -272,7 +283,7 @@ private static void decreaseAncestor(AbstractXsdElementNode element, Map<String,
* @param newNode Set the new XSD element.
*/
private void compareXsdElementProperties(XsdElement oldNode, XsdElement newNode) {
assert(oldNode.xpath.equals(newNode.xpath));
assert(oldNode.getXPath().equals(newNode.getXPath()));

List<XsdAttribute> oldNodeAttributes = new ArrayList<>(oldNode.getAttributes()); //take a copy as the actual list might be modified during comparison
List<XsdAttribute> newNodeAttributes = new ArrayList<>(newNode.getAttributes());
Expand Down Expand Up @@ -349,11 +360,11 @@ private void compareProperties(XsdNode oldNode, XsdNode newNode) {
modified++;
textReport.addChange(change);
if(newNode instanceof XsdElement){
assert(( ((AbstractXsdNode) newNode).xpath != null && ((AbstractXsdNode) newNode).xpath != null && ((AbstractXsdNode) newNode).xpath.equals(((AbstractXsdNode) oldNode).xpath)));
assert(( ((AbstractXsdNode) newNode).getXPath() != null && ((AbstractXsdNode) newNode).getXPath() != null && ((AbstractXsdNode) newNode).getXPath().equals(((AbstractXsdNode) oldNode).getXPath())));
assert(((XsdElement) newNode).getParent() != null);

}else if(newNode instanceof XsdAttribute) {
assert ((((AbstractXsdNode) newNode).getParent().xpath != null && ((AbstractXsdNode) newNode).getParent().xpath != null && ((AbstractXsdNode) newNode).getParent().xpath.equals(((AbstractXsdNode) oldNode).getParent().xpath)));
assert ((((AbstractXsdNode) newNode).getParent().getXPath() != null && ((AbstractXsdNode) newNode).getParent().getXPath() != null && ((AbstractXsdNode) newNode).getParent().getXPath().equals(((AbstractXsdNode) oldNode).getParent().getXPath())));
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/com/compare/xsd/comparison/XsdLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ public XsdDocument chooseAndLoad() {
* @return Returns the loaded {@link XsdDocument}.
*/
public XsdDocument load(File file) {
return load(file, 3);
}

/**
* Load the given XSD file into a {@link XsdDocument}.
*
* @param file Set the XSD file to load.
* @param duplicatedAnchestorNoAllowed Set the recursion depth by limiting same anchestors *
* @return Returns the loaded {@link XsdDocument}.
*/
public XsdDocument load(File file, int duplicatedAnchestorNoAllowed) {
Assert.notNull(file, "file cannot be null");

// verify if the file exists
Expand All @@ -81,6 +92,7 @@ public XsdDocument load(File file) {
}

log.debug("Loading xsd file " + file);
return new XsdDocument(file);
return new XsdDocument(file, duplicatedAnchestorNoAllowed);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public abstract class AbstractXsdNode implements XsdNode {
break;
*/
public String xpath;
protected StringBuilder sb = new StringBuilder();
protected String name;
protected String typeNamespace;
protected String typeName;
Expand Down Expand Up @@ -139,7 +139,20 @@ public Image getModificationColor() {

@Override
public String getXPath() {
return xpath;
if(this instanceof XsdDocument){
return "/";
}else{
StringBuilder sb = getXPath(this.parent).append("/").append(this.name);
return sb.toString();
}
}

public StringBuilder getXPath(AbstractXsdNode node) {
if(node instanceof XsdDocument){
return ((XsdDocument) node).getStringBuilder();
}else {
return getXPath(node.parent).append("/").append(node.name);
}
}

//endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public List<XsdNode> getNodes() {

@Override
public String getXPath() {
return parent.xpath + "/@" + getName();
return parent.getXPath() + "/@" + getName();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,14 @@
import javafx.scene.image.Image;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.xerces.dom.DOMXSImplementationSourceImpl;
import org.apache.xerces.impl.xs.XSElementDecl;
import org.apache.xerces.impl.xs.XSLoaderImpl;
import org.apache.xerces.impl.xs.XSModelImpl;
import org.apache.xerces.xs.*;
import org.springframework.util.Assert;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

@EqualsAndHashCode(callSuper = true)
Expand All @@ -34,7 +22,9 @@ public class XsdDocument extends AbstractXsdElementNode {
public Stack<String> xPathStack = new Stack<>();
public Map<String, Integer> ancestorCountByelementIO = new HashMap();
private DocumentBuilder builder;

public int duplicatedAnchestorNoAllowed = 2;
private StringBuilder sb;
private static final String XPATH_ROOT = "/";
//region Constructors

/**
Expand All @@ -45,7 +35,24 @@ public class XsdDocument extends AbstractXsdElementNode {
public XsdDocument(File file) {
Assert.notNull(file, "file cannot be null");
this.file = file;
init();
}

public StringBuilder getStringBuilder() {
sb.delete(0, sb.length());
return sb;
}

/**
* Initialize a new instance of {@link XsdDocument}.
*
* @param file Set the XSD file to load.
* @param duplicatedAnchestorNoAllowed Set the recursion depth by limiting same anchestors
*/
public XsdDocument(File file, int duplicatedAnchestorNoAllowed) {
Assert.notNull(file, "file cannot be null");
this.file = file;
this.duplicatedAnchestorNoAllowed = duplicatedAnchestorNoAllowed;
init();
}

Expand Down Expand Up @@ -77,7 +84,7 @@ public Image getModificationColor() {

@Override
public String getXPath() {
return xpath;
return XPATH_ROOT;
}


Expand All @@ -101,12 +108,11 @@ private void init() {
var loader = new XSLoaderImpl();
// XSD is now parsed by Xerces the XSModelImpl holds all information
XSModelImpl model = (XSModelImpl) loader.loadURI(file.getAbsolutePath());

// start to bootstrap our model by accessing the map of all root elements
XSNamedMap elements = model.getComponents(XSConstants.ELEMENT_DECLARATION);

this.sb = new StringBuilder();
this.name = file.getName();
this.xpath = "//";

// check every root element
for (Object item : elements.values()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private XsdElement(XSElementDeclaration elementDecl, XsdDocument document) {
this.name = elementDecl.getName();
this.typeNamespace = loadNamespace(this);
document.xPathStack.push(name);
this.xpath = createXPath(this.document.xPathStack);
// this.xpath = createXPath(this.document.xPathStack);
}

/**
Expand Down Expand Up @@ -92,7 +92,8 @@ private XsdElement(XSParticle particle, XsdElement parent, short compositor) {
this.typeNamespace = loadNamespace(this);
this.document = parent.getDocument();
this.document.xPathStack.push(name);
this.xpath = createXPath(this.document.xPathStack);
//log.debug("Creating new element " + this.name + " at " + xpath);
//System.err.println("Creating new element: " + getXPath());
}

/**
Expand Down Expand Up @@ -150,21 +151,13 @@ public static XsdElement newXsdElement(XSParticle particle, XsdElement parent, s
}
}

private static String createXPath(Stack<String> ancestorNames){
StringBuilder xPath = new StringBuilder();
for(String name : ancestorNames){
xPath.append("/").append(name);
}
return xPath.toString();
}

public String getUniqueId(){
return XsdElement.getUniqueId(elementDecl, minOccurrence, maxOccurrence);
}

static private Boolean hasTooManySameAncestors(XsdDocument document, String elementID){
Integer count = document.ancestorCountByelementIO.get(elementID);
if(count != null && count > 3){
if(count != null && count == document.duplicatedAnchestorNoAllowed){
return Boolean.TRUE;
}else{
return Boolean.FALSE;
Expand Down
67 changes: 66 additions & 1 deletion src/test/java/com/compare/xsd/comparison/XsdComparerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -114,12 +115,76 @@ public void multiComparisonTest() throws IOException {
assertTrue(compareCorrect,"\nRegression test fails as reference was different!\nNote: If the test fails due to a new output (e.g. programming update) copy the new result over the old reference:\n\t" + TARGET_DIR + "\n\t\tto" + "\n\t" + REFERENCES_DIR);
}

/************************************************************************************************/
/* NOTE: Following compare of UBL 2.1 with UBL 2.3 will throw an out-of-memory exception! ****/
/************************************************************************************************/
/*
private static final String UBL_2_1_XSD_PATH = XSD_DIR + "UBL-2.1" + File.separator + "xsd" + File.separator + "maindoc" + File.separator;
private static final String UBL_2_3_XSD_PATH = XSD_DIR + "UBL-2.3" + File.separator + "xsd" + File.separator + "maindoc" + File.separator;
private static final String EMPTY_XSD_FILE_PATH = XSD_DIR + "empty.xsd";
@Test
/** Compares several XSDs pairs and compares the result with the saved reference. */
/*
public void UBLComparisonTest() throws IOException {
Boolean compareCorrect = Boolean.TRUE;
// Creates a new File instance by converting the given pathname string
// into an abstract pathname
File directoryUBL21 = new File(UBL_2_1_XSD_PATH);
File directoryUBL23 = new File(UBL_2_3_XSD_PATH);
// Populates the array with names of files and directories
String[] fileNames_Ubl2_1 = directoryUBL21.list();
Set<String> fileNames_Ubl2_1Set = Set.of(fileNames_Ubl2_1);
String[] fileNames_Ubl2_3 = directoryUBL23.list();
Set<String> fileNames_Ubl2_3Set = new TreeSet<>(Set.of(fileNames_Ubl2_3));
String xsdFilePath_ubl2_1;
// For each pathname in the fileNames array
for (String xsdFileName_ubl2_3 : fileNames_Ubl2_3Set) {
// Print the names of files and directories
xsdFilePath_ubl2_1 = xsdFilePath_ubl2_1(xsdFileName_ubl2_3, fileNames_Ubl2_1Set);
if(xsdFilePath_ubl2_1 != null){
System.out.println("\nComparing:\n" + xsdFilePath_ubl2_1.substring(xsdFilePath_ubl2_1.lastIndexOf(File.separator) + 1, xsdFilePath_ubl2_1.length()) + "\nwith\n" + xsdFileName_ubl2_3);
for(TextReport.implementation reportType : TextReport.implementation.values()){
compareCorrect &= compareTwoXsdGrammars(xsdFilePath_ubl2_1, UBL_2_3_XSD_PATH + xsdFileName_ubl2_3, reportType, 1);
}
}
}
assertTrue(compareCorrect,"\nRegression test fails as reference was different!\nNote: If the test fails due to a new output (e.g. programming update) copy the new result over the old reference:\n\t" + TARGET_DIR + "\n\t\tto" + "\n\t" + REFERENCES_DIR);
}
/** Returning the correspondent UBL 2.1 file path according to the UBL 2.3 file name (or if not existent the file to an empty XSD file
* for instance the input file name "UBL-CatalogueRequest-2.3.xsd" will look for "UBL-CatalogueRequest-2.1.xsd" in the UBL 2.1 directory.
* */
/*
private static String xsdFilePath_ubl2_1(String xsdFileName_ubl2_3, Set fileNameSet_Ubl2_1){
String xsdFilePath_ubl2_1 = null;
int i = xsdFileName_ubl2_3.lastIndexOf("-2.3.xsd");
String xsdFileName_ubl2_1 = xsdFileName_ubl2_3.substring(0, xsdFileName_ubl2_3.lastIndexOf("-2.3.xsd")) + "-2.1.xsd";
if(fileNameSet_Ubl2_1.contains(xsdFileName_ubl2_1)){
xsdFilePath_ubl2_1 = UBL_2_1_XSD_PATH + xsdFileName_ubl2_1;
}else{
System.out.println("New XSD file in UBL 2.3: " + xsdFileName_ubl2_3);
//xsdFilePath_ubl2_1 = EMPTY_XSD_FILE_PATH;
}
return xsdFilePath_ubl2_1;
}
*/


/** Comparing two XSD grammar with a specific text report for the output*/
private boolean compareTwoXsdGrammars(String newXsd, String oldXsd, TextReport.implementation reportType) throws IOException {
return compareTwoXsdGrammars(newXsd, oldXsd, reportType, 3);
}

private boolean compareTwoXsdGrammars(String newXsd, String oldXsd, TextReport.implementation reportType, int duplicatedAnchestorNoAllowed) throws IOException {
Boolean compareCorrect = Boolean.TRUE;
String reportName = getReportName(newXsd, oldXsd, reportType);
XsdComparer comparer = new XsdComparer(newXsd, oldXsd, reportType);
XsdComparer comparer = new XsdComparer(newXsd, oldXsd, reportType, duplicatedAnchestorNoAllowed);
String result = comparer.compareAsString();
System.out.println(result);
// if you change the programming, update the reference by copying new result as new reference!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void testFileConstructor_shouldRootElementContainExpectedChildElementsWhe
@Test
public void shouldReturnTheExpectedResultWhenGetXPathIsCalled() {
//GIVEN
final String expectedResult = "//";
final String expectedResult = "/";

//WHEN
XsdDocument result = new XsdDocument(file);
Expand Down
7 changes: 7 additions & 0 deletions src/test/resources/xsd/empty.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="empty"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
</xsd:schema>

0 comments on commit 5f8bca8

Please sign in to comment.