Skip to content

Commit 7f10375

Browse files
committed
Try recovery from exceptions #6
1 parent 4c5e7bf commit 7f10375

File tree

10 files changed

+430
-325
lines changed

10 files changed

+430
-325
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ java io.fixprotocol.xml.XmlDiff <in-file1> <in-file2> [output-file]
2222
```
2323
If the output file is not provided, then results go to the console.
2424

25+
Optionally, argument `-e <event-filename>` can direct errors to a JSON file suitable for UI rendering.
26+
27+
Optionally, argument `-u` treats XML nodes as unordered so moves among children are not considered significant. Otherwise, a move will result in an add and a remove operation.
28+
2529
## Merge
2630

2731
The XmlMerge utility takes a base XML file and a difference file and merges the two to produce an new XML file.
@@ -31,9 +35,17 @@ To run the merge utility, run this command line:
3135
```
3236
java io.fixprotocol.xml.XmlMerge <base-xml-file> <diff-file> <output-xml-file>
3337
```
38+
Optionally, argument `-e <event-filename>` can direct errors to a JSON file suitable for UI rendering.
39+
40+
The merge utility attempts to continue even if errors occur. Examples of logged errors:
41+
42+
```
43+
11:12:53.019 [main] ERROR io.fixprotocol.xml.XmlMerge - Target not found for add; /foo
44+
11:12:53.035 [main] ERROR io.fixprotocol.xml.XmlMerge - Invalid XPath expression for remove; /aaa/fff[
45+
```
3446

3547
## License
36-
© Copyright 2017-2019 FIX Protocol Limited
48+
© Copyright 2017-2021 FIX Protocol Limited
3749

3850
Licensed under the Apache License, Version 2.0 (the "License");
3951
you may not use this file except in compliance with the License.

pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<groupId>io.fixprotocol.xml</groupId>
66
<artifactId>diff-merge</artifactId>
77
<description>Diff/merge tool for XML files</description>
8-
<version>1.4.1-SNAPSHOT</version>
8+
<version>1.5.1-SNAPSHOT</version>
99
<name>${project.groupId}:${project.artifactId}</name>
1010

1111
<prerequisites>
@@ -17,6 +17,7 @@
1717
<java.version>11</java.version>
1818
<junit.version>5.7.1</junit.version>
1919
<log4j.version>2.14.0</log4j.version>
20+
<orchestra.version>1.6.10</orchestra.version>
2021
<saxon.version>10.3</saxon.version>
2122
<ignoreSigningInformation>true</ignoreSigningInformation>
2223
</properties>
@@ -64,6 +65,11 @@
6465
<artifactId>Saxon-HE</artifactId>
6566
<version>${saxon.version}</version>
6667
</dependency>
68+
<dependency>
69+
<groupId>io.fixprotocol.orchestra</groupId>
70+
<artifactId>orchestra-common</artifactId>
71+
<version>${orchestra.version}</version>
72+
</dependency>
6773
<dependency>
6874
<groupId>org.apache.logging.log4j</groupId>
6975
<artifactId>log4j-api</artifactId>

src/main/java/io/fixprotocol/xml/CustomNamespaceContext.java

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,13 @@ public CustomNamespaceContext() {
3232
namespaces.put("xml", XMLConstants.XML_NS_URI);
3333
namespaces.put("xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
3434
}
35-
36-
public void populate(Document doc) {
37-
final Element baselineRoot = doc.getDocumentElement();
38-
final NamedNodeMap rootAttributes = baselineRoot.getAttributes();
39-
40-
for (int i = 0; i < rootAttributes.getLength(); i++) {
41-
final Attr attr = (Attr) rootAttributes.item(i);
42-
final String prefix = attr.getPrefix();
43-
if ("xmlns".equals(prefix)) {
44-
register(attr.getLocalName(), attr.getValue());
45-
} else if ("xmlns".equals(attr.getLocalName())) {
46-
// default namespace
47-
register(XMLConstants.DEFAULT_NS_PREFIX, attr.getValue());
48-
}
49-
}
50-
}
5135

36+
@Override
5237
public String getNamespaceURI(String prefix) {
5338
if (prefix == null) {
5439
throw new NullPointerException("Null prefix");
5540
}
56-
String uri = namespaces.get(prefix);
41+
final String uri = namespaces.get(prefix);
5742
if (uri != null) {
5843
return uri;
5944
} else {
@@ -62,17 +47,35 @@ public String getNamespaceURI(String prefix) {
6247
}
6348

6449
// This method isn't necessary for XPath processing.
50+
@Override
6551
public String getPrefix(String uri) {
6652
throw new UnsupportedOperationException();
6753
}
6854

6955
// This method isn't necessary for XPath processing either.
56+
@Override
7057
public Iterator<String> getPrefixes(String uri) {
7158
throw new UnsupportedOperationException();
7259
}
7360

61+
public void populate(Document doc) {
62+
final Element baselineRoot = doc.getDocumentElement();
63+
final NamedNodeMap rootAttributes = baselineRoot.getAttributes();
64+
65+
for (int i = 0; i < rootAttributes.getLength(); i++) {
66+
final Attr attr = (Attr) rootAttributes.item(i);
67+
final String prefix = attr.getPrefix();
68+
if ("xmlns".equals(prefix)) {
69+
register(attr.getLocalName(), attr.getValue());
70+
} else if ("xmlns".equals(attr.getLocalName())) {
71+
// default namespace
72+
register(XMLConstants.DEFAULT_NS_PREFIX, attr.getValue());
73+
}
74+
}
75+
}
76+
7477
public void register(String prefix, String uri) {
7578
namespaces.put(prefix, uri);
7679
}
7780

78-
}
81+
}

src/main/java/io/fixprotocol/xml/PatchOpsListener.java

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
*/
1515
package io.fixprotocol.xml;
1616

17+
import static io.fixprotocol.xml.XmlDiffListener.Event.Pos.append;
1718
import java.io.IOException;
1819
import java.io.OutputStream;
1920
import java.io.OutputStreamWriter;
2021
import java.nio.charset.StandardCharsets;
2122
import java.util.concurrent.atomic.AtomicBoolean;
22-
2323
import javax.xml.parsers.DocumentBuilder;
2424
import javax.xml.parsers.DocumentBuilderFactory;
2525
import javax.xml.parsers.ParserConfigurationException;
@@ -29,18 +29,15 @@
2929
import javax.xml.transform.TransformerFactory;
3030
import javax.xml.transform.dom.DOMSource;
3131
import javax.xml.transform.stream.StreamResult;
32-
3332
import org.w3c.dom.Attr;
3433
import org.w3c.dom.Document;
3534
import org.w3c.dom.Element;
3635
import org.w3c.dom.Node;
3736
import org.w3c.dom.Text;
3837

39-
import static io.fixprotocol.xml.XmlDiffListener.Event.Pos.*;
40-
4138
/**
4239
* Writes XML diffs as patch operations specified by IETF RFC 5261
43-
*
40+
*
4441
* @author Don Mendelson
4542
* @see <a href="https://tools.ietf.org/html/rfc5261">An Extensible Markup Language (XML) Patch
4643
* Operations Framework Utilizing XML Path Language (XPath) Selectors</a>
@@ -54,41 +51,42 @@ public class PatchOpsListener implements XmlDiffListener {
5451

5552
/**
5653
* Constructs a listener with an output stream
54+
*
5755
* @throws IOException if an IO error occurs
5856
* @throws ParserConfigurationException if a configuration error occurs
5957
* @throws TransformerConfigurationException if a configuration error occurs
60-
*
58+
*
6159
*/
6260
public PatchOpsListener(OutputStream out)
6361
throws IOException, ParserConfigurationException, TransformerConfigurationException {
6462
writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
6563

66-
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
64+
final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
6765
dbFactory.setNamespaceAware(true);
68-
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
66+
final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
6967
document = dBuilder.newDocument();
7068
rootElement = document.createElement("diff");
7169
document.appendChild(rootElement);
7270
}
7371

7472
/*
7573
* (non-Javadoc)
76-
*
74+
*
7775
* @see java.util.function.Consumer#accept(java.lang.Object)
7876
*/
7977
@Override
8078
public void accept(Event t) {
8179

8280
switch (t.getDifference()) {
8381
case ADD:
84-
Element addElement = document.createElement("add");
82+
final Element addElement = document.createElement("add");
8583
rootElement.appendChild(addElement);
8684

8785
if (t.getValue() instanceof Attr) {
8886
// add attribute
8987
addElement.setAttribute("sel", t.getXpath());
9088
addElement.setAttribute("type", "@" + t.getValue().getNodeName());
91-
Text textNode = document.createTextNode(t.getValue().getNodeValue());
89+
final Text textNode = document.createTextNode(t.getValue().getNodeValue());
9290
addElement.appendChild(textNode);
9391
} else if (t.getValue() instanceof Element) {
9492
// add element
@@ -97,30 +95,30 @@ public void accept(Event t) {
9795
addElement.setAttribute("pos", t.getPos().toString());
9896
}
9997
// will import child text node if it exists (deep copy)
100-
Element newValue = (Element) document.importNode(t.getValue(), true);
98+
final Element newValue = (Element) document.importNode(t.getValue(), true);
10199
addElement.appendChild(newValue);
102100
}
103101

104102
break;
105103
case REPLACE:
106-
Element replaceElement = document.createElement("replace");
104+
final Element replaceElement = document.createElement("replace");
107105
rootElement.appendChild(replaceElement);
108106

109107
if (t.getValue() instanceof Attr) {
110108
// replace attribute
111109
replaceElement.setAttribute("sel", t.getXpath());
112-
Text textNode = document.createTextNode(t.getValue().getNodeValue());
110+
final Text textNode = document.createTextNode(t.getValue().getNodeValue());
113111
replaceElement.appendChild(textNode);
114112
} else {
115113
// replace element
116114
replaceElement.setAttribute("sel", t.getXpath());
117115
// will import child text node if it exists
118-
Node newValue = document.importNode(t.getValue(), true);
116+
final Node newValue = document.importNode(t.getValue(), true);
119117
replaceElement.appendChild(newValue);
120118
}
121119
break;
122120
case REMOVE:
123-
Element removeElement = document.createElement("remove");
121+
final Element removeElement = document.createElement("remove");
124122
rootElement.appendChild(removeElement);
125123
removeElement.setAttribute("sel", t.getXpath());
126124
break;
@@ -131,18 +129,18 @@ public void accept(Event t) {
131129

132130
/*
133131
* (non-Javadoc)
134-
*
132+
*
135133
* @see java.lang.AutoCloseable#close()
136134
*/
137135
@Override
138136
public void close() throws Exception {
139137
// Idempotent - only close once
140138
if (isClosed.compareAndSet(false, true)) {
141-
TransformerFactory transformerFactory = TransformerFactory.newInstance();
142-
Transformer transformer = transformerFactory.newTransformer();
139+
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
140+
final Transformer transformer = transformerFactory.newTransformer();
143141
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
144-
DOMSource source = new DOMSource(document);
145-
StreamResult result = new StreamResult(writer);
142+
final DOMSource source = new DOMSource(document);
143+
final StreamResult result = new StreamResult(writer);
146144
transformer.transform(source, result);
147145
writer.close();
148146
}

0 commit comments

Comments
 (0)