Skip to content

CacheManager

Samera2022 edited this page Jan 30, 2026 · 1 revision

CacheManager

Relevant source files

Purpose and Scope

CacheManager is responsible for managing transient session state in the MouseMacros application. It persists user preferences that change frequently during normal usage, such as file chooser directories and window dimensions, to a JSON file (cache.json). This page documents the CacheManager class, its data structures, persistence mechanisms, and integration points.

For long-term user configuration settings (language, hotkeys, dark mode), see ConfigManager. For the file format specification of cache.json, see Configuration Files.

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L1-L86


Overview

CacheManager provides a centralized mechanism for storing and retrieving ephemeral application state that should persist across sessions but is not considered core configuration. The primary use cases are:

  • Directory Memory: Remembering the last directory used for saving/loading macro files
  • Window Size Caching: Storing window dimensions for each dialog/frame to restore user preferences
  • Session Continuity: Providing a seamless user experience by restoring UI state

The manager uses a static singleton pattern with lazy initialization, ensuring a single cache instance throughout the application lifecycle.

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L13-L60


Class Structure and Data Model

classDiagram
    class CacheManager {
        -String CACHE_PATH
        -Gson gson
        -FileChooserCache cache
        +FileChooserCache getCache()
        +void reloadCache()
        +void saveCache()
        +void saveCache(FileChooserCache)
        +FileChooserCache loadCache()
        +String getLastLoadDirectory()
        +void setLastLoadDirectory(String)
        +String getLastSaveDirectory()
        +void setLastSaveDirectory(String)
        +String getWindowSize(String)
        +void setWindowSize(String, String)
    }
    class FileChooserCache {
        +String lastLoadDirectory
        +String lastSaveDirectory
        +Map<String,String> windowSizeMap
    }
    class Gson {
    }
    class FileUtil {
    }
    FileChooserCache --o CacheManager : contains
    CacheManager ..> Gson : uses
    CacheManager ..> FileUtil : uses
Loading

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L13-L86

FileChooserCache Structure

The FileChooserCache inner class defines the JSON schema for the cache file:

Field Type Purpose Default Value
lastLoadDirectory String Last directory used when loading .mmc files "" (empty)
lastSaveDirectory String Last directory used when saving .mmc files "" (empty)
windowSizeMap Map<String, String> Maps window names to size strings (e.g., "800x600") Empty HashMap

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L19-L23


File Location and Persistence

Cache File Path

The cache file is stored at: <LocalStoragePath>/cache.json

Where LocalStoragePath is determined by FileUtil.getLocalStoragePath(), typically resolving to:

  • Windows: %USERPROFILE%/AppData/MouseMacros/cache.json
  • Cross-platform: Uses FileUtil to ensure consistent path resolution
flowchart TD

FileUtil["FileUtil.getLocalStoragePath()"]
CACHE_PATH["CACHE_PATH constant"]
File["cache.json file"]

FileUtil --> CACHE_PATH
CACHE_PATH --> File
Loading

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L15, src/io/github/samera2022/mouse_macros/util/FileUtil.java

Serialization Format

CacheManager uses Google's Gson library with pretty-printing enabled for human-readable JSON output:

// From CacheManager.java:16
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();

Example cache.json structure:

{
  "lastLoadDirectory": "C:/Users/Example/Documents/Macros",
  "lastSaveDirectory": "C:/Users/Example/Documents/Macros",
  "windowSizeMap": {
    "MainFrame": "800x600",
    "SettingsDialog": "600x400",
    "MacroSettingsDialog": "500x300"
  }
}

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L16


API Methods

Core Cache Operations

Loading Cache

