Skip to content

Commit bd20870

Browse files
committed
Add utility class for comparing directories
1 parent 9a6893c commit bd20870

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
package org.scijava.maven.plugin.install;
2+
3+
/* Copyright 2004 The Apache Software Foundation
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import java.io.File;
19+
import java.io.FileInputStream;
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.util.ArrayList;
23+
import java.util.Collections;
24+
import java.util.Comparator;
25+
import java.util.Iterator;
26+
import java.util.LinkedHashMap;
27+
import java.util.LinkedHashSet;
28+
import java.util.List;
29+
import java.util.Map;
30+
import java.util.Set;
31+
import java.util.function.Function;
32+
import java.util.regex.Matcher;
33+
import java.util.stream.Collectors;
34+
35+
public class CompareDirectories {
36+
37+
enum Status {
38+
ADDED, DELETED, MODIFIED, VERSION_CHANGED, MOVED;
39+
}
40+
41+
static class FileChange {
42+
String oldPath = "", newPath = "";
43+
Status status;
44+
45+
static String unversionedName(String name) {
46+
Matcher matcher = AbstractCopyJarsMojo.versionPattern.matcher(name);
47+
if(matcher.matches()) {
48+
return matcher.group(1);
49+
}else {
50+
return name;
51+
}
52+
}
53+
54+
String unversionedOldPath() {
55+
return unversionedName(oldPath);
56+
}
57+
58+
String unversionedNewPath() {
59+
return unversionedName(newPath);
60+
}
61+
62+
String oldFileName() {
63+
return new File(oldPath).getName();
64+
}
65+
66+
String newFileName() {
67+
return new File(newPath).getName();
68+
}
69+
}
70+
71+
private static List<FileChange> getChanges(File commit1, File commit2) throws IOException {
72+
List<FileChange> changes = CompareDirectories.compare(commit1, commit2);
73+
detectVersionChanges(changes);
74+
detectMoves(changes);
75+
Collections.sort(changes, Comparator.comparing((FileChange c) -> c.status)
76+
.thenComparing(c -> c.oldPath)
77+
.thenComparing(c -> c.newPath));
78+
changes.forEach(change -> System.out.println(toString(change)));
79+
return changes;
80+
}
81+
82+
private static String toString(FileChange change) {
83+
if(change.status.equals(Status.ADDED)) return change.status.name() + " " + change.newPath;
84+
if(change.status.equals(Status.DELETED)) return change.status.name() + " " + change.oldPath;
85+
if(change.status.equals(Status.VERSION_CHANGED)) {
86+
return change.status.name() + " " + change.unversionedOldPath() + " (" + version(change.oldPath) + " -> " + version(change.newPath) + ")";
87+
}
88+
return change.status.name() + " " + change.oldPath + " -> " + change.newPath;
89+
}
90+
91+
private static String version(String name) {
92+
Matcher matcher = AbstractCopyJarsMojo.versionPattern.matcher(name);
93+
if(matcher.matches()) {
94+
return matcher.group(2).substring(1);
95+
}else {
96+
return "";
97+
}
98+
}
99+
100+
private static void detectVersionChanges(List<FileChange> changes) {
101+
Map<String, FileChange> deleted = changes.stream()
102+
.filter(change -> change.status.equals(Status.DELETED))
103+
.collect(Collectors.toMap(FileChange::unversionedOldPath, Function.identity(),
104+
(o1,o2) -> o1 , LinkedHashMap::new));
105+
Map<String, FileChange> added = changes.stream()
106+
.filter(change -> change.status.equals(Status.ADDED))
107+
.collect(Collectors.toMap(FileChange::unversionedNewPath, Function.identity(),
108+
(o1,o2) -> o1 , LinkedHashMap::new));
109+
for (Map.Entry<String,FileChange> del : deleted.entrySet()) {
110+
FileChange add = added.get(del.getKey());
111+
if(add!=null) {
112+
changes.remove(del.getValue());
113+
changes.remove(add);
114+
FileChange c = new FileChange();
115+
c.oldPath = del.getValue().oldPath;
116+
c.newPath = add.newPath;
117+
c.status = Status.VERSION_CHANGED;
118+
changes.add(c);
119+
}
120+
}
121+
}
122+
123+
private static void detectMoves(List<FileChange> changes) {
124+
Map<String, FileChange> deleted = changes.stream()
125+
.filter(change -> change.status.equals(Status.DELETED))
126+
.collect(Collectors.toMap(FileChange::oldFileName, Function.identity(),
127+
(o1,o2) -> o1 , LinkedHashMap::new));
128+
Map<String, FileChange> added = changes.stream()
129+
.filter(change -> change.status.equals(Status.ADDED))
130+
.collect(Collectors.toMap(FileChange::newFileName, Function.identity(),
131+
(o1,o2) -> o1 , LinkedHashMap::new));
132+
for (Map.Entry<String,FileChange> del : deleted.entrySet()) {
133+
FileChange add = added.get(del.getKey());
134+
if(add!=null) {
135+
changes.remove(del.getValue());
136+
changes.remove(add);
137+
FileChange c = new FileChange();
138+
c.oldPath = del.getValue().oldPath;
139+
c.newPath = add.newPath;
140+
c.status = Status.MOVED;
141+
changes.add(c);
142+
}
143+
}
144+
}
145+
146+
private static List<FileChange> compare(File dir1, File dir2) throws IOException {
147+
List<FileChange> res = new ArrayList<>();
148+
149+
System.out.println("Comparing " + dir1 + " with " + dir2 + ":");
150+
151+
Set set1 = new LinkedHashSet();
152+
Set set2 = new LinkedHashSet();
153+
154+
if(dir1 != null) {
155+
addFilesToSet(dir1, set1, dir1);
156+
}
157+
158+
if(dir2 != null) {
159+
addFilesToSet(dir2, set2, dir2);
160+
}
161+
162+
for (Iterator i = set1.iterator(); i.hasNext();) {
163+
String name = (String) i.next();
164+
if (!set2.contains(name)) {
165+
FileChange change = new FileChange();
166+
change.status = Status.DELETED;
167+
change.oldPath = name;
168+
res.add(change);
169+
continue;
170+
}
171+
set2.remove(name);
172+
if (!streamsEqual(new FileInputStream(new File(dir1.getAbsolutePath(), name)),
173+
new FileInputStream(new File(dir2.getAbsolutePath(), name)))) {
174+
FileChange change = new FileChange();
175+
change.status = Status.MODIFIED;
176+
change.oldPath = name;
177+
change.newPath = name;
178+
res.add(change);
179+
}
180+
}
181+
for (Object o : set2) {
182+
String name = (String) o;
183+
FileChange change = new FileChange();
184+
change.status = Status.ADDED;
185+
change.newPath = name;
186+
res.add(change);
187+
}
188+
189+
return res;
190+
}
191+
192+
private static void addFilesToSet(File baseDir, Set result, File file1) {
193+
if(file1.isDirectory()) for (File file : file1.listFiles()) {
194+
addFilesToSet(baseDir, result, file);
195+
} else {
196+
result.add(file1.getAbsolutePath().replace(baseDir.getAbsolutePath(), ""));
197+
}
198+
}
199+
200+
private static boolean streamsEqual(InputStream stream1, InputStream stream2) throws IOException {
201+
byte[] buf1 = new byte[4096];
202+
byte[] buf2 = new byte[4096];
203+
boolean done1 = false;
204+
boolean done2 = false;
205+
206+
try {
207+
while (!done1) {
208+
int off1 = 0;
209+
int off2 = 0;
210+
211+
while (off1 < buf1.length) {
212+
int count = stream1.read(buf1, off1, buf1.length - off1);
213+
if (count < 0) {
214+
done1 = true;
215+
break;
216+
}
217+
off1 += count;
218+
}
219+
while (off2 < buf2.length) {
220+
int count = stream2.read(buf2, off2, buf2.length - off2);
221+
if (count < 0) {
222+
done2 = true;
223+
break;
224+
}
225+
off2 += count;
226+
}
227+
if (off1 != off2 || done1 != done2)
228+
return false;
229+
for (int i = 0; i < off1; i++) {
230+
if (buf1[i] != buf2[i])
231+
return false;
232+
}
233+
}
234+
return true;
235+
} finally {
236+
stream1.close();
237+
stream2.close();
238+
}
239+
}
240+
241+
public static void main(String[] args) throws IOException {
242+
if (args.length != 2) {
243+
System.out.println("Add args [dir1] [dir2]");
244+
System.exit(1);
245+
}
246+
getChanges(new File(args[0]), new File(args[1]));
247+
}
248+
}

0 commit comments

Comments
 (0)