/** * $Id$ * $Date$ * */ package org.xmlsh.internal.commands; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.List; import javax.xml.namespace.QName; import javax.xml.stream.Location; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.Characters; import javax.xml.stream.events.Comment; import javax.xml.stream.events.DTD; import javax.xml.stream.events.EndDocument; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.Namespace; import javax.xml.stream.events.ProcessingInstruction; import javax.xml.stream.events.StartDocument; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import org.xmlsh.core.CoreException; import org.xmlsh.core.InputPort; import org.xmlsh.core.InvalidArgumentException; import org.xmlsh.core.Options; import org.xmlsh.core.XCommand; import org.xmlsh.core.XValue; import org.xmlsh.sh.shell.SerializeOpts; import org.xmlsh.util.Util; public class xcmp extends XCommand { private boolean xopt = false; private boolean bopt = false; // blank (CR/LF) insensitive private boolean nopt = false; // dont print anything, just exit private boolean copt = false; // Ignore comments private boolean popt = false; @Override public int run(List<XValue> args) throws Exception { Options opts = new Options("x,b,n,c,p"); opts.parse(args); xopt = opts.hasOpt("x"); bopt = opts.hasOpt("b"); nopt = opts.hasOpt("n"); copt = opts.hasOpt("c"); popt = opts.hasOpt("p"); List<XValue> argv; if((argv = opts.getRemainingArgs()).size() != 2) { usage(); return -1; } InputPort i1 = getInput(argv.get(0)); InputPort i2 = getInput(argv.get(1)); if(xopt) return xml_cmp(i1, i2); else return bin_cmp(i1, i2); } private int readByte(InputStream is) throws IOException { int c = is.read(); if(c == '\r' && bopt) c = is.read(); return c; } private int bin_cmp(InputPort f1, InputPort f2) throws CoreException, IOException { BufferedInputStream is1 = new BufferedInputStream( f1.asInputStream(getSerializeOpts())); BufferedInputStream is2 = null; try { is2 = new BufferedInputStream(f2.asInputStream(getSerializeOpts())); int b = 0; int c; while((c = readByte(is1)) >= 0) { int c2 = readByte(is2); if(c != c2) { if(!nopt) printErr("Differs at byte: " + b); return 1; } b++; } if(is2.read() != -1) { if(!nopt) printErr("Differs at byte: " + b); return 2; } return 0; } finally { is1.close(); if(is2 != null) is2.close(); } } private int xml_cmp(InputPort f1, InputPort f2) throws InvalidArgumentException, CoreException, XMLStreamException { /* * XMLInputFactory inputFactory=XMLInputFactory.newInstance(); * * * inputFactory.setProperty(XMLInputFactory.IS_COALESCING, * Boolean.valueOf(true)); * inputFactory.setProperty(XMLInputFactory.IS_VALIDATING, * Boolean.valueOf(false)); * inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, * Boolean.valueOf(false)); */ SerializeOpts opts = getSerializeOpts().clone(); opts.setSupports_dtd(false); XMLEventReader xmlreader1 = f1.asXMLEventReader(opts); XMLEventReader xmlreader2 = f2.asXMLEventReader(opts); try { while(xmlreader1.hasNext() && xmlreader2.hasNext()) { XMLEvent e1 = xmlreader1.nextEvent(); /* * DAL: Note: skip START_DOCUMENT tags on either e1 or e2 * StAX only produdes START_DOCUMENT tags if there is an <?xml?> * declaration * which doesnt change the logical meaning of the document wrt to * comparison */ if(e1.getEventType() == XMLStreamConstants.START_DOCUMENT && xmlreader1.hasNext()) e1 = xmlreader1.nextEvent(); XMLEvent e2 = xmlreader2.nextEvent(); if(e2.getEventType() == XMLStreamConstants.START_DOCUMENT && xmlreader2.hasNext()) e2 = xmlreader2.nextEvent(); /* * If ignoring blanks then entirely skip any element which is charactors * and entirely blank */ if(bopt || copt || popt) { while(((bopt && (e1.getEventType() == XMLStreamConstants.CHARACTERS) && isBlank(e1.asCharacters())) || (copt && e1.getEventType() == XMLStreamConstants.COMMENT) || (popt && e1 .getEventType() == XMLStreamConstants.PROCESSING_INSTRUCTION)) && xmlreader1.hasNext()) e1 = xmlreader1.nextEvent(); while(((bopt && (e2.getEventType() == XMLStreamConstants.CHARACTERS) && isBlank(e2.asCharacters())) || (copt && e2.getEventType() == XMLStreamConstants.COMMENT) || (popt && e2 .getEventType() == XMLStreamConstants.PROCESSING_INSTRUCTION)) && xmlreader2.hasNext()) e2 = xmlreader2.nextEvent(); } if(!isEqual(e1, e2)) { Location loc = e1.getLocation(); if(!nopt) printErr("xml diff at file1: " + loc.toString()); return 2; } } return ((!xmlreader1.hasNext()) && (!xmlreader2.hasNext())) ? 0 : 1; } finally { try { xmlreader1.close(); } finally { } try { xmlreader2.close(); } finally { } } } private boolean isBlank(Characters e) { if(e.isIgnorableWhiteSpace()) return true; return Util.isBlank(e.getData()); } private boolean isEqual(StartElement e1, StartElement e2) { if(e1 == e2) return true; if(!isEqual(e1.getName(), e2.getName())) return false; if(!compareAttributes(e1.getAttributes(), e2.getAttributes())) return false; // Do not isEqual namespace declarations for now, it is not relevent // namespaces are propogated into the attribute and element QName return true; } @SuppressWarnings("unchecked") private boolean compareAttributes(Iterator attributes1, Iterator attributes2) { /* * Compare attributes in no order */ /* * Put the attributes1 into a map by qname * For each element in attributes2 isEqual against the set and remove * If there are any left in the set then non-equal otherwise equal */ HashMap<QName, Attribute> set = new HashMap<QName, Attribute>(); while(attributes1.hasNext()) { Attribute attr = (Attribute) attributes1.next(); set.put(attr.getName(), attr); } while(attributes2.hasNext()) { Attribute attr2 = (Attribute) attributes2.next(); Attribute attr1 = set.remove(attr2.getName()); if(attr1 == null) return false; if(!isEqual(attr1, attr2)) return false; } return set.isEmpty(); } private boolean isEqual(QName name1, QName name2) { return name1.equals(name2); } private boolean isEqual(EndElement e1, EndElement e2) { return isEqual(e1.getName(), e2.getName()); } private boolean isEqual(Characters e1, Characters e2) { if(bopt) return isEqual(e1.getData().trim(), e2.getData().trim()); else return isEqual(e1.getData(), e2.getData()); } private boolean isEqual(String s1, String s2) { // Compare strings considering null and "" equal return Util.isEqual(s1, s2); } private boolean isEqual(Attribute e1, Attribute e2) { return isEqual(e1.getName(), e2.getName()) && isEqual(e1.getValue(), e2.getValue()); } private boolean isEqual(Namespace e1, Namespace e2) { // Namespaces are equal if their URI's are equal return isEqual(e1.getNamespaceURI(), e2.getNamespaceURI()); } private boolean isEqual(Comment e1, Comment e2) { return isEqual(e1.getText(), e2.getText()); } private boolean isEqual(StartDocument e1, StartDocument e2) { return true; // ignore document stuff } private boolean isEqual(EndDocument e1, EndDocument e2) { return true; } private boolean isEqual(DTD e1, DTD e2) { return true; } private boolean isEqual(ProcessingInstruction e1, ProcessingInstruction e2) { String t1 = e1.getTarget(); String t2 = e2.getTarget(); if(!Util.isEqual(t1, t2)) return false; t1 = e1.getData(); t2 = e2.getData(); return Util.isEqual(t1, t2); } private boolean isEqual(XMLEvent e1, XMLEvent e2) { if(e1.getEventType() != e2.getEventType()) return false; switch(e1.getEventType()){ case XMLStreamConstants.START_ELEMENT: return isEqual(e1.asStartElement(), e2.asStartElement()); case XMLStreamConstants.END_ELEMENT: return isEqual(e1.asEndElement(), e2.asEndElement()); case XMLStreamConstants.CHARACTERS: return isEqual(e1.asCharacters(), e2.asCharacters()); case XMLStreamConstants.ATTRIBUTE: return isEqual((Attribute) e1, (Attribute) e2); case XMLStreamConstants.NAMESPACE: return isEqual((Namespace) e1, (Namespace) e2); case XMLStreamConstants.PROCESSING_INSTRUCTION: return isEqual((ProcessingInstruction) e1, (ProcessingInstruction) e2); case XMLStreamConstants.COMMENT: return isEqual((Comment) e1, (Comment) e2); case XMLStreamConstants.START_DOCUMENT: return isEqual((StartDocument) e1, (StartDocument) e2); case XMLStreamConstants.END_DOCUMENT: return isEqual((EndDocument) e1, (EndDocument) e2); case XMLStreamConstants.DTD: return isEqual((DTD) e1, (DTD) e2); default: return false; } } } // // // Copyright (C) 2008-2014 David A. Lee. // // The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. // See the License for the specific language governing rights and limitations // under the License. // // The Original Code is: all this file. // // The Initial Developer of the Original Code is David A. Lee // // Portions created by (your name) are Copyright (C) (your legal entity). All // Rights Reserved. // // Contributor(s): none. //