sequenceDiagram
  participant Application
  participant CacheManager
  participant File System
  participant Gson Library

  Application->>CacheManager: loadCache()
  CacheManager->>CacheManager: Paths.get(CACHE_PATH)
  CacheManager->>File System: Files.exists(cachePath)
  loop [Cache file exists]
    File System-->>CacheManager: true
    CacheManager->>File System: Files.readAllBytes(cachePath)
    File System-->>CacheManager: JSON bytes
    CacheManager->>Gson Library: fromJson(json, FileChooserCache.class)
    Gson Library-->>CacheManager: FileChooserCache object
    CacheManager-->>Application: FileChooserCache instance
    File System-->>CacheManager: false
    CacheManager->>CacheManager: new FileChooserCache()
    CacheManager-->>Application: Default instance
  end
Loading

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L49-L60

The loadCache() method:

  1. Constructs the cache file path using Paths.get(CACHE_PATH)
  2. Checks if the file exists with Files.exists(cachePath)
  3. If exists, reads the file content as UTF-8 encoded bytes
  4. Deserializes JSON to FileChooserCache using Gson
  5. If missing or error occurs, returns a new default FileChooserCache instance

Error handling prints to System.err but does not throw exceptions, ensuring graceful degradation.

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L49-L60

Saving Cache

// From CacheManager.java:37-47
public static void saveCache(FileChooserCache c) {
    try {
        Path cachePath = Paths.get(CACHE_PATH);
        // Ensure parent directory exists
        if (cachePath.getParent() != null) Files.createDirectories(cachePath.getParent());
        String json = gson.toJson(c);
        Files.write(cachePath, json.getBytes(StandardCharsets.UTF_8));
    } catch (IOException e) {
        System.err.println("Failed to save cache: " + e.getMessage());
    }
}

The save operation:

  1. Creates parent directory structure if needed using Files.createDirectories()
  2. Serializes the FileChooserCache object to JSON
  3. Writes JSON as UTF-8 encoded bytes to the cache file
  4. Handles IOException gracefully with error logging

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L37-L47

Directory Management Methods

Method Parameters Return Purpose
getLastLoadDirectory() None String Returns the last directory used for loading macros
setLastLoadDirectory(String dir) dir: String void Sets last load directory and persists cache
getLastSaveDirectory() None String Returns the last directory used for saving macros
setLastSaveDirectory(String dir) dir: String void Sets last save directory and persists cache

Key Implementation Detail: All setter methods automatically call saveCache() to persist changes immediately, ensuring no data loss if the application terminates unexpectedly.

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L62-L75

Window Size Management Methods

Method Parameters Return Purpose
getWindowSize(String windowName) windowName: String String Retrieves cached size for the specified window
setWindowSize(String windowName, String size) windowName: String, size: String void Stores window size and persists cache

The window size format is typically "<width>x<height>" (e.g., "800x600"), though the manager does not enforce this format.

Implementation Note: Both getter and setter perform null-safety checks on cache.windowSizeMap, initializing it to a new HashMap if null.

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L77-L85


Integration with Application Components

flowchart TD

MainFrame["MainFrame"]
SettingsDialog["SettingsDialog"]
MacroSettingsDialog["MacroSettingsDialog"]
HotkeyDialog["HotkeyDialog"]
MacroManager["MacroManager"]
CacheManager["CacheManager<br>(static singleton)"]
FileChooser["JFileChooser instances"]
WindowClosingAdapters["WindowClosingAdapter instances"]

MainFrame --> CacheManager
SettingsDialog --> CacheManager
MacroSettingsDialog --> CacheManager
HotkeyDialog --> CacheManager
MacroManager --> CacheManager
MacroManager --> CacheManager
FileChooser --> MacroManager
WindowClosingAdapters --> MainFrame
WindowClosingAdapters --> SettingsDialog
WindowClosingAdapters --> MacroSettingsDialog
WindowClosingAdapters --> HotkeyDialog
Loading

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L1-L86, README.md L63-L79

Usage Patterns

  1. Window Size Restoration: UI components retrieve cached dimensions on initialization and save new dimensions on window close
  2. File Chooser State: MacroManager queries last directories when displaying save/load dialogs and updates cache after successful file operations
  3. Configuration Override: When ConfigManager.config.enableDefaultStorage is true, the defaultMmcStoragePath overrides cached directory values

Sources: README.md L77-L78


Relationship to ConfigManager

