|
@@ -1,23 +1,19 @@ |
1 |
1 |
|
// This Source Code is in the Public Domain per: http://unlicense.org |
2 |
2 |
|
package org.litesoft.util; |
3 |
3 |
|
|
4 |
|
- |
import java.io.*; |
5 |
|
- |
|
6 |
4 |
|
import org.litesoft.commonfoundation.typeutils.*; |
7 |
5 |
|
|
|
6 |
+ |
import java.io.*; |
8 |
7 |
|
import java.util.*; |
9 |
8 |
|
|
10 |
|
- |
public class TriDirUpdater |
11 |
|
- |
{ |
|
9 |
+ |
public class TriDirUpdater { |
12 |
10 |
|
public static final String VERSION = "1.0"; |
13 |
11 |
|
|
14 |
12 |
|
public static final int MAX_FILE_SIZE = 32 * 1024 * 1024; |
15 |
13 |
|
|
16 |
|
- |
private static File checkPath( String pType, String pPath ) |
17 |
|
- |
{ |
|
14 |
+ |
private static File checkPath( String pType, String pPath ) { |
18 |
15 |
|
File zPath = new File( pPath ); |
19 |
|
- |
if ( !zPath.isDirectory() ) |
20 |
|
- |
{ |
|
16 |
+ |
if ( !zPath.isDirectory() ) { |
21 |
17 |
|
System.out.println( pType + " Path, Not a Directory: " + zPath.getAbsolutePath() ); |
22 |
18 |
|
System.exit( 2 ); |
23 |
19 |
|
} |
|
@@ -25,16 +21,13 @@ |
25 |
21 |
|
} |
26 |
22 |
|
|
27 |
23 |
|
public static void main( String[] args ) |
28 |
|
- |
throws Exception |
29 |
|
- |
{ |
|
24 |
+ |
throws Exception { |
30 |
25 |
|
System.out.println( "3 (Tri) Directory Updater vs " + VERSION ); |
31 |
26 |
|
String zEditor = System.getenv( "Editor" ); |
32 |
|
- |
if ( zEditor == null ) |
33 |
|
- |
{ |
|
27 |
+ |
if ( zEditor == null ) { |
34 |
28 |
|
throw new IllegalStateException( "No 'Editor' environment variable" ); |
35 |
29 |
|
} |
36 |
|
- |
if ( args.length != 3 ) |
37 |
|
- |
{ |
|
30 |
+ |
if ( args.length != 3 ) { |
38 |
31 |
|
showHelp(); |
39 |
32 |
|
} |
40 |
33 |
|
|
|
@@ -44,8 +37,7 @@ |
44 |
37 |
|
checkPath( "Working", args[2] ) ).process(); |
45 |
38 |
|
} |
46 |
39 |
|
|
47 |
|
- |
private static void showHelp() |
48 |
|
- |
{ |
|
40 |
+ |
private static void showHelp() { |
49 |
41 |
|
System.out.println( "Requires 3 parameters:" ); |
50 |
42 |
|
System.out.println(); |
51 |
43 |
|
System.out.println( " 1) Update Directory Path" ); |
|
@@ -88,44 +80,36 @@ |
88 |
80 |
|
private final File mUpdatePath; |
89 |
81 |
|
private final TargetTree mOriginal, mWorking; |
90 |
82 |
|
|
91 |
|
- |
private TriDirUpdater( String pEditor, File pUpdatePath, File pOriginalPath, File pWorkingPath ) |
92 |
|
- |
{ |
|
83 |
+ |
private TriDirUpdater( String pEditor, File pUpdatePath, File pOriginalPath, File pWorkingPath ) { |
93 |
84 |
|
mEditor = pEditor; |
94 |
85 |
|
mUpdatePath = pUpdatePath; |
95 |
86 |
|
mOriginal = new TargetTree( pOriginalPath ); |
96 |
87 |
|
mWorking = new TargetTree( pWorkingPath ); |
97 |
88 |
|
} |
98 |
89 |
|
|
99 |
|
- |
private void process() |
100 |
|
- |
{ |
|
90 |
+ |
private void process() { |
101 |
91 |
|
mOriginal.populate(); |
102 |
92 |
|
mWorking.populate(); |
103 |
93 |
|
Queue<String> zDirectoriesToProcess = new LinkedList<String>(); |
104 |
94 |
|
process( zDirectoriesToProcess, "", getDirectoryEntries( mUpdatePath ) ); |
105 |
|
- |
while ( !zDirectoriesToProcess.isEmpty() ) |
106 |
|
- |
{ |
|
95 |
+ |
while ( !zDirectoriesToProcess.isEmpty() ) { |
107 |
96 |
|
String zRelativeDirectoryPath = zDirectoriesToProcess.remove(); |
108 |
97 |
|
process( zDirectoriesToProcess, zRelativeDirectoryPath, getDirectoryEntries( new File( mUpdatePath, zRelativeDirectoryPath ) ) ); |
109 |
98 |
|
} |
110 |
|
- |
for ( String zUnmatchedOriginalRelativePath : mOriginal.getRelativePaths() ) |
111 |
|
- |
{ |
|
99 |
+ |
for ( String zUnmatchedOriginalRelativePath : mOriginal.getRelativePaths() ) { |
112 |
100 |
|
processNoUpdateFile( zUnmatchedOriginalRelativePath ); |
113 |
101 |
|
} |
114 |
102 |
|
} |
115 |
103 |
|
|
116 |
|
- |
private void process( Queue<String> pDirectoryCollector, String pRelativeDirectoryPath, String[] pNames ) |
117 |
|
- |
{ |
118 |
|
- |
for ( String zName : pNames ) |
119 |
|
- |
{ |
|
104 |
+ |
private void process( Queue<String> pDirectoryCollector, String pRelativeDirectoryPath, String[] pNames ) { |
|
105 |
+ |
for ( String zName : pNames ) { |
120 |
106 |
|
String zRelativePath = relativePath( pRelativeDirectoryPath, zName ); |
121 |
107 |
|
File zFile = new File( mUpdatePath, zRelativePath ); |
122 |
|
- |
if ( zFile.isFile() ) |
123 |
|
- |
{ |
|
108 |
+ |
if ( zFile.isFile() ) { |
124 |
109 |
|
processUpdateFile( zRelativePath ); |
125 |
110 |
|
continue; |
126 |
111 |
|
} |
127 |
|
- |
if ( zFile.isDirectory() ) |
128 |
|
- |
{ |
|
112 |
+ |
if ( zFile.isDirectory() ) { |
129 |
113 |
|
pDirectoryCollector.add( zRelativePath ); |
130 |
114 |
|
continue; |
131 |
115 |
|
} |
|
@@ -133,100 +117,81 @@ |
133 |
117 |
|
} |
134 |
118 |
|
} |
135 |
119 |
|
|
136 |
|
- |
private static String[] getDirectoryEntries( File pDirectory ) |
137 |
|
- |
{ |
|
120 |
+ |
private static String[] getDirectoryEntries( File pDirectory ) { |
138 |
121 |
|
String[] zFiles = pDirectory.list(); |
139 |
|
- |
if ( zFiles != null ) |
140 |
|
- |
{ |
|
122 |
+ |
if ( zFiles != null ) { |
141 |
123 |
|
return zFiles; |
142 |
124 |
|
} |
143 |
125 |
|
throw new IllegalStateException( "Unable to get Directory Listing from: " + pDirectory.getAbsolutePath() ); |
144 |
126 |
|
} |
145 |
127 |
|
|
146 |
|
- |
private static String relativePath( String pRelativeDirectoryPath, String pName ) |
147 |
|
- |
{ |
|
128 |
+ |
private static String relativePath( String pRelativeDirectoryPath, String pName ) { |
148 |
129 |
|
return (pRelativeDirectoryPath.length() == 0) ? pName : pRelativeDirectoryPath + "/" + pName; |
149 |
130 |
|
} |
150 |
131 |
|
|
151 |
|
- |
private static class TargetTree |
152 |
|
- |
{ |
|
132 |
+ |
private static class TargetTree { |
153 |
133 |
|
private final Map<String, File> mEntries = Maps.newHashMap(); |
154 |
134 |
|
private final File mBasePath; |
155 |
135 |
|
|
156 |
|
- |
public TargetTree( File pBasePath ) |
157 |
|
- |
{ |
|
136 |
+ |
public TargetTree( File pBasePath ) { |
158 |
137 |
|
mBasePath = pBasePath; |
159 |
138 |
|
} |
160 |
139 |
|
|
161 |
|
- |
public void populate() |
162 |
|
- |
{ |
|
140 |
+ |
public void populate() { |
163 |
141 |
|
populate( "", getDirectoryEntries( mBasePath ) ); |
164 |
142 |
|
} |
165 |
143 |
|
|
166 |
|
- |
private void populate( String pRelativeDirectoryPath, String[] pNames ) |
167 |
|
- |
{ |
168 |
|
- |
for ( String zName : pNames ) |
169 |
|
- |
{ |
|
144 |
+ |
private void populate( String pRelativeDirectoryPath, String[] pNames ) { |
|
145 |
+ |
for ( String zName : pNames ) { |
170 |
146 |
|
String zRelativePath = relativePath( pRelativeDirectoryPath, zName ); |
171 |
147 |
|
File zFile = new File( mBasePath, zRelativePath ); |
172 |
|
- |
if ( zFile.isFile() ) |
173 |
|
- |
{ |
|
148 |
+ |
if ( zFile.isFile() ) { |
174 |
149 |
|
mEntries.put( zRelativePath, zFile ); |
175 |
150 |
|
continue; |
176 |
151 |
|
} |
177 |
|
- |
if ( !zFile.isDirectory() ) |
178 |
|
- |
{ |
|
152 |
+ |
if ( !zFile.isDirectory() ) { |
179 |
153 |
|
throw new IllegalStateException( "Neither a File or a Directory: " + zFile.getAbsolutePath() ); |
180 |
154 |
|
} |
181 |
155 |
|
populate( zRelativePath, getDirectoryEntries( zFile ) ); |
182 |
156 |
|
} |
183 |
157 |
|
} |
184 |
158 |
|
|
185 |
|
- |
public File getFile( String pRelativePath ) |
186 |
|
- |
{ |
|
159 |
+ |
public File getFile( String pRelativePath ) { |
187 |
160 |
|
return mEntries.get( pRelativePath ); |
188 |
161 |
|
} |
189 |
162 |
|
|
190 |
|
- |
public void removeEntry( String pRelativePath ) |
191 |
|
- |
{ |
|
163 |
+ |
public void removeEntry( String pRelativePath ) { |
192 |
164 |
|
mEntries.remove( pRelativePath ); |
193 |
165 |
|
} |
194 |
166 |
|
|
195 |
|
- |
public String[] getRelativePaths() |
196 |
|
- |
{ |
|
167 |
+ |
public String[] getRelativePaths() { |
197 |
168 |
|
return mEntries.keySet().toArray( new String[mEntries.size()] ); |
198 |
169 |
|
} |
199 |
170 |
|
|
200 |
|
- |
public void delete( String pRelativePath ) |
201 |
|
- |
{ |
|
171 |
+ |
public void delete( String pRelativePath ) { |
202 |
172 |
|
FileUtils.deleteIfExists( mEntries.remove( pRelativePath ) ); |
203 |
173 |
|
} |
204 |
174 |
|
|
205 |
|
- |
public void save( String pRelativePath, File pFile, byte[] pContents ) |
206 |
|
- |
{ |
|
175 |
+ |
public void save( String pRelativePath, File pFile, byte[] pContents ) { |
207 |
176 |
|
save( pFile, pContents ); |
208 |
177 |
|
removeEntry( pRelativePath ); |
209 |
178 |
|
} |
210 |
179 |
|
|
211 |
|
- |
public void save( String pRelativePath, byte[] pContents ) |
212 |
|
- |
{ |
|
180 |
+ |
public void save( String pRelativePath, byte[] pContents ) { |
213 |
181 |
|
save( new File( mBasePath, pRelativePath ), pContents ); |
214 |
182 |
|
} |
215 |
183 |
|
|
216 |
|
- |
private void save( File pFile, byte[] pContents ) |
217 |
|
- |
{ |
|
184 |
+ |
private void save( File pFile, byte[] pContents ) { |
218 |
185 |
|
FileUtils.store( pFile, pContents ); |
219 |
186 |
|
FileUtils.deleteIfExists( new File( pFile.getAbsolutePath() + ".bak" ) ); |
220 |
187 |
|
} |
221 |
188 |
|
} |
222 |
189 |
|
|
223 |
|
- |
private byte[] load( File pUpdateFile ) |
224 |
|
- |
{ |
|
190 |
+ |
private byte[] load( File pUpdateFile ) { |
225 |
191 |
|
return FileUtils.load( pUpdateFile, MAX_FILE_SIZE ); |
226 |
192 |
|
} |
227 |
193 |
|
|
228 |
|
- |
private boolean different( byte[] pBytes1, byte[] pBytes2 ) |
229 |
|
- |
{ |
|
194 |
+ |
private boolean different( byte[] pBytes1, byte[] pBytes2 ) { |
230 |
195 |
|
return !Arrays.equals( pBytes1, pBytes2 ); |
231 |
196 |
|
} |
232 |
197 |
|
|
|
@@ -235,19 +200,16 @@ |
235 |
200 |
|
// No 'Original' file... |
236 |
201 |
|
// Same - No Action taken... |
237 |
202 |
|
// Different... |
238 |
|
- |
private void processUpdateFile( String pRelativePath ) |
239 |
|
- |
{ |
|
203 |
+ |
private void processUpdateFile( String pRelativePath ) { |
240 |
204 |
|
File zUpdateFile = new File( mUpdatePath, pRelativePath ); |
241 |
205 |
|
byte[] zUpdateContents = load( zUpdateFile ); |
242 |
206 |
|
File zOriginalFile = mOriginal.getFile( pRelativePath ); |
243 |
|
- |
if ( zOriginalFile == null ) |
244 |
|
- |
{ |
|
207 |
+ |
if ( zOriginalFile == null ) { |
245 |
208 |
|
processUpdateButNoOriginal( pRelativePath, zUpdateFile, zUpdateContents ); |
246 |
209 |
|
return; |
247 |
210 |
|
} |
248 |
211 |
|
byte[] zOriginalContents = load( zOriginalFile ); |
249 |
|
- |
if ( different( zUpdateContents, zOriginalContents ) ) |
250 |
|
- |
{ |
|
212 |
+ |
if ( different( zUpdateContents, zOriginalContents ) ) { |
251 |
213 |
|
processUpdateDifferentThanOriginal( pRelativePath, zUpdateFile, zUpdateContents, zOriginalFile, zOriginalContents ); |
252 |
214 |
|
return; |
253 |
215 |
|
} |
|
@@ -263,20 +225,16 @@ |
263 |
225 |
|
// - Offer Delete 'Working'? |
264 |
226 |
|
// (if 'Working' is 0 length, then delete). |
265 |
227 |
|
// Delete 'Original'. |
266 |
|
- |
private void processNoUpdateFile( String pRelativePath ) |
267 |
|
- |
{ |
|
228 |
+ |
private void processNoUpdateFile( String pRelativePath ) { |
268 |
229 |
|
File zWorkingFile = mWorking.getFile( pRelativePath ); |
269 |
|
- |
if ( zWorkingFile != null ) |
270 |
|
- |
{ |
|
230 |
+ |
if ( zWorkingFile != null ) { |
271 |
231 |
|
File zOriginalFile = mOriginal.getFile( pRelativePath ); |
272 |
232 |
|
byte[] zWorkingContents = load( zWorkingFile ); |
273 |
233 |
|
byte[] zOriginalContents = load( zOriginalFile ); |
274 |
|
- |
if ( different( zOriginalContents, zWorkingContents ) ) |
275 |
|
- |
{ |
|
234 |
+ |
if ( different( zOriginalContents, zWorkingContents ) ) { |
276 |
235 |
|
checkWorkingChangedButNoUpdate( pRelativePath, zWorkingFile, zOriginalFile ); |
277 |
236 |
|
// ************************************* If Working is Empty, then complete merge! |
278 |
|
- |
if ( zWorkingFile.length() != 0 ) |
279 |
|
- |
{ |
|
237 |
+ |
if ( zWorkingFile.length() != 0 ) { |
280 |
238 |
|
// Ignore! |
281 |
239 |
|
mOriginal.removeEntry( pRelativePath ); |
282 |
240 |
|
mWorking.removeEntry( pRelativePath ); |
|
@@ -294,15 +252,12 @@ |
294 |
252 |
|
// b) No 'Working' file |
295 |
253 |
|
// - copy 'Update' to 'Working'. |
296 |
254 |
|
// Copy 'Update' to 'Original'. |
297 |
|
- |
private void processUpdateButNoOriginal( String pRelativePath, File pUpdateFile, byte[] pUpdateContents ) |
298 |
|
- |
{ |
|
255 |
+ |
private void processUpdateButNoOriginal( String pRelativePath, File pUpdateFile, byte[] pUpdateContents ) { |
299 |
256 |
|
File zWorkingFile = mWorking.getFile( pRelativePath ); |
300 |
|
- |
if ( zWorkingFile != null ) |
301 |
|
- |
{ |
|
257 |
+ |
if ( zWorkingFile != null ) { |
302 |
258 |
|
checkNewUpdateCollidesWithExistingWorking( pRelativePath, zWorkingFile, pUpdateFile ); |
303 |
259 |
|
// ************************************* If Working is Empty, then complete merge! |
304 |
|
- |
if ( zWorkingFile.length() != 0 ) |
305 |
|
- |
{ |
|
260 |
+ |
if ( zWorkingFile.length() != 0 ) { |
306 |
261 |
|
// Ignore! |
307 |
262 |
|
mWorking.removeEntry( pRelativePath ); |
308 |
263 |
|
return; |
|
@@ -320,26 +275,18 @@ |
320 |
275 |
|
// 'Update', 'Original', and 'Working'. |
321 |
276 |
|
// Copy 'Update' to 'Original'. |
322 |
277 |
|
private void processUpdateDifferentThanOriginal( String pRelativePath, File pUpdateFile, byte[] pUpdateContents, File pOriginalFile, |
323 |
|
- |
byte[] pOriginalContents ) |
324 |
|
- |
{ |
|
278 |
+ |
byte[] pOriginalContents ) { |
325 |
279 |
|
File zWorkingFile = mWorking.getFile( pRelativePath ); |
326 |
|
- |
if ( zWorkingFile == null ) |
327 |
|
- |
{ |
|
280 |
+ |
if ( zWorkingFile == null ) { |
328 |
281 |
|
mWorking.save( pRelativePath, zWorkingFile, pUpdateContents ); |
329 |
|
- |
} |
330 |
|
- |
else |
331 |
|
- |
{ |
|
282 |
+ |
} else { |
332 |
283 |
|
byte[] zWorkingContents = load( zWorkingFile ); |
333 |
|
- |
if ( !different( pOriginalContents, zWorkingContents ) ) |
334 |
|
- |
{ |
|
284 |
+ |
if ( !different( pOriginalContents, zWorkingContents ) ) { |
335 |
285 |
|
mWorking.save( pRelativePath, zWorkingFile, pUpdateContents ); |
336 |
|
- |
} |
337 |
|
- |
else |
338 |
|
- |
{ |
|
286 |
+ |
} else { |
339 |
287 |
|
checkUpdateAndWorkingDifferentThanOriginal_threeWayMerge( pRelativePath, zWorkingFile, pUpdateFile, pOriginalFile ); |
340 |
288 |
|
// ************************************* If Working changed, then complete merge! |
341 |
|
- |
if ( !different( zWorkingContents, load( zWorkingFile ) ) ) |
342 |
|
- |
{ |
|
289 |
+ |
if ( !different( zWorkingContents, load( zWorkingFile ) ) ) { |
343 |
290 |
|
// Ignore! |
344 |
291 |
|
mOriginal.removeEntry( pRelativePath ); |
345 |
292 |
|
mWorking.removeEntry( pRelativePath ); |
|
@@ -350,22 +297,19 @@ |
350 |
297 |
|
mOriginal.save( pRelativePath, pOriginalFile, pUpdateContents ); |
351 |
298 |
|
} |
352 |
299 |
|
|
353 |
|
- |
private void checkWorkingChangedButNoUpdate( String pRelativePath, File pWorkingFile, File pOriginalFile ) |
354 |
|
- |
{ |
|
300 |
+ |
private void checkWorkingChangedButNoUpdate( String pRelativePath, File pWorkingFile, File pOriginalFile ) { |
355 |
301 |
|
System.out.println( "Working Changed But No Update: " + pRelativePath + "\n" + |
356 |
302 |
|
" So Working shouldn't exist - Empty Working to complete merge..." ); |
357 |
303 |
|
ShellUtils.shell( mEditor, pWorkingFile.getAbsolutePath(), pOriginalFile.getAbsolutePath() ); |
358 |
304 |
|
} |
359 |
305 |
|
|
360 |
|
- |
private void checkNewUpdateCollidesWithExistingWorking( String pRelativePath, File pWorkingFile, File pUpdateFile ) |
361 |
|
- |
{ |
|
306 |
+ |
private void checkNewUpdateCollidesWithExistingWorking( String pRelativePath, File pWorkingFile, File pUpdateFile ) { |
362 |
307 |
|
System.out.println( "New Update Collides With Existing Working: " + pRelativePath + "\n" + |
363 |
308 |
|
" Move contents of Working to new file (leaving Working empty) to complete merge..." ); |
364 |
309 |
|
ShellUtils.shell( mEditor, pWorkingFile.getAbsolutePath(), pUpdateFile.getAbsolutePath() ); |
365 |
310 |
|
} |
366 |
311 |
|
|
367 |
|
- |
private void checkUpdateAndWorkingDifferentThanOriginal_threeWayMerge( String pRelativePath, File pWorkingFile, File pUpdateFile, File pOriginalFile ) |
368 |
|
- |
{ |
|
312 |
+ |
private void checkUpdateAndWorkingDifferentThanOriginal_threeWayMerge( String pRelativePath, File pWorkingFile, File pUpdateFile, File pOriginalFile ) { |
369 |
313 |
|
System.out.println( "Change Collition, both Update AND Working differ from Original: " + pRelativePath + "\n" + |
370 |
314 |
|
" Change Working to complete merge..." ); |
371 |
315 |
|
ShellUtils.shell( mEditor, pWorkingFile.getAbsolutePath(), pUpdateFile.getAbsolutePath(), pOriginalFile.getAbsolutePath() ); |