1 package de.spiritscorp.datasync.io;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import java.io.BufferedReader;
24 import java.io.BufferedWriter;
25 import java.io.IOException;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.nio.file.StandardOpenOption;
30 import java.util.ArrayList;
31 import java.util.Map;
32
33 import de.spiritscorp.datasync.BgTime;
34 import de.spiritscorp.datasync.ScanType;
35 import de.spiritscorp.datasync.model.FileAttributes;
36 import de.spiritscorp.datasync.model.Model;
37 import jakarta.json.Json;
38 import jakarta.json.JsonArray;
39 import jakarta.json.JsonArrayBuilder;
40 import jakarta.json.JsonObject;
41 import jakarta.json.JsonObjectBuilder;
42 import jakarta.json.JsonValue;
43
44
45
46
47
48
49 public final class Preference {
50
51 private String jobName;
52 private final IOSyncMap ioSyncMap;
53 private final Path jobScanTimePath;
54
55 private ArrayList<Path> sourcePaths = new ArrayList<>();
56 private ArrayList<Path> destPaths = new ArrayList<>();
57 private final Map<Path, FileAttributes> syncMap = Model.createMap();
58
59 private static final String TRASHBIN_STRING = "Papierkorb";
60 private Path startSourcePath = PreferenceManager.DATASYNC_HOME;
61 private Path startDestPath = PreferenceManager.DATASYNC_HOME;
62 private Path trashbinPath = startDestPath.resolve( TRASHBIN_STRING );
63
64 private ScanType scanMode = ScanType.FLAT_SCAN;
65 private BgTime bgTime = BgTime.DAYLY;
66
67 private boolean logOn = true;
68 private boolean trashbin = true;
69 private boolean subDir;
70 private boolean autoDel;
71 private boolean autoSync;
72 private boolean bgSync;
73 private boolean autoBgDel;
74
75 private Preference( String jobName ) {
76 this.jobName = jobName;
77 sourcePaths.add( startSourcePath );
78 destPaths.add( startDestPath );
79 final String safeName = jobName.replaceAll( "[^a-zA-Z0-9-_]", "_" );
80 this.ioSyncMap = new IOSyncMap( safeName );
81 this.jobScanTimePath = PreferenceManager.getInstance().getConfigPath().getParent().resolve( "lastScanTime_" + safeName );
82 }
83
84
85
86
87
88
89
90
91 static Preference createSinglePreference( String jobName ) {
92 return new Preference( jobName );
93 }
94
95
96
97
98 JsonObject serialize() {
99 final JsonObjectBuilder builder = Json.createObjectBuilder();
100
101 final JsonArrayBuilder srcArr = Json.createArrayBuilder();
102 for( final Path p : sourcePaths )
103 srcArr.add( p.toString() );
104
105 final JsonArrayBuilder destArr = Json.createArrayBuilder();
106 for( final Path p : destPaths )
107 destArr.add( p.toString() );
108
109 builder.add( "sourcePaths", srcArr.build() )
110 .add( "destPaths", destArr.build() )
111 .add( "startSourcePath", startSourcePath.toString() )
112 .add( "startDestPath", startDestPath.toString() )
113 .add( "trashbinPath", trashbinPath.toString() )
114 .add( "scanMode", scanMode.getDescription() )
115 .add( "bgTime", bgTime.getName() )
116 .add( "logOn", logOn )
117 .add( "subDir", subDir )
118 .add( "autoDel", autoDel )
119 .add( "autoBgDel", autoBgDel )
120 .add( "autoSync", autoSync )
121 .add( "bgSync", bgSync )
122 .add( "trashbin", trashbin );
123
124 return builder.build();
125 }
126
127
128
129
130
131
132
133
134
135 void deserialize( JsonObject json ) throws ConfigException {
136 if( json == null ) { throw new ConfigException( "JSON payload context is null." ); }
137
138 try {
139
140 sourcePaths.clear();
141 final JsonArray srcArr = json.getJsonArray( "sourcePaths" );
142 if( srcArr != null ) {
143 for( final JsonValue v : srcArr ) {
144
145 final Path p = Paths.get( v.toString().replace( "\"", "" ) );
146
147 if( Files.exists( p ) && Files.isDirectory( p ) ) {
148 sourcePaths.add( p );
149 }else {
150 Debug.printDebug( "[Preference] Warning: Source path no longer available on filesystem: " + p );
151 }
152 }
153 }
154
155
156 destPaths.clear();
157 final JsonArray destArr = json.getJsonArray( "destPaths" );
158 if( destArr != null ) {
159 for( final JsonValue v : destArr ) {
160 final Path p = Paths.get( v.toString().replace( "\"", "" ) );
161
162 destPaths.add( p );
163 }
164 }
165
166
167 if( json.containsKey( "startSourcePath" ) ) {
168 final Path p = Paths.get( json.getString( "startSourcePath" ) );
169 this.startSourcePath = Files.exists( p ) ? p : PreferenceManager.DATASYNC_HOME;
170 }
171
172 if( json.containsKey( "startDestPath" ) ) {
173 final Path p = Paths.get( json.getString( "startDestPath" ) );
174 setStartDestPath( p );
175 }
176
177 if( json.containsKey( "trashbinPath" ) ) {
178 this.trashbinPath = Paths.get( json.getString( "trashbinPath" ) );
179 }
180
181
182
183 if( sourcePaths.isEmpty() || destPaths.isEmpty() ) {
184 final Path userHome = PreferenceManager.DATASYNC_HOME;
185 if( sourcePaths.isEmpty() ) sourcePaths.add( userHome );
186 if( destPaths.isEmpty() ) destPaths.add( userHome );
187 this.startSourcePath = userHome;
188 this.startDestPath = userHome;
189 this.trashbinPath = userHome.resolve( TRASHBIN_STRING );
190 }
191
192
193 if( json.containsKey( "scanMode" ) ) {
194 final ScanType parsed = ScanType.get( json.getString( "scanMode" ) );
195 this.scanMode = ( parsed != null ) ? parsed : ScanType.FLAT_SCAN;
196 }
197 if( json.containsKey( "bgTime" ) ) {
198 final BgTime parsed = BgTime.get( json.getString( "bgTime" ) );
199 this.bgTime = ( parsed != null ) ? parsed : BgTime.DAYLY;
200 }
201
202
203
204 this.logOn = getSafeBoolean( json, "logOn", true );
205 this.trashbin = getSafeBoolean( json, "trashbin", true );
206 this.subDir = getSafeBoolean( json, "subDir", false );
207 this.autoDel = getSafeBoolean( json, "autoDel", false );
208 this.autoBgDel = getSafeBoolean( json, "autoBgDel", false );
209 this.autoSync = getSafeBoolean( json, "autoSync", false );
210 this.bgSync = getSafeBoolean( json, "bgSync", false );
211
212
213 ioSyncMap.loadSyncMap( syncMap );
214
215 }catch( final Exception e ) {
216
217 throw new ConfigException( "JSON payload structure is corrupt or mismatched.", e );
218 }
219 }
220
221
222
223
224
225
226
227
228
229
230
231 private boolean getSafeBoolean( JsonObject json, String key, boolean defaultValue ) {
232 if( !json.containsKey( key ) ) { return defaultValue; }
233 try {
234 return json.getBoolean( key );
235 }catch( final ClassCastException e ) {
236 Debug.printDebug( "[Preference] Warning: Invalid data type mapping tracking key '%s'. Falling back to default: %s", key, defaultValue );
237 return defaultValue;
238 }
239 }
240
241
242
243
244 public void saveLastScanTime() {
245 try( BufferedWriter bw = Files.newBufferedWriter( jobScanTimePath,
246 StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING ) ) {
247 bw.write( String.valueOf( System.currentTimeMillis() ) );
248 }catch( final IOException e ) {
249 Debug.printException( this.getClass(), e );
250 }
251 }
252
253
254
255
256
257
258 public long getLastScanTime() {
259 if( !Files.exists( jobScanTimePath ) ) { return 0; }
260 try( BufferedReader br = Files.newBufferedReader( jobScanTimePath ) ) {
261 final String str = br.readLine();
262 return ( str != null ) ? Long.parseLong( str.trim() ) : 0;
263 }catch( final IOException | NumberFormatException e ) {
264 return 0;
265 }
266 }
267
268 public void writeSyncMap() {
269 ioSyncMap.writeSyncMap( syncMap );
270 }
271
272
273 public String getJobName() { return jobName; }
274
275 void setJobNameFromManager( String newName ) { this.jobName = newName; }
276
277 public ArrayList<Path> getSourcePath() { return sourcePaths; }
278
279 public void setSourcePath( ArrayList<Path> paths ) { this.sourcePaths = paths; }
280
281 public ArrayList<Path> getDestPath() { return destPaths; }
282
283 public void setDestPath( ArrayList<Path> paths ) { this.destPaths = paths; }
284
285 public Path getStartSourcePath() { return startSourcePath; }
286
287 public void setStartSourcePath( Path p ) { this.startSourcePath = p; }
288
289 public Path getStartDestPath() { return startDestPath; }
290
291 public void setStartDestPath( Path p ) {
292 this.startDestPath = p;
293 this.trashbinPath = p.resolve( TRASHBIN_STRING );
294 }
295
296 public Map<Path, FileAttributes> getSyncMap() { return syncMap; }
297
298 public ScanType getScanMode() { return scanMode; }
299
300 public void setScanMode( ScanType mode ) { this.scanMode = mode; }
301
302 public BgTime getBgTime() { return bgTime; }
303
304 public void setBgTime( BgTime time ) { this.bgTime = time; }
305
306 public boolean isLogOn() { return logOn; }
307
308 public void setLogOn( boolean logOn ) { this.logOn = logOn; }
309
310 public boolean isSubDir() { return subDir; }
311
312 public void setSubDir( boolean subDir ) { this.subDir = subDir; }
313
314 public boolean isAutoDel() { return autoDel; }
315
316 public void setAutoDel( boolean autoDel ) { this.autoDel = autoDel; }
317
318 public boolean isAutoBgDel() { return autoBgDel; }
319
320 public void setAutoBgDel( boolean autoBgDel ) { this.autoBgDel = autoBgDel; }
321
322 public boolean isAutoSync() { return autoSync; }
323
324 public void setAutoSync( boolean autoSync ) { this.autoSync = autoSync; }
325
326 public boolean isBgSync() { return bgSync; }
327
328 public void setBgSync( boolean bgSync ) { this.bgSync = bgSync; }
329
330 public boolean isTrashbin() { return trashbin; }
331
332 public void setTrashbin( boolean trashbin ) { this.trashbin = trashbin; }
333
334 public Path getTrashbinPath() { return trashbinPath; }
335
336 }