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