Added JsonConfiguration
This commit is contained in:
parent
5aa75be6ba
commit
6c1338d171
270
gson/src/main/java/de/neitzel/gson/config/JsonConfiguration.java
Normal file
270
gson/src/main/java/de/neitzel/gson/config/JsonConfiguration.java
Normal file
@ -0,0 +1,270 @@
|
||||
package de.neitzel.gson.config;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import lombok.Builder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a configuration utility class that manages application settings and allows
|
||||
* loading and saving the configuration in JSON format. This class is built using Gson for
|
||||
* JSON serialization and deserialization.
|
||||
* <p>
|
||||
* The class maintains application settings in memory and provides methods to store
|
||||
* the configuration on the filesystem and retrieve it as needed.
|
||||
*/
|
||||
@Builder(builderClassName = "ConfigurationBuilder")
|
||||
public class JsonConfiguration {
|
||||
|
||||
/**
|
||||
* The name of the application.
|
||||
* This variable is used to identify the application and is incorporated into various configurations,
|
||||
* such as the naming of directories or files associated with the application's settings.
|
||||
* It is finalized and set during the construction of the {@code JsonConfiguration} instance and cannot be changed afterward.
|
||||
*/
|
||||
private final String appName;
|
||||
|
||||
/**
|
||||
* The filesystem path representing the home directory utilized by the application.
|
||||
* This directory serves as the location where application-specific configuration files
|
||||
* and data are stored. The path is initialized when the {@code JsonConfiguration} instance
|
||||
* is constructed, based on the settings provided through the {@code JsonConfigurationBuilder}.
|
||||
* It can be customized to a user-specified value or defaults to the user's home directory.
|
||||
* <p>
|
||||
* The {@code homeDir} variable is integral in determining the location of the main configuration
|
||||
* file and other related resources used by the application.
|
||||
*/
|
||||
private final String homeDir;
|
||||
|
||||
/**
|
||||
* A map that stores configuration settings as key-value pairs,
|
||||
* where both the keys and values are represented as strings.
|
||||
* <p>
|
||||
* This map is used to manage dynamic or runtime settings for the configuration,
|
||||
* allowing for flexible assignment and retrieval of values associated with specific
|
||||
* configuration keys. It is initialized as a final instance, ensuring it cannot be
|
||||
* reassigned after creation.
|
||||
*/
|
||||
private final Map<String, String> settings;
|
||||
|
||||
/**
|
||||
* A Gson instance used for handling JSON serialization and deserialization within
|
||||
* the JsonConfiguration class. This instance is immutable and customized with
|
||||
* registered adapters during the initialization of the JsonConfiguration.
|
||||
* <p>
|
||||
* The Gson object provides functionality for converting Java objects to JSON
|
||||
* and vice versa. It supports complex serialization and deserialization
|
||||
* workflows by leveraging adapters specified during the configuration phase.
|
||||
* <p>
|
||||
* Adapters can be registered to customize the behavior of (de)serialization
|
||||
* for specific types.
|
||||
*/
|
||||
private final Gson gson;
|
||||
|
||||
/**
|
||||
* Stores a key-value pair into the configuration settings.
|
||||
* The value is serialized into a JSON string before being stored.
|
||||
*
|
||||
* @param key the key under which the value will be stored
|
||||
* @param value the object to be associated with the specified key
|
||||
*/
|
||||
public void set(String key, Object value) {
|
||||
settings.put(key, gson.toJson(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value associated with the specified key from the configuration,
|
||||
* deserializing it into the specified class type using Gson.
|
||||
* If the key does not exist in the settings, the method returns {@code null}.
|
||||
*
|
||||
* @param <T> the type of the object to be deserialized
|
||||
* @param key the key corresponding to the value in the configuration
|
||||
* @param clazz the {@code Class} object representing the expected type of the value
|
||||
* @return the deserialized object of type {@code T}, or {@code null}
|
||||
* if the key does not exist
|
||||
*/
|
||||
public <T> T get(String key, Class<T> clazz) {
|
||||
String json = settings.get(key);
|
||||
return json != null ? gson.fromJson(json, clazz) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value associated with the given key from the configuration as an object of the specified type.
|
||||
* If the key does not exist in the configuration, the provided default value is returned.
|
||||
*
|
||||
* @param <T> the type of the object to be returned
|
||||
* @param key the key whose associated value is to be retrieved
|
||||
* @param clazz the class of the object to deserialize the value into
|
||||
* @param defaultValue the default value to return if the key does not exist or the value is null
|
||||
* @return the value associated with the specified key, deserialized into the specified type,
|
||||
* or the default value if the key does not exist or the value is null
|
||||
*/
|
||||
public <T> T get(String key, Class<T> clazz, T defaultValue) {
|
||||
String json = settings.get(key);
|
||||
return json != null ? gson.fromJson(json, clazz) : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configuration data from the JSON configuration file located at the path
|
||||
* determined by the {@code getConfigFilePath()} method.
|
||||
* If the configuration file exists, its content is read and deserialized into a map
|
||||
* of key-value pairs using Gson. The method clears the current settings and populates
|
||||
* them with the loaded values.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs while reading the configuration file
|
||||
*/
|
||||
public void load() throws IOException {
|
||||
Path configPath = getConfigFilePath();
|
||||
if (Files.exists(configPath)) {
|
||||
try (var reader = Files.newBufferedReader(configPath)) {
|
||||
Map<String, String> loaded = gson.fromJson(reader, new TypeToken<Map<String, String>>() {
|
||||
}.getType());
|
||||
settings.clear();
|
||||
settings.putAll(loaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the path to the configuration file for the application.
|
||||
* The configuration file is located in the user's home directory, within a hidden
|
||||
* folder named after the application, and is named "config.json".
|
||||
*
|
||||
* @return the {@code Path} to the application's configuration file
|
||||
*/
|
||||
private Path getConfigFilePath() {
|
||||
return Path.of(homeDir, "." + appName, "config.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current configuration to a JSON file.
|
||||
* <p>
|
||||
* This method serializes the current settings into a JSON format and writes
|
||||
* them to a file located at the configuration path. If the parent directory
|
||||
* of the configuration file does not exist, it is created automatically.
|
||||
* <p>
|
||||
* The configuration file path is determined based on the application name
|
||||
* and home directory. Any existing content in the file will be overwritten
|
||||
* during this operation.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs while creating the directory,
|
||||
* opening the file, or writing to the file
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
Path configPath = getConfigFilePath();
|
||||
Files.createDirectories(configPath.getParent());
|
||||
try (var writer = Files.newBufferedWriter(configPath)) {
|
||||
gson.toJson(settings, writer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for creating instances of {@code JsonConfiguration}.
|
||||
* The {@code JsonConfigurationBuilder} allows the configuration of application
|
||||
* name, home directory, and custom Gson adapters before building a {@code JsonConfiguration} object.
|
||||
*/
|
||||
public static class JsonConfigurationBuilder {
|
||||
/**
|
||||
* A map that holds custom Gson adapters to register with a GsonBuilder.
|
||||
* The keys represent the classes for which the adapters are applicable,
|
||||
* and the values are the adapter instances associated with those classes.
|
||||
* <p>
|
||||
* This variable is used to store user-defined type adapters, allowing
|
||||
* for customized serialization and deserialization behavior for specific
|
||||
* classes when constructing a {@code JsonConfiguration}.
|
||||
* <p>
|
||||
* It is populated using the {@code addGsonAdapter} method in the
|
||||
* {@code JsonConfigurationBuilder} class and is later passed to a
|
||||
* {@code GsonBuilder} for registration.
|
||||
*/
|
||||
private final Map<Class<?>, Object> gsonAdapters = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The name of the application being configured.
|
||||
* This variable holds a string representation of the application's name and is used
|
||||
* to identify the application within the context of the {@code JsonConfiguration}.
|
||||
* It is set during the construction of a {@code JsonConfigurationBuilder} instance.
|
||||
*/
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* Represents the home directory path of the current user.
|
||||
* By default, this variable is initialized to the value of the "user.home" system property,
|
||||
* which typically points to the user's home directory on the filesystem.
|
||||
* <p>
|
||||
* This field can be customized to point to a different directory via the builder class
|
||||
* methods when building an instance of {@code JsonConfiguration}.
|
||||
* <p>
|
||||
* Example system values for "user.home" may include paths such as "~/Users/username" for macOS,
|
||||
* "C:/Users/username" for Windows, or "/home/username" for Unix/Linux systems.
|
||||
*/
|
||||
private String homeDir = System.getProperty("user.home");
|
||||
|
||||
/**
|
||||
* Sets the application name to be used in the configuration.
|
||||
*
|
||||
* @param appName the name of the application to be set
|
||||
* @return the current instance of {@code JsonConfigurationBuilder} for chaining
|
||||
*/
|
||||
public JsonConfigurationBuilder appName(String appName) {
|
||||
this.appName = appName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the home directory for the configuration being built.
|
||||
* This method specifies the directory path where the application's configuration
|
||||
* files or settings should be stored.
|
||||
*
|
||||
* @param homeDir the path to the desired home directory
|
||||
* @return the current instance of {@code JsonConfigurationBuilder} to enable method chaining
|
||||
*/
|
||||
public JsonConfigurationBuilder homeDir(String homeDir) {
|
||||
this.homeDir = homeDir;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom Gson adapter to the builder for the specified type.
|
||||
* This method allows registration of a type-to-adapter mapping that will be applied
|
||||
* when building the Gson instance within the {@code JsonConfiguration}.
|
||||
*
|
||||
* @param type the {@code Class} of the type for which the adapter is being registered
|
||||
* @param adapter the adapter object to be used for the specified type
|
||||
* @return the current instance of {@code JsonConfigurationBuilder}, allowing method chaining
|
||||
*/
|
||||
public JsonConfigurationBuilder addGsonAdapter(Class<?> type, Object adapter) {
|
||||
gsonAdapters.put(type, adapter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns an instance of {@code JsonConfiguration} with the specified
|
||||
* settings, including the application name, home directory, and any registered
|
||||
* custom Gson adapters.
|
||||
*
|
||||
* @return a {@code JsonConfiguration} instance configured with the builder's parameters.
|
||||
*/
|
||||
public JsonConfiguration build() {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
for (var entry : gsonAdapters.entrySet()) {
|
||||
gsonBuilder.registerTypeAdapter(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
Gson gson = gsonBuilder.create();
|
||||
|
||||
return new JsonConfiguration(
|
||||
appName,
|
||||
homeDir,
|
||||
new HashMap<>(),
|
||||
gson
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
package de.neitzel.gson.config;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class JsonConfigurationTest {
|
||||
|
||||
/**
|
||||
* Test class for JsonConfiguration containing unit tests for the `get` method.
|
||||
* The `get` method fetches the value associated with a given key and converts
|
||||
* it from JSON representation to the specified class type.
|
||||
*/
|
||||
|
||||
@Test
|
||||
void testGetExistingKeyWithValidValue() {
|
||||
// Arrange
|
||||
Gson gson = new Gson();
|
||||
HashMap<String, String> settings = new HashMap<>();
|
||||
settings.put("testKey", gson.toJson(42));
|
||||
JsonConfiguration jsonConfiguration = JsonConfiguration.builder()
|
||||
.settings(settings)
|
||||
.gson(gson)
|
||||
.build();
|
||||
|
||||
// Act
|
||||
Integer value = jsonConfiguration.get("testKey", Integer.class);
|
||||
|
||||
// Assert
|
||||
assertEquals(42, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetNonExistingKey() {
|
||||
// Arrange
|
||||
Gson gson = new Gson();
|
||||
HashMap<String, String> settings = new HashMap<>();
|
||||
JsonConfiguration jsonConfiguration = JsonConfiguration.builder()
|
||||
.settings(settings)
|
||||
.gson(gson)
|
||||
.build();
|
||||
|
||||
// Act
|
||||
String value = jsonConfiguration.get("nonExistentKey", String.class);
|
||||
|
||||
// Assert
|
||||
assertNull(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetWithDefaultValueWhenKeyExists() {
|
||||
// Arrange
|
||||
Gson gson = new Gson();
|
||||
HashMap<String, String> settings = new HashMap<>();
|
||||
settings.put("testKey", gson.toJson("Hello World"));
|
||||
JsonConfiguration jsonConfiguration = JsonConfiguration.builder()
|
||||
.settings(settings)
|
||||
.gson(gson)
|
||||
.build();
|
||||
|
||||
// Act
|
||||
String value = jsonConfiguration.get("testKey", String.class, "Default Value");
|
||||
|
||||
// Assert
|
||||
assertEquals("Hello World", value);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetWithDefaultValueWhenKeyDoesNotExist() {
|
||||
// Arrange
|
||||
Gson gson = new Gson();
|
||||
HashMap<String, String> settings = new HashMap<>();
|
||||
JsonConfiguration jsonConfiguration = JsonConfiguration.builder()
|
||||
.settings(settings)
|
||||
.gson(gson)
|
||||
.build();
|
||||
|
||||
// Act
|
||||
String value = jsonConfiguration.get("nonExistentKey", String.class, "Default Value");
|
||||
|
||||
// Assert
|
||||
assertEquals("Default Value", value);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetWithComplexObject() {
|
||||
// Arrange
|
||||
Gson gson = new Gson();
|
||||
HashMap<String, String> settings = new HashMap<>();
|
||||
TestObject testObject = new TestObject("John Doe", 30);
|
||||
settings.put("complexKey", gson.toJson(testObject));
|
||||
JsonConfiguration jsonConfiguration = JsonConfiguration.builder()
|
||||
.settings(settings)
|
||||
.gson(gson)
|
||||
.build();
|
||||
|
||||
// Act
|
||||
TestObject result = jsonConfiguration.get("complexKey", TestObject.class);
|
||||
|
||||
// Assert
|
||||
assertEquals("John Doe", result.name());
|
||||
assertEquals(30, result.age());
|
||||
}
|
||||
|
||||
// Helper class for complex object tests
|
||||
static class TestObject {
|
||||
private final String name;
|
||||
|
||||
private final int age;
|
||||
|
||||
TestObject(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int age() {
|
||||
return age;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user