-
Notifications
You must be signed in to change notification settings - Fork 0
File and Path Utilities
Relevant source files
This document covers the FileUtil class, which provides cross-platform path management and file I/O operations for the MouseMacros application. FileUtil abstracts platform-specific storage locations (Windows, macOS, Linux) and standardizes file operations by enforcing UTF-8 encoding, automatic directory creation, and consistent error handling through IOException propagation.
For configuration file management that uses FileUtil, see page 5.1 (ConfigManager). For other utility classes, see page 8.2 (Component Utilities), page 8.3 (System Integration Utilities), and page 8.4 (Logging and Window Management).
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L1-L61
The FileUtil class src/io/github/samera2022/mouse_macros/util/FileUtil.java L10
is a static utility class located in the io.github.samera2022.mouse_macros.util package. It provides four core operations:
- Cross-platform path resolution - Determines OS-specific application data directories
- Reading file content as a UTF-8 string
- Writing string content to a file with UTF-8 encoding
- Listing file names in a directory
All file I/O operations explicitly use StandardCharsets.UTF_8 to ensure consistent character encoding across different operating systems and locales. This is critical for handling localized text in configuration files and language resources.
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L1-L61
The following diagram maps the public API surface of FileUtil to its implementation details:
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L12-L61
The readFile(String path) method src/io/github/samera2022/mouse_macros/util/FileUtil.java L13-L24
reads a file's entire content into a String:
| Aspect | Behavior |
|---|---|
| Return Value | File content as UTF-8 string, or null if file doesn't exist |
| Encoding |
StandardCharsets.UTF_8 via InputStreamReader
|
| Line Handling | Appends \n after each line, including the last line |
| Exception | Throws IOException for I/O errors (not file-not-found) |
| Resource Management | Uses try-with-resources for automatic stream closure |
Implementation Note: The method checks file.exists() before attempting to read, returning null for non-existent files rather than throwing an exception. This allows callers to distinguish between "file not found" and "I/O error during read".
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L13-L24
The writeFile(String path, String content) method src/io/github/samera2022/mouse_macros/util/FileUtil.java L27-L34
writes string content to a file with automatic directory creation:
| Aspect | Behavior |
|---|---|
| Directory Creation | Calls parent.mkdirs() if parent directory doesn't exist |
| Encoding |
StandardCharsets.UTF_8 via OutputStreamWriter
|
| Overwrite Behavior | Overwrites existing files completely (no append mode) |
| Exception | Throws IOException for any I/O errors |
| Resource Management | Uses try-with-resources for automatic stream closure |
Implementation Details:
- Lines 28-30: Extracts parent directory and creates it if necessary
- Lines 31-33: Opens
FileOutputStream(which truncates existing files) and writes content
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L27-L34
The listFileNames(String dirPath) method src/io/github/samera2022/mouse_macros/util/FileUtil.java L37-L47
returns an array of file names (without paths) in a directory:
| Aspect | Behavior |
|---|---|
| Return Value |
String[] containing only file names (e.g., "config.cfg"), not full paths |
| Non-Existent Directory | Returns empty array new String[0]
|
| Non-Directory Path | Returns empty array new String[0]
|
| Null List | Returns empty array if listFiles() returns null
|
| Exception | Does not throw exceptions; returns empty array for any error condition |
Usage Pattern: This method is used by ConfigManager.getAvailableLangs() to enumerate language files in the lang/ directory during development mode.
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L37-L47
The getLocalStoragePath() method src/io/github/samera2022/mouse_macros/util/FileUtil.java L50-L61
determines the platform-specific directory where MouseMacros stores its configuration and cache files:
| Operating System | Storage Location | Environment/System Property Used |
|---|---|---|
| Windows | %LOCALAPPDATA%\MouseMacros |
System.getenv("LOCALAPPDATA") |
| macOS | ~/Library/Application Support/MouseMacros |
System.getProperty("user.home") |
| Linux/Unix | ~/.local/share/MouseMacros |
System.getProperty("user.home") |
Platform Detection Logic:
Implementation Details:
- Line 51: Retrieves OS name via
System.getProperty("os.name")and converts to lowercase for case-insensitive comparison - Lines 53-55: Windows detection uses
LOCALAPPDATAenvironment variable - Lines 56-57: macOS follows Apple's Application Support directory convention
- Lines 58-60: Linux/Unix follows XDG Base Directory specification
Usage: This method is used by ConfigManager to initialize CONFIG_DIR, which determines where config.cfg and cache.json are stored. It is also referenced in the application entry point src/io/github/samera2022/mouse_macros/MouseMacro.java
for native library path configuration.
Return Type: Returns java.nio.file.Path for convenient path manipulation with NIO.2 APIs.
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L50-L61, src/io/github/samera2022/mouse_macros/constant/OtherConsts.java
FileUtil enforces UTF-8 encoding for all file I/O operations to ensure proper handling of international characters:
Both read and write operations explicitly specify StandardCharsets.UTF_8 when constructing their respective stream wrappers:
-
Read: src/io/github/samera2022/mouse_macros/util/FileUtil.java L16 -
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8) -
Write: src/io/github/samera2022/mouse_macros/util/FileUtil.java L31 -
new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)
This is essential for:
- Configuration files containing user-defined paths with non-ASCII characters
- Language files (
en_us.json,zh_cn.json) containing localized strings - Macro file metadata that may include Unicode descriptions
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L16, src/io/github/samera2022/mouse_macros/util/FileUtil.java L31
The writeFile method automatically creates parent directories if they don't exist src/io/github/samera2022/mouse_macros/util/FileUtil.java L29-L30
:
Behavior:
- Extracts parent directory using
file.getParentFile() - Checks if parent is non-null and doesn't exist
- Calls
parent.mkdirs()to create the entire directory hierarchy - Proceeds with file write operation
Example: Writing to D:/Users/User/AppData/MouseMacros/config.cfg automatically creates D:/Users/User/AppData/MouseMacros/ if it doesn't exist.
This eliminates the need for callers to manually ensure directory existence before writing files.
Note: The getLocalStoragePath() method only returns the path; it does not create the directory. Directory creation happens automatically when writeFile() is called for configuration or cache files.
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L27-L34, src/io/github/samera2022/mouse_macros/util/FileUtil.java L50-L61
ConfigManager is the primary consumer of FileUtil, using it for configuration persistence:
| Operation | FileUtil Method | File Path | Purpose |
|---|---|---|---|
| Get config directory | getLocalStoragePath() |
N/A | Determine platform-specific storage location |
| Load config | readFile() |
CONFIG_PATH |
Read config.cfg as JSON string |
| Save config | writeFile() |
CONFIG_PATH |
Write JSON-serialized config |
| List languages | listFileNames() |
"lang" |
Enumerate available .json language files |
Implementation Details:
-
Initialize Config Directory: * Line 26:
ConfigManagerinitializesCONFIG_DIRstatic field by callingFileUtil.getLocalStoragePath().toString()* Line 27:CONFIG_PATHis constructed asCONFIG_DIR + "\\config.cfg"* This ensures configuration is stored in the platform-appropriate location -
Load Config: * Line 51: Calls
FileUtil.readFile(CONFIG_PATH)to read JSON string * Lines 52-56: If result isnullor empty, creates defaultConfiginstance and saves it * Lines 58-62: CatchesIOExceptionand creates/saves default config on error * Line 57: Uses Gson to deserialize JSON toConfigobject -
Save Config: * Line 71: Serializes
Configobject to JSON using Gson with pretty printing * Line 72: CallsFileUtil.writeFile(CONFIG_PATH, json)to persist * Lines 73-75: Prints stack trace ifIOExceptionoccurs (non-fatal) -
Get Available Languages: * Lines 82-91: In development mode (detected by
Localizer.isDevMode()), callsFileUtil.listFileNames("lang")to list language files * Lines 86-90: Strips.jsonextensions to get language codes (e.g.,"en_us","zh_cn") * Lines 93-130: In production mode, reads from JAR resources instead
Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L26-L132, src/io/github/samera2022/mouse_macros/util/FileUtil.java L49-L61
FileUtil employs a dual error handling strategy based on operation semantics:
| Method | Error Handling | Rationale |
|---|---|---|
readFile() |
Returns null for non-existent files; throws IOException for I/O errors |
Distinguishes between "file not found" (expected) and "I/O failure" (unexpected) |
writeFile() |
Throws IOException for any error |
Write failures are always unexpected and should be handled by caller |
listFileNames() |
Returns empty array for any error | Non-existent or inaccessible directories should not crash the application |
getLocalStoragePath() |
Never throws exceptions; returns platform-specific path | Path determination should always succeed using system properties |
Both readFile and writeFile declare throws IOException in their signatures, allowing callers to handle errors appropriately:
ConfigManager Example:
This pattern ensures that configuration loading failures result in default settings rather than application crashes.
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L13, src/io/github/samera2022/mouse_macros/util/FileUtil.java L27
The following diagram shows how FileUtil fits into the larger codebase architecture:
Notable Architecture Decision: ConfigManager uses FileUtil for config.cfg operations and path determination, but the codebase uses CacheManager (not shown in this utility class) for cache.json operations. This separation suggests different persistence strategies for configuration vs. runtime cache.
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L1-L61, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L1-L133
FileUtil provides a minimal, UTF-8-enforced file I/O abstraction used primarily by ConfigManager for configuration persistence. Its design prioritizes:
- Encoding Consistency: All operations use UTF-8 explicitly
- Caller Convenience: Automatic directory creation on write
-
Error Transparency:
IOExceptionpropagation for unexpected errors,null/empty-array returns for expected conditions - Resource Safety: Try-with-resources ensures stream closure
The class serves as a thin wrapper over Java's standard I/O classes, eliminating boilerplate while enforcing best practices.
Sources: src/io/github/samera2022/mouse_macros/util/FileUtil.java L1-L43, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L1-L146