package org.simpleframework.xml;
import java.io.File;
import java.io.FileOutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import junit.framework.TestCase;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.strategy.CycleStrategy;
import org.simpleframework.xml.strategy.Strategy;
import org.simpleframework.xml.strategy.Type;
import org.simpleframework.xml.strategy.Visitor;
import org.simpleframework.xml.strategy.VisitorStrategy;
import org.simpleframework.xml.stream.CamelCaseStyle;
import org.simpleframework.xml.stream.Format;
import org.simpleframework.xml.stream.HyphenStyle;
import org.simpleframework.xml.stream.InputNode;
import org.simpleframework.xml.stream.NodeMap;
import org.simpleframework.xml.stream.OutputNode;
import org.simpleframework.xml.stream.Style;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class ValidationTestCase extends TestCase {
//private static TransformerFactory transformerFactory;
//private static Transformer transformer;
private static DocumentBuilderFactory builderFactory;
private static DocumentBuilder builder;
static {
try {
builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
builder = builderFactory.newDocumentBuilder();
//transformerFactory = TransformerFactory.newInstance();
//transformer = transformerFactory.newTransformer();
} catch(Exception cause) {
cause.printStackTrace();
}
}
public void testDirectory() throws Exception {
assertTrue(FileSystem.validFileSystem());
}
public static synchronized void validate(Serializer out, Object type) throws Exception {
validate(type, out);
}
public static synchronized void validate(Object type, Serializer out) throws Exception {
String fileName = type.getClass().getSimpleName() + ".xml";
StringWriter buffer = new StringWriter();
out.write(type, buffer);
String text = buffer.toString();
byte[] octets = text.getBytes("UTF-8");
System.out.write(octets);
System.out.flush();
FileSystem.writeBytes(fileName, octets);
validate(text);
//File domDestination = new File(directory, type.getClass().getSimpleName() + ".dom.xml");
//File asciiDestination = new File(directory, type.getClass().getSimpleName() + ".ascii-dom.xml");
//OutputStream domFile = new FileOutputStream(domDestination);
//OutputStream asciiFile = new FileOutputStream(asciiDestination);
//Writer asciiOut = new OutputStreamWriter(asciiFile, "iso-8859-1");
/*
* This DOM document is the result of serializing the object in to a
* string. The document can then be used to validate serialization.
*/
//Document doc = builder.parse(new InputSource(new StringReader(text)));
//transformer.setOutputProperty(OutputKeys.INDENT, "yes");
//transformer.transform(new DOMSource(doc), new StreamResult(domFile));
//transformer.transform(new DOMSource(doc), new StreamResult(asciiOut));
//domFile.close();
//asciiFile.close();
out.validate(type.getClass(), text);
String hyphenFile = type.getClass().getSimpleName() + ".hyphen.xml";
Strategy strategy = new CycleStrategy("ID", "REFERER");
Visitor visitor = new DebugVisitor();
strategy = new VisitorStrategy(visitor, strategy);
Style style = new HyphenStyle();
Format format = new Format(style);
Persister hyphen = new Persister(strategy, format);
StringWriter hyphenWriter = new StringWriter();
hyphen.write(type, hyphenWriter);
hyphen.write(type, System.out);
hyphen.read(type.getClass(), hyphenWriter.toString());
FileSystem.writeString(hyphenFile, hyphenWriter.toString());
String camelCaseFile = type.getClass().getSimpleName() + ".camel-case.xml";
Style camelCaseStyle = new CamelCaseStyle(true, false);
Format camelCaseFormat = new Format(camelCaseStyle);
Persister camelCase = new Persister(strategy, camelCaseFormat);
StringWriter camelCaseWriter = new StringWriter();
camelCase.write(type, camelCaseWriter);
camelCase.write(type, System.out);
camelCase.read(type.getClass(), camelCaseWriter.toString());
FileSystem.writeString(camelCaseFile, camelCaseWriter.toString());
}
public static synchronized Document parse(String text) throws Exception {
return builder.parse(new InputSource(new StringReader(text)));
}
public static synchronized void validate(String text) throws Exception {
builder.parse(new InputSource(new StringReader(text)));
System.out.println(text);
}
public void assertElementExists(String sourceXml, String pathExpression) throws Exception {
assertMatch(sourceXml, pathExpression, new MatchAny(), true);
}
public void assertElementHasValue(String sourceXml, String pathExpression, String value) throws Exception {
assertMatch(sourceXml, pathExpression, new ElementMatch(value), true);
}
public void assertElementHasCDATA(String sourceXml, String pathExpression, String value) throws Exception {
assertMatch(sourceXml, pathExpression, new ElementCDATAMatch(value), true);
}
public void assertElementHasAttribute(String sourceXml, String pathExpression, String name, String value) throws Exception {
assertMatch(sourceXml, pathExpression, new AttributeMatch(name, value), true);
}
public void assertElementHasNamespace(String sourceXml, String pathExpression, String reference) throws Exception {
assertMatch(sourceXml, pathExpression, new OfficialNamespaceMatch(reference), true);
}
public void assertElementDoesNotExist(String sourceXml, String pathExpression) throws Exception {
assertMatch(sourceXml, pathExpression, new MatchAny(), false);
}
public void assertElementDoesNotHaveValue(String sourceXml, String pathExpression, String value) throws Exception {
assertMatch(sourceXml, pathExpression, new ElementMatch(value), false);
}
public void assertElementDoesNotHaveCDATA(String sourceXml, String pathExpression, String value) throws Exception {
assertMatch(sourceXml, pathExpression, new ElementCDATAMatch(value), false);
}
public void assertElementDoesNotHaveAttribute(String sourceXml, String pathExpression, String name, String value) throws Exception {
assertMatch(sourceXml, pathExpression, new AttributeMatch(name, value), false);
}
public void assertElementDoesNotHaveNamespace(String sourceXml, String pathExpression, String reference) throws Exception {
assertMatch(sourceXml, pathExpression, new OfficialNamespaceMatch(reference), false);
}
private void assertMatch(String sourceXml, String pathExpression, ExpressionMatch match, boolean assertTrue) throws Exception {
Document document = parse(sourceXml);
ExpressionMatcher matcher = new ExpressionMatcher(pathExpression, match);
if(!assertTrue) {
assertFalse("Document does have expression '"+pathExpression+"' with "+match.getDescription()+" for "+sourceXml, matcher.matches(document));
} else {
assertTrue("Document does not match expression '"+pathExpression+"' with "+match.getDescription()+" for "+sourceXml, matcher.matches(document));
}
}
private static class ExpressionMatcher {
private Pattern pattern;
private String[] segments;
private ExpressionMatch match;
private String pathExpression;
public ExpressionMatcher(String pathExpression, ExpressionMatch match) {
this.segments = pathExpression.replaceAll("^\\/", "").split("\\/");
this.pattern = Pattern.compile("^(.*)\\[([0-9]+)\\]$");
this.pathExpression = pathExpression;
this.match = match;
}
public boolean matches(Document document) {
org.w3c.dom.Element element = document.getDocumentElement();
if(!getLocalPart(element).equals(segments[0])) {
return false;
}
for(int i = 1; i < segments.length; i++) {
Matcher matcher = pattern.matcher(segments[i]);
String path = segments[i];
int index = 0;
if(matcher.matches()) {
String value = matcher.group(2);
index = Integer.parseInt(value) -1;
path = matcher.group(1);
}
List<org.w3c.dom.Element> list = getElementsByTagName(element, path);
if(index >= list.size()) {
return false;
}
element = list.get(index);
if(element == null) {
return false;
}
}
return match.match(element);
}
public String toString() {
return pathExpression;
}
}
private static List<org.w3c.dom.Element> getElementsByTagName(org.w3c.dom.Element element, String name) {
List<org.w3c.dom.Element> list = new ArrayList<org.w3c.dom.Element>();
NodeList allElements = element.getElementsByTagName("*");
for(int i = 0; i < allElements.getLength(); i++) {
Node node = allElements.item(i);
if(node instanceof org.w3c.dom.Element && node.getParentNode() == element) {
org.w3c.dom.Element itemNode = (org.w3c.dom.Element)node;
String localName = getLocalPart(itemNode);
if(localName.equals(name)) {
list.add(itemNode);
}
}
}
return list;
}
private static String getLocalPart(org.w3c.dom.Element element) {
if(element != null) {
String tagName = element.getTagName();
if(tagName != null) {
return tagName.replaceAll(".*:", "");
}
}
return null;
}
private static String getPrefix(org.w3c.dom.Element element) {
if(element != null) {
String tagName = element.getTagName();
if(tagName != null && tagName.matches(".+:.+")) {
return tagName.replaceAll(":.*", "");
}
}
return null;
}
private static interface ExpressionMatch{
public boolean match(org.w3c.dom.Element element);
public String getDescription();
}
private static class MatchAny implements ExpressionMatch {
public boolean match(org.w3c.dom.Element element) {
return element != null;
}
public String getDescription(){
return "path";
}
}
private static class ElementMatch implements ExpressionMatch {
private final String text;
public ElementMatch(String text){
this.text = text;
}
public boolean match(org.w3c.dom.Element element) {
if(element != null) {
Node value = element.getFirstChild();
return value != null && value.getNodeValue().equals(text);
}
return false;
}
public String getDescription() {
return "text value equal to '"+text+"'";
}
}
private static class ElementCDATAMatch implements ExpressionMatch {
private final String text;
public ElementCDATAMatch(String text){
this.text = text;
}
public boolean match(org.w3c.dom.Element element) {
if(element != null) {
Node value = element.getFirstChild();
if(value instanceof CDATASection) {
return value != null && value.getNodeValue().equals(text);
}
return false;
}
return false;
}
public String getDescription() {
return "text value equal to '"+text+"'";
}
}
private static class NamespaceMatch implements ExpressionMatch {
private final String reference;
public NamespaceMatch(String reference) {
this.reference = reference;
}
public boolean match(org.w3c.dom.Element element) {
if(element != null) {
String prefix = getPrefix(element); // a:element -> a
if(prefix != null && prefix.equals("")) {
prefix = null;
}
return match(element, prefix);
}
return false;
}
private boolean match(org.w3c.dom.Element element, String prefix) {
if(element != null) {
String currentPrefix = getPrefix(element); // if prefix is null, then this is inherited
if((currentPrefix != null && prefix == null) ) {
prefix = currentPrefix; // inherit parents
}
String name = "xmlns"; // default xmlns=<reference>
if(prefix != null && !prefix.equals("")) {
name = name + ":" + prefix; // xmlns:a=<reference>
}
String value = element.getAttribute(name);
if(value == null || value.equals("")) {
Node parent = element.getParentNode();
if(parent instanceof org.w3c.dom.Element) {
return match((org.w3c.dom.Element)element.getParentNode(), prefix);
}
}
return value != null && value.equals(reference);
}
return false;
}
public String getDescription(){
return "namespace reference as '"+reference+"'";
}
}
private static class OfficialNamespaceMatch implements ExpressionMatch {
private final String reference;
public OfficialNamespaceMatch(String reference) {
this.reference = reference;
}
public boolean match(org.w3c.dom.Element element) {
if(element != null) {
String actual = element.getNamespaceURI();
if(actual == null){
return reference == null || reference.equals("");
}
return element.getNamespaceURI().equals(reference);
}
return false;
}
public String getDescription(){
return "namespace reference as '"+reference+"'";
}
}
private static class AttributeMatch implements ExpressionMatch {
private final String name;
private final String value;
public AttributeMatch(String name, String value) {
this.name = name;
this.value = value;
}
public boolean match(org.w3c.dom.Element element) {
if(element != null) {
String attribute = element.getAttribute(name);
return attribute != null && attribute.equals(value);
}
return false;
}
public String getDescription() {
return "attribute "+name+"='"+value+"'";
}
}
private static void print(Node node) {
Queue<Node> nodes = new LinkedList<Node>();
nodes.add(node);
while (!nodes.isEmpty()) {
node = nodes.poll();
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
if(list.item(i) instanceof org.w3c.dom.Element) {
nodes.add(list.item(i));
}
}
System.out.format("name='%s' prefix='%s' reference='%s'%n", node.getPrefix(), node.getLocalName(),
node.getNamespaceURI());
}
}
public static void dumpNamespaces(String xml) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(
new InputSource(new StringReader(xml)));
print(doc.getDocumentElement());
}
private static class DebugVisitor implements Visitor {
public void read(Type type, NodeMap<InputNode> node){
InputNode element = node.getNode();
if(element.isRoot()) {
Object source = element.getSource();
Class sourceType = source.getClass();
Class itemType = type.getType();
System.out.printf(">>>>> ELEMENT=[%s]%n>>>>> TYPE=[%s]%n>>>>> SOURCE=[%s]%n", element, itemType, sourceType);
}
}
public void write(Type type, NodeMap<OutputNode> node) throws Exception {
if(!node.getNode().isRoot()) {
node.getNode().setComment(type.getType().getName());
}
}
}
public static class FileSystem {
public static File getDirectory() {
File directory = null;
try{
String path = System.getProperty("output");
if(path != null) {
directory = new File(path);
}
} catch(Exception e){
e.printStackTrace();
}
if(directory == null) {
directory = new File("output");
}
if(!directory.exists()) {
directory.mkdirs();
}
return directory;
}
public static boolean validFileSystem() {
return getDirectory().exists();
}
public static void writeString(String fileName, String content) {
writeBytes(fileName, content.getBytes());
}
public static void writeBytes(String fileName, byte[] content) {
try {
File directory = getDirectory();
File file = new File(directory, fileName);
FileOutputStream out = new FileOutputStream(file);
out.write(content);
out.flush();
out.close();
}catch(Exception e){
System.err.println(e.getMessage());
}
}
}
}