View Javadoc
1   package de.spiritscorp.datasync.io;
2   
3   /*-
4    * 		Data Sync
5    *
6    * 		Copyright ©   2022    The Spirit
7    * 		@email                        thespirit@spiritscorp.network
8    *
9    * 		This program is free software; you can redistribute it and/or modify
10   * 		it under the terms of the GNU General Public License as published by
11   * 		the Free Software Foundation; either version 3 of the License, or
12   * 		(at your option) any later version.
13   *
14   * 		This program is distributed in the hope that it will be useful,
15   * 		but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * 		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17   * 		See the GNU General Public License for more details.
18   *
19   * 		You should have received a copy of the GNU General Public License
20   * 		along with this program. If not, see <http://www.gnu.org/licenses/>.
21   */
22  
23  import java.io.IOException;
24  import java.io.OutputStream;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.Paths;
28  import java.nio.file.StandardOpenOption;
29  import java.nio.file.attribute.FileTime;
30  import java.util.HashMap;
31  import java.util.Map;
32  
33  import de.spiritscorp.datasync.model.FileAttributes;
34  import jakarta.json.Json;
35  import jakarta.json.JsonObject;
36  import jakarta.json.JsonObjectBuilder;
37  import jakarta.json.JsonReader;
38  import jakarta.json.JsonValue;
39  import jakarta.json.JsonWriter;
40  import jakarta.json.JsonWriterFactory;
41  import jakarta.json.stream.JsonGenerator;
42  
43  /**
44   * Isolated binary mapping engine handling structural cache entries matching runtime properties.
45   * * @author Tom Spirit
46   */
47  class IOSyncMap {
48  
49  	private final Path jobSyncMapPath;
50  
51  	/**
52  	 * Binds tracking matrices to unique physical layout footprints.
53  	 */
54  	IOSyncMap( String jobName ) {
55  		this.jobSyncMapPath = PreferenceManager.getInstance().getConfigPath().getParent().resolve( "syncMap_" + jobName + ".json" );
56  	}
57  
58  	boolean loadSyncMap( Map<Path, FileAttributes> syncMap ) {
59  		if( !syncMap.isEmpty() || !Files.exists( jobSyncMapPath ) ) { return false; }
60  		try( JsonReader jsonReader = Json.createReader( Files.newInputStream( jobSyncMapPath, StandardOpenOption.READ ) ) ) {
61  			final JsonObject jsonObject = jsonReader.readObject();
62  
63  			for( final Map.Entry<String, JsonValue> entry : jsonObject.entrySet() ) {
64  				final JsonObject objectEntry = entry.getValue().asJsonObject();
65  				final FileAttributes fileAttributes = new FileAttributes(
66  						Paths.get( objectEntry.getString( "relativeFilePath" ) ),
67  						objectEntry.getString( "createTimeString" ),
68  						FileTime.fromMillis( Long.parseLong( objectEntry.get( "createTime" ).toString() ) ),
69  						objectEntry.getString( "modTimeString" ),
70  						FileTime.fromMillis( Long.parseLong( objectEntry.get( "modTime" ).toString() ) ),
71  						Long.parseLong( objectEntry.get( "size" ).toString() ),
72  						objectEntry.getString( "fileHash" ) );
73  				syncMap.put( Paths.get( objectEntry.getString( "relativeFilePath" ) ), fileAttributes );
74  			}
75  			return true;
76  		}catch( final IllegalArgumentException | UnsupportedOperationException | SecurityException | IOException e ) {
77  			// Block 1: IO & File System Operations (Options, Permissions, Missing Files)
78  			Debug.printDebug( "[Error] File system or configuration failure while opening stream: %s", e.getMessage() );
79  			Debug.printException( this.getClass(), e );
80  			return false;
81  
82  		}catch( final jakarta.json.JsonException e ) {
83  			// Block 2: JSON Processing (Deals with both syntax/parsing and structural creation errors)
84  			Debug.printDebug( "[Error] JSON processing or syntax error: %s", e.getMessage() );
85  			Debug.printException( this.getClass(), e );
86  			return false;
87  
88  		}catch( final IllegalStateException e ) {
89  			// Block 3: Reader State Management (Reader already closed or multi-call violation)
90  			Debug.printDebug( "[Error] Parser state conflict: %s", e.getMessage() );
91  			Debug.printException( this.getClass(), e );
92  			return false;
93  		}
94  	}
95  
96  	void writeSyncMap( Map<Path, FileAttributes> syncMap ) {
97  		try( OutputStream os = Files.newOutputStream( jobSyncMapPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING ) ) {
98  			final HashMap<String, Boolean> config = new HashMap<>();
99  			config.put( JsonGenerator.PRETTY_PRINTING, true );
100 			final JsonWriterFactory jwf = Json.createWriterFactory( config );
101 
102 			final JsonObject jobObj = createSyncMap( syncMap );
103 			final JsonWriter jw = jwf.createWriter( os );
104 			jw.write( jobObj );
105 			jw.close();
106 		}catch( final IOException e ) {
107 			Debug.printException( this.getClass(), e );
108 		}
109 	}
110 
111 	private JsonObject createSyncMap( Map<Path, FileAttributes> syncMap ) {
112 		final JsonObjectBuilder jo = Json.createObjectBuilder();
113 		for( final Map.Entry<Path, FileAttributes> entry : syncMap.entrySet() ) {
114 			final JsonObject jo2 = Json.createObjectBuilder()
115 					.add( "relativeFilePath", entry.getValue().getRelativeFilePath().toString() )
116 					.add( "fileHash", entry.getValue().getFileHash() )
117 					.add( "modTimeString", entry.getValue().getModTimeString() )
118 					.add( "createTime", entry.getValue().getCreateTime().toMillis() )
119 					.add( "modTime", entry.getValue().getModTime().toMillis() )
120 					.add( "size", entry.getValue().getSize() )
121 					.add( "createTimeString", entry.getValue().getCreateTimeString() )
122 					.build();
123 			jo.add( entry.getValue().getRelativeFilePath().toString(), jo2 );
124 		}
125 		return jo.build();
126 	}
127 }