blob: 49adb83b431571527a3b52033d09303f6ff63c0d [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.impl.tool;
import org.apache.xmlbeans.SystemProperties;
import org.apache.xmlbeans.impl.schema.SchemaTypeSystemImpl;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
public class Diff {
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("Usage: diff <jarname1> <jarname2> to compare two jars");
System.out.println(" or diff <dirname1> <dirname2> to compare two dirs");
return;
}
File file1 = new File(args[0]);
if (!file1.exists()) {
System.out.println("File \"" + args[0] + "\" not found.");
return;
}
File file2 = new File(args[1]);
if (!file2.exists()) {
System.out.println("File \"" + args[1] + "\" not found.");
return;
}
List<String> result = new ArrayList<>();
if (file1.isDirectory()) {
if (!file2.isDirectory()) {
System.out.println("Both parameters have to be directories if the first parameter is a directory.");
return;
}
dirsAsTypeSystems(file1, file2, result);
} else {
if (file2.isDirectory()) {
System.out.println("Both parameters have to be jar files if the first parameter is a jar file.");
return;
}
try {
JarFile jar1 = new JarFile(file1);
JarFile jar2 = new JarFile(file2);
jarsAsTypeSystems(jar1, jar2, result);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
if (result.size() < 1) {
System.out.println("No differences encountered.");
} else {
System.out.println("Differences:");
for (String s : result) {
System.out.println(s);
}
}
}
/**
* Diffs the contents of two jars, looking only at the schema typesystems
* saved inside those jars
*/
public static void jarsAsTypeSystems(JarFile jar1, JarFile jar2, List<String> diffs) {
Enumeration<JarEntry> entries1 = jar1.entries();
Enumeration<JarEntry> entries2 = jar2.entries();
List<ZipEntry> list1 = new ArrayList<>();
List<ZipEntry> list2 = new ArrayList<>();
while (entries1.hasMoreElements()) {
ZipEntry ze = entries1.nextElement();
String name = ze.getName();
if (name.startsWith(SchemaTypeSystemImpl.METADATA_PACKAGE_GEN + "/system/s") && name.endsWith(".xsb")) {
list1.add(ze);
}
}
while (entries2.hasMoreElements()) {
ZipEntry ze = entries2.nextElement();
String name = ze.getName();
if (name.startsWith(SchemaTypeSystemImpl.METADATA_PACKAGE_GEN + "/system/s") && name.endsWith(".xsb")) {
list2.add(ze);
}
}
ZipEntry[] files1 = list1.toArray(new ZipEntry[0]);
ZipEntry[] files2 = list2.toArray(new ZipEntry[0]);
Comparator<ZipEntry> comparator = Comparator.comparing(ZipEntry::getName);
Arrays.sort(files1, comparator);
Arrays.sort(files2, comparator);
int i1 = 0;
int i2 = 0;
while (i1 < files1.length && i2 < files2.length) {
String name1 = files1[i1].getName();
String name2 = files2[i2].getName();
int dif = name1.compareTo(name2);
if (dif == 0) {
// Compare the files
zipEntriesAsXsb(files1[i1], jar1, files2[i2], jar2, diffs);
i1++;
i2++; // Move to next pair
} else if (dif < 0) {
// dir1 contains a file that dir2 doesn't
diffs.add("Jar \"" + jar1.getName() + "\" contains an extra file: \"" +
name1 + "\"");
i1++;
} else {
// dir2 contains a file that dir1 doesn't
diffs.add("Jar \"" + jar2.getName() + "\" contains an extra file: \"" +
name2 + "\"");
i2++;
}
}
while (i1 < files1.length) {
diffs.add("Jar \"" + jar1.getName() + "\" contains an extra file: \"" +
files1[i1].getName() + "\"");
i1++;
}
while (i2 < files2.length) {
diffs.add("Jar \"" + jar2.getName() + "\" contains an extra file: \"" +
files2[i2].getName() + "\"");
i2++;
}
}
/**
* Diffs the contents of two dirs looking only at the xsb files
* contained in these two dirs
* Updated diffs with a list of differences (for the time being, strings
* describing the difference)
*/
public static void dirsAsTypeSystems(File dir1, File dir2, List<String> diffs) {
assert dir1.isDirectory() : "Parameters must be directories";
assert dir2.isDirectory() : "Parameters must be directories";
/*
Navigate three directories deep to get to the type system.
Assume the schema[METADATA_PACKAGE_LOAD]/system/* structure
*/
File temp1 = new File(dir1, SchemaTypeSystemImpl.METADATA_PACKAGE_GEN + "/system");
File temp2 = new File(dir2, SchemaTypeSystemImpl.METADATA_PACKAGE_GEN + "/system");
if (temp1.exists() && temp2.exists()) {
File[] files1 = temp1.listFiles();
File[] files2 = temp2.listFiles();
assert (files1 != null && files2 != null);
if (files1.length == 1 && files2.length == 1) {
temp1 = files1[0];
temp2 = files2[0];
} else {
if (files1.length == 0) {
temp1 = null;
}
if (files2.length == 0) {
temp2 = null;
}
if (files1.length > 1) {
diffs.add("More than one typesystem found in dir \"" +
dir1.getName() + "\"");
return;
}
if (files2.length > 1) {
diffs.add("More than one typesystem found in dir \"" +
dir2.getName() + "\"");
return;
}
}
} else {
if (!temp1.exists()) {
temp1 = null;
}
if (!temp2.exists()) {
temp2 = null;
}
}
if (temp1 == null && temp2 == null) {
return;
} else if (temp1 == null || temp2 == null) {
if (temp1 == null) {
diffs.add("No typesystems found in dir \"" + dir1 + "\"");
}
if (temp2 == null) {
diffs.add("No typesystems found in dir \"" + dir2 + "\"");
}
return;
} else {
dir1 = temp1;
dir2 = temp2;
}
boolean diffIndex = isDiffIndex();
FilenameFilter xsbName = (dir, name) -> name.endsWith(".xsb");
File[] files1 = dir1.listFiles(xsbName);
File[] files2 = dir2.listFiles(xsbName);
assert (files1 != null && files2 != null);
Comparator<File> comparator = Comparator.comparing(File::getName);
Arrays.sort(files1, comparator);
Arrays.sort(files2, comparator);
int i1 = 0;
int i2 = 0;
while (i1 < files1.length && i2 < files2.length) {
String name1 = files1[i1].getName();
String name2 = files2[i2].getName();
int dif = name1.compareTo(name2);
if (dif == 0) {
if (diffIndex || !files1[i1].getName().equals("index.xsb")) {
filesAsXsb(files1[i1], files2[i2], diffs); // Compare the files
}
i1++;
i2++; // Move to next pair
} else if (dif < 0) {
// dir1 contains a file that dir2 doesn't
diffs.add("Dir \"" + dir1.getName() + "\" contains an extra file: \"" +
name1 + "\"");
i1++;
} else {
// dir2 contains a file that dir1 doesn't
diffs.add("Dir \"" + dir2.getName() + "\" contains an extra file: \"" +
name2 + "\"");
i2++;
}
}
while (i1 < files1.length) {
diffs.add("Dir \"" + dir1.getName() + "\" contains an extra file: \"" +
files1[i1].getName() + "\"");
i1++;
}
while (i2 < files2.length) {
diffs.add("Dir \"" + dir2.getName() + "\" contains an extra file: \"" +
files2[i2].getName() + "\"");
i2++;
}
}
private static boolean isDiffIndex() {
String prop = SystemProperties.getProperty("xmlbeans.diff.diffIndex");
return prop == null || !"0".equals(prop) && !"false".equalsIgnoreCase(prop);
}
/**
* Diffs the two given files assuming they are in xsb format
* Updates diffs with differences in string format
*/
public static void filesAsXsb(File file1, File file2, List<String> diffs) {
assert file1.exists() : "File \"" + file1.getAbsolutePath() + "\" does not exist.";
assert file2.exists() : "File \"" + file2.getAbsolutePath() + "\" does not exist.";
try (FileInputStream stream1 = new FileInputStream(file1);
FileInputStream stream2 = new FileInputStream(file2)) {
streamsAsXsb(stream1, file1.getName(), stream2, file2.getName(), diffs);
} catch (IOException ignored) {
}
}
public static void zipEntriesAsXsb(ZipEntry file1, JarFile jar1,
ZipEntry file2, JarFile jar2, List<String> diffs) {
try (InputStream stream1 = jar1.getInputStream(file1);
InputStream stream2 = jar2.getInputStream(file2)) {
streamsAsXsb(stream1, file1.getName(), stream2, file2.getName(), diffs);
} catch (IOException ignored) {
}
}
public static void streamsAsXsb(InputStream stream1, String name1,
InputStream stream2, String name2, List<String> diffs)
throws IOException {
String charset = StandardCharsets.UTF_8.name();
ByteArrayOutputStream buf1 = new ByteArrayOutputStream();
ByteArrayOutputStream buf2 = new ByteArrayOutputStream();
XsbDumper.dump(stream1, "", new PrintStream(buf1, true, charset));
XsbDumper.dump(stream2, "", new PrintStream(buf2, true, charset));
readersAsText(new StringReader(buf1.toString(charset)), name1,
new StringReader(buf2.toString(charset)), name2, diffs);
}
public static void readersAsText(Reader r1, String name1, Reader r2, String name2,
List<String> diffs)
throws IOException {
org.apache.xmlbeans.impl.util.Diff.readersAsText(r1, name1, r2, name2, diffs);
}
}