# File and Path Utilities > **Relevant source files** > * [src/io/github/samera2022/mouse_macros/Localizer.java](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/Localizer.java) > * [src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/listener/GlobalMouseListener.java) > * [src/io/github/samera2022/mouse_macros/manager/ConfigManager.java](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/manager/ConfigManager.java) > * [src/io/github/samera2022/mouse_macros/util/FileUtil.java](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java) ## Purpose and Scope 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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L1-L61) --- ## Overview The `FileUtil` class [src/io/github/samera2022/mouse_macros/util/FileUtil.java L10](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L10-L10) is a static utility class located in the `io.github.samera2022.mouse_macros.util` package. It provides four core operations: 1. **Cross-platform path resolution** - Determines OS-specific application data directories 2. **Reading file content** as a UTF-8 string 3. **Writing string content** to a file with UTF-8 encoding 4. **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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L1-L61) ## FileUtil API Structure 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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L12-L61) --- ## Core Operations ### Read File The `readFile(String path)` method [src/io/github/samera2022/mouse_macros/util/FileUtil.java L13-L24](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L13-L24) ### Write File The `writeFile(String path, String content)` method [src/io/github/samera2022/mouse_macros/util/FileUtil.java L27-L34](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L27-L34) ### List File Names The `listFileNames(String dirPath)` method [src/io/github/samera2022/mouse_macros/util/FileUtil.java L37-L47](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L37-L47) --- ### Get Local Storage Path The `getLocalStoragePath()` method [src/io/github/samera2022/mouse_macros/util/FileUtil.java L50-L61](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/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 `LOCALAPPDATA` environment 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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L50-L61), [src/io/github/samera2022/mouse_macros/constant/OtherConsts.java](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/constant/OtherConsts.java) --- ## UTF-8 Encoding Enforcement `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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L16-L16) - `new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)` * **Write:** [src/io/github/samera2022/mouse_macros/util/FileUtil.java L31](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L31-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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L16-L16), [src/io/github/samera2022/mouse_macros/util/FileUtil.java L31](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L31-L31) --- ## Directory Management The `writeFile` method automatically creates parent directories if they don't exist [src/io/github/samera2022/mouse_macros/util/FileUtil.java L29-L30](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L27-L34), [src/io/github/samera2022/mouse_macros/util/FileUtil.java L50-L61](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L50-L61) --- ## Usage in ConfigManager `ConfigManager` is the primary consumer of `FileUtil`, using it for configuration persistence: ``` ``` ### ConfigManager Usage Patterns | 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:** 1. **Initialize Config Directory:** * Line 26: `ConfigManager` initializes `CONFIG_DIR` static field by calling `FileUtil.getLocalStoragePath().toString()` * Line 27: `CONFIG_PATH` is constructed as `CONFIG_DIR + "\\config.cfg"` * This ensures configuration is stored in the platform-appropriate location 2. **Load Config:** * Line 51: Calls `FileUtil.readFile(CONFIG_PATH)` to read JSON string * Lines 52-56: If result is `null` or empty, creates default `Config` instance and saves it * Lines 58-62: Catches `IOException` and creates/saves default config on error * Line 57: Uses Gson to deserialize JSON to `Config` object 3. **Save Config:** * Line 71: Serializes `Config` object to JSON using Gson with pretty printing * Line 72: Calls `FileUtil.writeFile(CONFIG_PATH, json)` to persist * Lines 73-75: Prints stack trace if `IOException` occurs (non-fatal) 4. **Get Available Languages:** * Lines 82-91: In development mode (detected by `Localizer.isDevMode()`), calls `FileUtil.listFileNames("lang")` to list language files * Lines 86-90: Strips `.json` extensions 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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/manager/ConfigManager.java#L26-L132), [src/io/github/samera2022/mouse_macros/util/FileUtil.java L49-L61](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L49-L61) --- ## Error Handling Strategy `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 | ### IOException Propagation 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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L13-L13), [src/io/github/samera2022/mouse_macros/util/FileUtil.java L27](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L27-L27) --- ## FileUtil Dependency Graph 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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L1-L61), [src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L1-L133](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/manager/ConfigManager.java#L1-L133) --- ## Summary `FileUtil` provides a minimal, UTF-8-enforced file I/O abstraction used primarily by `ConfigManager` for configuration persistence. Its design prioritizes: 1. **Encoding Consistency:** All operations use UTF-8 explicitly 2. **Caller Convenience:** Automatic directory creation on write 3. **Error Transparency:** `IOException` propagation for unexpected errors, `null`/empty-array returns for expected conditions 4. **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](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/util/FileUtil.java#L1-L43), [src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L1-L146](https://github.com/Samera2022/MouseMacros/blob/1eb6620b/src/io/github/samera2022/mouse_macros/manager/ConfigManager.java#L1-L146)