View Javadoc
1   package de.spiritscorp.datasync;
2   
3   /*
4   	Data Sync
5   		Application to synchronize your data
6   
7   	@author Tom Spirit
8   	@date 16.12.2021
9   	@version	1.1.0.0-beta
10  	@email tomspirit@spiritscorp.network
11  
12  	Copyright ©
13  
14  This program is free software; you can redistribute it and/or modify
15  it under the terms of the GNU General Public License as published by
16  the Free Software Foundation; either version 3 of the License, or
17  (at your option) any later version.
18  
19  This program is distributed in the hope that it will be useful,
20  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  GNU General Public License for more details.
23  
24  You should have received a copy of the GNU General Public License
25  along with this program. If not, see <http://www.gnu.org/licenses/>.
26  */
27  
28  import java.nio.file.Path;
29  import java.time.LocalDateTime;
30  import java.time.ZoneId;
31  import java.time.format.DateTimeFormatter;
32  import java.time.format.FormatStyle;
33  import java.util.Locale;
34  
35  import javafx.application.Application;
36  
37  import de.spiritscorp.datasync.gui.Gui;
38  import de.spiritscorp.datasync.io.Debug;
39  import de.spiritscorp.datasync.io.PreferenceManager;
40  
41  /**
42   * Main application entry point responsible for runtime arguments parsing,
43   * debug subsystems orchestration, and boots-strapping the JavaFX platform lifecycle.
44   *
45   * @author Tom Spirit
46   */
47  public final class Main { // NOPMD ShortClassName
48  
49  	/**
50  	 * The current version of the application in semantic format, including the development stage.
51  	 */
52  	public static final String VERSION = "V1.1.0.0-beta";
53  
54  	/** Debug mode */
55  	private static boolean debug;
56  	/** Boot delay mode */
57  	private static boolean firstStart;
58  	/** Helper to jump over the next argument if config dir needs 2 */
59  	private static boolean folderJumpArg;
60  	/** Will be set debug to file */
61  	private static boolean toFile;
62  
63  	/**
64  	 * Application entry point. Orchestrates the initial boot sequence by parsing
65  	 * command-line options and bootstrapping the underlying JavaFX application subsystem.
66  	 * <p>
67  	 * This method delegates argument parsing to {@link #parseArguments(String [] args)} before
68  	 * handing over control to the JavaFX application lifecycle via {@link Application#launch(Class, String [] )}.
69  	 * </p>
70  	 *
71  	 * @param args Runtime command-line execution flags and configuration parameters.
72  	 */
73  	public static void main( final String... args ) {
74  		parseArguments( args );
75  		Locale.setDefault( Locale.GERMANY );
76  		Application.launch( Gui.class, args );
77  	}
78  
79  	/**
80  	 * Checks whether the application was launched automatically by the operating system's
81  	 * startup/autostart routine.
82  	 * <p>
83  	 * When {@code true}, a timer delay is initialized to reduce system resource contention
84  	 * during OS boot, and the application is instructed to start minimized in the background.
85  	 * This flag is managed and set automatically during the autostart registration process.
86  	 *
87  	 * @return {@code true} if the application was triggered via OS autostart;
88  	 *         {@code false} if it was started manually by the user.
89  	 */
90  	public static boolean isFirstStart() { return firstStart; }
91  
92  	/**
93  	 * Checks whether the debug mode is active for extended verbose and additional runtime diagnostic outputs.
94  	 *
95  	 * @return {@code true} if advanced diagnostic information should be emitted;
96  	 *         {@code false} otherwise.
97  	 */
98  	public static boolean isDebug() { return debug; }
99  
100 	/**
101 	 * Checks whether the debug mode is active for extended verbose and additional runtime diagnostic outputs.
102 	 *
103 	 * @return {@code true} if advanced diagnostic information should be emitted;
104 	 *         {@code false} otherwise.
105 	 */
106 	public static boolean isDebugToFile() { return toFile; }
107 
108 	/**
109 	 * Evaluates and processes runtime command-line arguments in a single pass to configure
110 	 * global application states and subsystem parameters.
111 	 * <p>
112 	 * The parser evaluates standard flags for debugging, diagnostic routing, execution delays,
113 	 * and configuration root directory adjustments. For key-value configurations, it supports
114 	 * both standard inline assignment (e.g., {@code --config-dir=/path}) and safe whitespace
115 	 * lookahead token isolation (e.g., {@code -c /path}), ensuring subsequent flags are not
116 	 * accidentally consumed as paths.
117 	 * </p>
118 	 *
119 	 * @param args An array of string arguments passed to the application upon startup.
120 	 *             Null elements within the array are safely ignored.
121 	 */
122 	static void parseArguments( final String... args ) {
123 
124 		final PreferenceManager manager = PreferenceManager.getInstance();
125 
126 		// Single-pass argument processing to minimize iteration overhead
127 		for( int i = 0; i < args.length; i++ ) {
128 			if( args[i] == null || folderJumpArg ) {
129 				folderJumpArg = false;
130 				continue;
131 			}
132 			evaluateArgumentFlags( args, i, manager );
133 		}
134 		if( toFile ) Debug.setDebugToFile();
135 		// Initialize debug diagnostics if debog is enabled
136 		if( debug ) initializeDebugDiagnostics( manager.getConfigPath() );
137 	}
138 
139 	/**
140 	 * Resets the global execution states and diagnostic tracking flags to their
141 	 * initial default values.
142 	 * <p>
143 	 * This helper method is intended exclusively for test isolation purposes (e.g., within
144 	 * {@code @BeforeEach} setup methods) to clear out internal static modifications
145 	 * between consecutive test executions and guarantee a deterministic environment.
146 	 * </p>
147 	 */
148 	static void resetForTesting() {
149 		debug = false;
150 		firstStart = false;
151 	}
152 
153 	/**
154 	 * Evaluates individual argument flags and updates global execution variables.
155 	 */
156 	private static void evaluateArgumentFlags( final String[] args, final int currentIndex, final PreferenceManager manager ) {
157 		final String arg = args[currentIndex].trim();
158 		final CLIFlags flag = CLIFlags.fromArgument( arg );
159 		switch( flag ) {
160 			case CONFIG_DIR -> handleConfigDirectoryArgument( args, currentIndex, manager );
161 			case BOOT_DELAY -> firstStart = true;
162 			case DEBUG -> debug = true;
163 			case DEBUG_TO_FILE -> {
164 				toFile = true;
165 				debug = true;
166 			}
167 			default -> {
168 				// No match found, skip silently
169 			}
170 		}
171 	}
172 
173 	/**
174 	 * Handles the logic for extracting and setting the configuration directory path.
175 	 *
176 	 * @param args         The command-line arguments.
177 	 * @param currentIndex The current index in the arguments array.
178 	 * @param manager      The preference manager instance.
179 	 */
180 	private static void handleConfigDirectoryArgument( final String[] args, final int currentIndex, final PreferenceManager manager ) {
181 		final String arg = args[currentIndex].trim();
182 		final String generalArg = arg.toLowerCase( Locale.ROOT );
183 		String configFolder = "";
184 		if( generalArg.contains( "=" ) ) {
185 			configFolder = arg.substring( arg.indexOf( '=' ) + 1 );
186 		}else if( currentIndex + 1 < args.length && !args[currentIndex + 1].startsWith( "-" ) ) {
187 			// Safeguard: Only consume next argument if it's not another flag
188 			configFolder = args[currentIndex + 1].trim();
189 			folderJumpArg = true;
190 		}
191 		if( !configFolder.isBlank() ) {
192 			manager.initGlobalRootConfigPath( Path.of( configFolder ) );
193 		}
194 	}
195 
196 	/**
197 	 * Initializes debug diagnostics with application information.
198 	 *
199 	 * @param manager The preference manager instance.
200 	 */
201 	private static void initializeDebugDiagnostics( final Path configPath ) {
202 		Debug.printDebugTimeless( "%nDEBUG BEGIN -> [%s]: %s",
203 				System.getProperty( "app.instance.name", "Standard Instance" ),
204 				LocalDateTime.now( ZoneId.systemDefault() ).format( DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL, FormatStyle.SHORT ) ) );
205 		Debug.printDebug( "[Info] Data Sync Application initialized. Beginning system initialization." );
206 		Debug.printDebug( "[Setup] Set config root path -> %s", configPath.toString() );
207 	}
208 }