flowchart TD

ConfigManager["ConfigManager<br>config.cfg"]
CacheManager["CacheManager<br>cache.json"]
LongTerm["Long-term Settings<br>- Language<br>- Hotkeys<br>- Dark mode<br>- Default paths"]
ShortTerm["Transient State<br>- Last directories<br>- Window sizes"]

ConfigManager --> LongTerm
CacheManager --> ShortTerm

subgraph subGraph1 ["Data Classification"]
    LongTerm
    ShortTerm
end

subgraph subGraph0 ["Configuration Management"]
    ConfigManager
    CacheManager
    ConfigManager --> CacheManager
end
Loading

Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L1-L121, src/io/github/samera2022/mouse_macros/manager/CacheManager.java L1-L86, README.md L77-L78

Comparison Table

Aspect ConfigManager CacheManager
File config.cfg cache.json
Purpose Long-term user preferences Transient session state
Update Frequency Infrequent (user-initiated) Frequent (automatic)
Contents Language, hotkeys, dark mode, custom settings Last directories, window sizes
Initialization Static block loads on class load Static field loads on first access
Reload Method ConfigManager.reloadConfig() CacheManager.reloadCache()
Storage Location Same directory Same directory

Sources: src/io/github/samera2022/mouse_macros/manager/ConfigManager.java L14-L50, src/io/github/samera2022/mouse_macros/manager/CacheManager.java L13-L60


Data Flow Example: Save Macro Operation

sequenceDiagram
  participant User
  participant MainFrame
  participant MacroManager
  participant JFileChooser
  participant CacheManager
  participant File System

  User->>MainFrame: Click "Save Macro" button
  MainFrame->>MacroManager: saveMacro()
  MacroManager->>CacheManager: getLastSaveDirectory()
  CacheManager-->>MacroManager: "/path/to/last/dir"
  MacroManager->>JFileChooser: new JFileChooser(lastDir)
  MacroManager->>JFileChooser: showSaveDialog()
  User->>JFileChooser: Select file and confirm
  JFileChooser-->>MacroManager: Selected file path
  MacroManager->>File System: Write macro to file
  File System-->>MacroManager: Success
  MacroManager->>CacheManager: setLastSaveDirectory(newDir)
  CacheManager->>CacheManager: cache.lastSaveDirectory = newDir
  CacheManager->>CacheManager: saveCache()
  CacheManager->>File System: Write cache.json
Loading

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L69-L74


Error Handling and Resilience

CacheManager implements defensive programming practices:

  1. Null Safety: All getter methods check for null windowSizeMap and initialize if needed
  2. Exception Suppression: File I/O exceptions are caught and logged to System.err but do not propagate
  3. Graceful Degradation: If cache.json is corrupt or missing, returns default FileChooserCache instance
  4. Automatic Recovery: Missing parent directories are created automatically during save operations

This design ensures the cache system never causes application crashes, even when the file system is in an unexpected state.

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L37-L60, src/io/github/samera2022/mouse_macros/manager/CacheManager.java L77-L85


Implementation Notes

Singleton Pattern

CacheManager uses the static singleton pattern with lazy initialization:

// From CacheManager.java:17
private static FileChooserCache cache = loadCache();

The cache is loaded once when the class is first accessed by the JVM, ensuring a single shared instance across the application.

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L17

Thread Safety

Important: CacheManager is not thread-safe. All methods are static and operate on shared mutable state without synchronization. However, this is acceptable for MouseMacros because:

  • The application is single-threaded (Swing EDT)
  • Cache operations occur sequentially during user interactions
  • No concurrent access to cache methods occurs in practice

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L13-L86

Manual Reload Support

The reloadCache() method allows explicit cache refresh from disk:

// From CacheManager.java:29-31
public static void reloadCache() {
    cache = loadCache();
}

This is primarily intended for development/debugging scenarios where the cache file may be manually edited while the application is running.

Sources: src/io/github/samera2022/mouse_macros/manager/CacheManager.java L29-L31

Clone this wiki locally