]> git.argeo.org Git - lgpl/argeo-commons.git/blob - fs/BasicSyncFileVisitor.java
Prepare next development cycle
[lgpl/argeo-commons.git] / fs / BasicSyncFileVisitor.java
1 package org.argeo.fs;
2
3 import java.io.IOException;
4 import java.nio.file.FileVisitResult;
5 import java.nio.file.Files;
6 import java.nio.file.Path;
7 import java.nio.file.SimpleFileVisitor;
8 import java.nio.file.StandardCopyOption;
9 import java.nio.file.attribute.BasicFileAttributes;
10 import java.nio.file.attribute.FileTime;
11
12 import org.argeo.sync.SyncResult;
13
14 /** Synchronises two directory structures. */
15 public class BasicSyncFileVisitor extends SimpleFileVisitor<Path> {
16 // TODO make it configurable
17 private boolean trace = false;
18
19 private final Path sourceBasePath;
20 private final Path targetBasePath;
21 private final boolean delete;
22 private final boolean recursive;
23
24 private SyncResult<Path> syncResult = new SyncResult<>();
25
26 public BasicSyncFileVisitor(Path sourceBasePath, Path targetBasePath, boolean delete, boolean recursive) {
27 this.sourceBasePath = sourceBasePath;
28 this.targetBasePath = targetBasePath;
29 this.delete = delete;
30 this.recursive = recursive;
31 }
32
33 @Override
34 public FileVisitResult preVisitDirectory(Path sourceDir, BasicFileAttributes attrs) throws IOException {
35 if (!recursive && !sourceDir.equals(sourceBasePath))
36 return FileVisitResult.SKIP_SUBTREE;
37 Path targetDir = toTargetPath(sourceDir);
38 Files.createDirectories(targetDir);
39 return FileVisitResult.CONTINUE;
40 }
41
42 @Override
43 public FileVisitResult postVisitDirectory(Path sourceDir, IOException exc) throws IOException {
44 if (delete) {
45 Path targetDir = toTargetPath(sourceDir);
46 for (Path targetPath : Files.newDirectoryStream(targetDir)) {
47 Path sourcePath = sourceDir.resolve(targetPath.getFileName());
48 if (!Files.exists(sourcePath)) {
49 try {
50 FsUtils.delete(targetPath);
51 deleted(targetPath);
52 } catch (Exception e) {
53 deleteFailed(targetPath, exc);
54 }
55 }
56 }
57 }
58 return FileVisitResult.CONTINUE;
59 }
60
61 @Override
62 public FileVisitResult visitFile(Path sourceFile, BasicFileAttributes attrs) throws IOException {
63 Path targetFile = toTargetPath(sourceFile);
64 try {
65 if (!Files.exists(targetFile)) {
66 Files.copy(sourceFile, targetFile);
67 added(sourceFile, targetFile);
68 } else {
69 if (shouldOverwrite(sourceFile, targetFile)) {
70 Files.copy(sourceFile, targetFile, StandardCopyOption.REPLACE_EXISTING);
71 }
72 }
73 } catch (Exception e) {
74 copyFailed(sourceFile, targetFile, e);
75 }
76 return FileVisitResult.CONTINUE;
77 }
78
79 protected boolean shouldOverwrite(Path sourceFile, Path targetFile) throws IOException {
80 long sourceSize = Files.size(sourceFile);
81 long targetSize = Files.size(targetFile);
82 if (sourceSize != targetSize) {
83 return true;
84 }
85 FileTime sourceLastModif = Files.getLastModifiedTime(sourceFile);
86 FileTime targetLastModif = Files.getLastModifiedTime(targetFile);
87 if (sourceLastModif.compareTo(targetLastModif) > 0)
88 return true;
89 return shouldOverwriteLaterSameSize(sourceFile, targetFile);
90 }
91
92 protected boolean shouldOverwriteLaterSameSize(Path sourceFile, Path targetFile) {
93 return false;
94 }
95
96 // @Override
97 // public FileVisitResult visitFileFailed(Path sourceFile, IOException exc) throws IOException {
98 // error("Cannot sync " + sourceFile, exc);
99 // return FileVisitResult.CONTINUE;
100 // }
101
102 private Path toTargetPath(Path sourcePath) {
103 Path relativePath = sourceBasePath.relativize(sourcePath);
104 Path targetPath = targetBasePath.resolve(relativePath.toString());
105 return targetPath;
106 }
107
108 public Path getSourceBasePath() {
109 return sourceBasePath;
110 }
111
112 public Path getTargetBasePath() {
113 return targetBasePath;
114 }
115
116 protected void added(Path sourcePath, Path targetPath) {
117 syncResult.getAdded().add(targetPath);
118 if (isTraceEnabled())
119 trace("Added " + sourcePath + " as " + targetPath);
120 }
121
122 protected void modified(Path sourcePath, Path targetPath) {
123 syncResult.getModified().add(targetPath);
124 if (isTraceEnabled())
125 trace("Overwritten from " + sourcePath + " to " + targetPath);
126 }
127
128 protected void copyFailed(Path sourcePath, Path targetPath, Exception e) {
129 syncResult.addError(sourcePath, targetPath, e);
130 if (isTraceEnabled())
131 error("Cannot copy " + sourcePath + " to " + targetPath, e);
132 }
133
134 protected void deleted(Path targetPath) {
135 syncResult.getDeleted().add(targetPath);
136 if (isTraceEnabled())
137 trace("Deleted " + targetPath);
138 }
139
140 protected void deleteFailed(Path targetPath, Exception e) {
141 syncResult.addError(null, targetPath, e);
142 if (isTraceEnabled())
143 error("Cannot delete " + targetPath, e);
144 }
145
146 /** Log error. */
147 protected void error(Object obj, Throwable e) {
148 System.err.println(obj);
149 e.printStackTrace();
150 }
151
152 protected boolean isTraceEnabled() {
153 return trace;
154 }
155
156 protected void trace(Object obj) {
157 System.out.println(obj);
158 }
159
160 public SyncResult<Path> getSyncResult() {
161 return syncResult;
162 }
163
164 }