-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #70 from damianszczepanik/xml-support
Added support for XMLProcessor
- Loading branch information
Showing
16 changed files
with
492 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,9 +40,7 @@ | |
<url>[email protected]:damianszczepanik/silencio.git</url> | ||
</scm> | ||
|
||
<description> | ||
Silencio is a tool for transforming and converting JSON files. | ||
</description> | ||
<description>Silencio is a tool for transforming and converting JSON, XML and Properties files.</description> | ||
|
||
<licenses> | ||
<license> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
src/main/java/pl/szczepanik/silencio/processors/XMLProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package pl.szczepanik.silencio.processors; | ||
|
||
import java.io.IOException; | ||
import java.io.Reader; | ||
import java.io.Writer; | ||
|
||
import javax.xml.parsers.DocumentBuilder; | ||
import javax.xml.parsers.DocumentBuilderFactory; | ||
import javax.xml.parsers.ParserConfigurationException; | ||
import javax.xml.transform.Transformer; | ||
import javax.xml.transform.TransformerException; | ||
import javax.xml.transform.TransformerFactory; | ||
import javax.xml.transform.dom.DOMSource; | ||
import javax.xml.transform.stream.StreamResult; | ||
|
||
import org.w3c.dom.Document; | ||
import org.w3c.dom.Element; | ||
import org.xml.sax.InputSource; | ||
import org.xml.sax.SAXException; | ||
|
||
import pl.szczepanik.silencio.api.Format; | ||
import pl.szczepanik.silencio.core.ProcessorException; | ||
import pl.szczepanik.silencio.processors.visitors.XMLVisitor; | ||
|
||
/** | ||
* Provides processor that supports XML format. | ||
* | ||
* @author Damian Szczepanik (damianszczepanik@github) | ||
*/ | ||
public class XMLProcessor extends AbstractProcessor { | ||
|
||
private final XMLVisitor visitor = new XMLVisitor(); | ||
|
||
private Document document = null; | ||
|
||
/** Creates new processor for XML file. */ | ||
public XMLProcessor() { | ||
super(Format.XML); | ||
} | ||
|
||
@Override | ||
public void realLoad(Reader reader) { | ||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | ||
try { | ||
DocumentBuilder builder = factory.newDocumentBuilder(); | ||
document = builder.parse(new InputSource(reader)); | ||
} catch (ParserConfigurationException | SAXException | IOException e) { | ||
throw new ProcessorException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public void realProcess() { | ||
visitor.setConfiguration(configuration); | ||
Element rootElement = document.getDocumentElement(); | ||
// optional, but recommended | ||
// http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work | ||
rootElement.normalize(); | ||
|
||
visitor.processXML(rootElement); | ||
} | ||
|
||
@Override | ||
public void realWrite(Writer writer) { | ||
TransformerFactory transformerFactory = TransformerFactory.newInstance(); | ||
try { | ||
Transformer transformer = transformerFactory.newTransformer(); | ||
DOMSource source = new DOMSource(document); | ||
StreamResult result = new StreamResult(writer); | ||
transformer.transform(source, result); | ||
} catch (TransformerException e) { | ||
throw new ProcessorException(e); | ||
} | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
src/main/java/pl/szczepanik/silencio/processors/visitors/XMLVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package pl.szczepanik.silencio.processors.visitors; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
import org.w3c.dom.Element; | ||
import org.w3c.dom.NamedNodeMap; | ||
import org.w3c.dom.Node; | ||
import org.w3c.dom.NodeList; | ||
|
||
import pl.szczepanik.silencio.core.Key; | ||
import pl.szczepanik.silencio.core.ProcessorException; | ||
import pl.szczepanik.silencio.core.Value; | ||
|
||
/** | ||
* Iterates over XML nodes and calls {@link #processValue(Key, Object)} for each basic node. | ||
* | ||
* @author Damian Szczepanik (damianszczepanik@github) | ||
*/ | ||
public class XMLVisitor extends AbstractVisitor { | ||
|
||
static final String EXCEPTION_MESSAGE_NODE_TYPE_UNSUPPORTED = "Node with type %d, name '%s' and value '%s' is not supported"; | ||
|
||
/** | ||
* Process passed JSON map and iterates over each node. | ||
* | ||
* @param rootElement | ||
* XML root element to iterate over | ||
*/ | ||
public void processXML(Element rootElement) { | ||
processAttributes(rootElement.getAttributes()); | ||
processNodes(rootElement.getChildNodes()); | ||
} | ||
|
||
private void processNodes(NodeList nodeList) { | ||
for (int i = 0; i < nodeList.getLength(); i++) { | ||
Node node = nodeList.item(i); | ||
processNode(node); | ||
|
||
processAttributes(node.getAttributes()); | ||
processNodes(node.getChildNodes()); | ||
} | ||
} | ||
|
||
private void processAttributes(NamedNodeMap attributes) { | ||
int attributeLength = attributes == null ? 0 : attributes.getLength(); | ||
if (attributeLength != 0) { | ||
for (int i = 0; i < attributeLength; i++) { | ||
processNode(attributes.item(i)); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Process the node with its attributes but without its children. | ||
* | ||
* @param node | ||
* node to process | ||
*/ | ||
private void processNode(Node node) { | ||
node.normalize(); | ||
|
||
short type = node.getNodeType(); | ||
switch (type) { | ||
case Node.ELEMENT_NODE: | ||
case Node.ATTRIBUTE_NODE: | ||
convertNodeIfNeeded(node, node.getNodeName()); | ||
break; | ||
case Node.TEXT_NODE: | ||
convertNodeIfNeeded(node, node.getParentNode().getNodeName()); | ||
break; | ||
|
||
default: | ||
// looks like new type of node is present so there must be done additional | ||
// switch-case with support or ignore such situation | ||
// if you get this, raise the issue | ||
throw new ProcessorException(String.format(EXCEPTION_MESSAGE_NODE_TYPE_UNSUPPORTED, | ||
type, node.getNodeName(), node.getNodeValue())); | ||
} | ||
} | ||
|
||
private void convertNodeIfNeeded(Node node, String key) { | ||
if (shouldConvert(node)) { | ||
final String pureValue = StringUtils.trim(node.getNodeValue()); | ||
Value newValue = processValue(new Key(key), pureValue); | ||
node.setNodeValue(newValue.getValue().toString()); | ||
} | ||
} | ||
|
||
private boolean shouldConvert(Node node) { | ||
return StringUtils.isNotBlank(StringUtils.trim(node.getNodeValue())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
src/test/java/pl/szczepanik/silencio/integration/XMLProcessorTestInt.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package pl.szczepanik.silencio.integration; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import java.io.StringWriter; | ||
|
||
import org.junit.Test; | ||
|
||
import pl.szczepanik.silencio.GenericTest; | ||
import pl.szczepanik.silencio.api.Format; | ||
import pl.szczepanik.silencio.api.Processor; | ||
import pl.szczepanik.silencio.core.Builder; | ||
import pl.szczepanik.silencio.diagnostics.ProcessorSmokeChecker; | ||
import pl.szczepanik.silencio.processors.XMLProcessor; | ||
import pl.szczepanik.silencio.utils.ResourceLoader; | ||
|
||
/** | ||
* @author Damian Szczepanik (damianszczepanik@github) | ||
*/ | ||
public class XMLProcessorTestInt extends GenericTest { | ||
|
||
@Test | ||
public void shouldProcessXMLFile() { | ||
|
||
// given | ||
input = ResourceLoader.loadXmlAsReader("suv.xml"); | ||
output = new StringWriter(); | ||
Processor processor = new Builder(Format.XML).with(Builder.NUMBER_SEQUENCE).build(); | ||
processor.load(input); | ||
|
||
// when | ||
processor.process(); | ||
|
||
// then | ||
processor.write(output); | ||
String reference = ResourceLoader.loadXmlAsString("suv_Positive_NumberSequence.xml"); | ||
assertThat(output.toString()).isEqualTo(reference); | ||
} | ||
|
||
@Test | ||
public void shouldNotCrashOnDiagnosticTests() { | ||
|
||
// given | ||
String content = ResourceLoader.loadXmlAsString("suv.xml"); | ||
ProcessorSmokeChecker checker = new ProcessorSmokeChecker(new XMLProcessor()); | ||
|
||
// when | ||
checker.validateWithAllCombinations(content); | ||
|
||
// then | ||
// no crash | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 94 additions & 0 deletions
94
src/test/java/pl/szczepanik/silencio/processors/XMLProcessorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package pl.szczepanik.silencio.processors; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.hamcrest.core.StringContains.containsString; | ||
|
||
import java.io.StringWriter; | ||
|
||
import org.junit.Test; | ||
|
||
import pl.szczepanik.silencio.GenericTest; | ||
import pl.szczepanik.silencio.api.Format; | ||
import pl.szczepanik.silencio.api.Processor; | ||
import pl.szczepanik.silencio.core.Builder; | ||
import pl.szczepanik.silencio.core.Configuration; | ||
import pl.szczepanik.silencio.core.Execution; | ||
import pl.szczepanik.silencio.core.ProcessorException; | ||
import pl.szczepanik.silencio.decisions.PositiveDecision; | ||
import pl.szczepanik.silencio.mocks.WriterCrashOnWrite; | ||
import pl.szczepanik.silencio.utils.ResourceLoader; | ||
|
||
/** | ||
* @author Damian Szczepanik (damianszczepanik@github) | ||
*/ | ||
public class XMLProcessorTest extends GenericTest { | ||
|
||
@Test | ||
public void shouldReturnPassedFormat() { | ||
|
||
// given | ||
XMLProcessor processor = new XMLProcessor(); | ||
|
||
// when | ||
Format format = processor.getFormat(); | ||
|
||
// then | ||
assertThat(format).isEqualTo(Format.XML); | ||
} | ||
|
||
@Test | ||
public void shouldLoadXMLFileOnRealLoad() { | ||
|
||
// given | ||
XMLProcessor processor = new XMLProcessor(); | ||
input = ResourceLoader.loadXmlAsReader("suv.xml"); | ||
String refInput = ResourceLoader.loadXmlAsString("suv_tranformed.xml"); | ||
output = new StringWriter(); | ||
|
||
// when | ||
processor.load(input); | ||
|
||
// then | ||
processor.realWrite(output); | ||
assertThat(refInput).isEqualTo(output.toString()); | ||
} | ||
|
||
@Test | ||
public void shouldFailWhenLoadingInvalidJSONFile() { | ||
|
||
// given | ||
Processor processor = new XMLProcessor(); | ||
Execution execution = new Execution(new PositiveDecision(), Builder.BLANK); | ||
input = ResourceLoader.loadXmlAsReader("corrupted.xml"); | ||
|
||
// when | ||
processor.setConfiguration(new Configuration(execution)); | ||
|
||
// then | ||
thrown.expect(ProcessorException.class); | ||
thrown.expectMessage(containsString("XML document structures must start and end within the same entity")); | ||
processor.load(input); | ||
} | ||
|
||
@Test | ||
public void shouldFailWhenWrittingToInvalidWriter() { | ||
|
||
final String errorMessage = "Don't write into this writter!"; | ||
|
||
// given | ||
XMLProcessor processor = new XMLProcessor(); | ||
Execution execution = new Execution(new PositiveDecision(), Builder.BLANK); | ||
processor.setConfiguration(new Configuration(execution)); | ||
input = ResourceLoader.loadXmlAsReader("suv.xml"); | ||
output = new WriterCrashOnWrite(errorMessage); | ||
|
||
// when | ||
processor.load(input); | ||
processor.realProcess(); | ||
|
||
// then | ||
thrown.expect(ProcessorException.class); | ||
thrown.expectMessage(errorMessage); | ||
processor.realWrite(output); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.