Skip to content

File and Path Utilities

Samera2022 edited this page Jan 30, 2026 · 1 revision

File and Path Utilities

Relevant source files

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


Overview

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:

  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

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


Core Operations

Read File

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

Write File

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

List File Names

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


Get Local Storage Path

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 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

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


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:

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


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

:


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


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, 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, src/io/github/samera2022/mouse_macros/util/FileUtil.java 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, 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, src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L1-L146

Clone this wiki locally