clean-up, improvements, fixes, docs

dev
Exlll 8 years ago
parent 6b72d6293c
commit 71f342783b

@ -1,5 +1,5 @@
name: ConfigLib
author: Exlll
version: 1.3.0-SNAPSHOT
version: 1.3.0
main: de.exlll.configlib.ConfigLib

@ -1,5 +1,5 @@
name: ConfigLib
author: Exlll
version: 1.3.0-SNAPSHOT
version: 1.3.0
main: de.exlll.configlib.ConfigLib

@ -191,6 +191,7 @@ public final class ConfigList<T> implements Defaultable<List<?>>, List<T>, Rando
@Override
public void fromDefault(Object value) {
clear();
for (Object item : (List<?>) value) {
Object instance = fromDefault(item, cls);
add(cls.cast(instance));

@ -20,7 +20,7 @@ public final class ConfigMap<K, V> implements Defaultable<Map<K, ?>>, Map<K, V>
this.keyClass = keyClass;
this.valueClass = valueClass;
this.map = Collections.checkedMap(
new HashMap<>(), keyClass, valueClass
new LinkedHashMap<>(), keyClass, valueClass
);
}
@ -170,6 +170,7 @@ public final class ConfigMap<K, V> implements Defaultable<Map<K, ?>>, Map<K, V>
@Override
public void fromDefault(Object value) {
clear();
for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
Object instance = fromDefault(entry.getValue(), valueClass);
Reflect.checkType(entry.getKey(), keyClass);

@ -15,7 +15,7 @@ public final class ConfigSet<T> implements Defaultable<Set<?>>, Set<T> {
Reflect.checkDefaultConstructor(cls);
}
this.cls = cls;
this.set = Collections.checkedSet(new HashSet<>(), cls);
this.set = Collections.checkedSet(new LinkedHashSet<>(), cls);
}
@Override
@ -130,6 +130,7 @@ public final class ConfigSet<T> implements Defaultable<Set<?>>, Set<T> {
@Override
public void fromDefault(Object value) {
clear();
for (Object item : (Set<?>) value) {
Object instance = fromDefault(item, cls);
add(cls.cast(instance));

@ -45,14 +45,14 @@ enum FieldMapper {
if (serialized == null) {
return; // keep default value
}
if (instance instanceof Defaultable<?>) {
instance = Reflect.getValue(field, instance);
((Defaultable<?>) instance).fromDefault(serialized);
Object fieldValue = Reflect.getValue(field, instance);
checkNull(field, fieldValue);
if (fieldValue instanceof Defaultable<?>) {
((Defaultable<?>) fieldValue).fromDefault(serialized);
} else if (Reflect.isDefault(field.getType())) {
Reflect.setValue(field, instance, serialized);
} else {
instance = Reflect.getValue(field, instance);
instanceFromMap(instance, castToMap(serialized));
instanceFromMap(fieldValue, castToMap(serialized));
}
}

@ -25,18 +25,6 @@ enum Reflect {
return cls.isPrimitive() || simpleTypes.contains(cls);
}
static boolean isConfigList(Class<?> cls) {
return ConfigList.class.isAssignableFrom(cls);
}
static boolean isConfigSet(Class<?> cls) {
return ConfigSet.class.isAssignableFrom(cls);
}
static boolean isConfigMap(Class<?> cls) {
return ConfigMap.class.isAssignableFrom(cls);
}
static boolean isContainerType(Class<?> cls) {
return List.class.isAssignableFrom(cls) ||
Set.class.isAssignableFrom(cls) ||

@ -80,27 +80,6 @@ public class ReflectTest {
}
}
@Test
public void isConfigList() throws Exception {
assertThat(Reflect.isConfigList(list.getClass()), is(true));
assertThat(Reflect.isConfigList(set.getClass()), is(false));
assertThat(Reflect.isConfigList(map.getClass()), is(false));
}
@Test
public void isConfigSet() throws Exception {
assertThat(Reflect.isConfigSet(list.getClass()), is(false));
assertThat(Reflect.isConfigSet(set.getClass()), is(true));
assertThat(Reflect.isConfigSet(map.getClass()), is(false));
}
@Test
public void isConfigMap() throws Exception {
assertThat(Reflect.isConfigMap(list.getClass()), is(false));
assertThat(Reflect.isConfigMap(set.getClass()), is(false));
assertThat(Reflect.isConfigMap(map.getClass()), is(true));
}
@Test
public void isContainerType() throws Exception {
assertThat(Reflect.isContainerType(list.getClass()), is(true));

@ -1,36 +1,42 @@
# ConfigLib
This library facilitates creating, saving and loading YAML configuration files. It does so
by using Reflection on configuration classes and automatically saving and loading their
attribute values, creating the configuration file and its parent directories if necessary.
by using Reflection on configuration classes and automatically saving and loading their field
names and values, creating the configuration file and its parent directories if necessary.
## Features
- automatic creation, saving and loading of YAML configurations
- automatic creation of parent directories
- option to add explanatory comments by adding annotations to the class and its fields
- option to add explanatory comments by adding annotations to the class or its fields
- option to exclude fields by making them final, static or transient
- option to change the style of the configuration file
## General information
#### What can be serialized?
If your configuration class uses the following types as attributes, it will be properly saved.
- `String`s
- primitive types (e.g. `int`, `char`) and their corresponding wrapper types
(e.g. `Integer`, `Character`)
- `Set`s, `List`s, and `Map`s containing the above (e.g. `Set<Integer>`, `Map<String, Double>`)
- any other class that consists of the above
You can add fields to your configuration class whose type is one of the following:
- a simple type, which are all primitive types (e.g. `boolean`, `int`), their wrapper types (e.g.
`Boolean`, `Integer`) and strings
- `List`s, `Set`s and `Map`s of simple types (e.g `List<Double>`) or other lists, sets and maps
(e.g. `List<List<Map<String, Integer>>>`)
- custom types which have a no-argument constructor
- `ConfigList`s, `ConfigSet`s and `ConfigMap`s of custom types
If you want to use lists, sets or maps containing objects of custom types,
you have to use `ConfigList`, `ConfigSet` or `ConfigMap`, respectively. If you don't use these
special classes for storing custom objects, the stored objects won't be properly (de-)serialized.
#### Default and null values
All reference type fields of a configuration class must be assigned non-null default values.
If a value is `null` while saving takes place, a `NullPointerException` will be thrown.
If any value is `null`, (de-)serialization will fail with a `NullPointerException`.
#### Serialization of custom classes
You can add fields to your configuration class whose type is some custom class.
`@Comment`s added to the custom class or its fields are ignored and won't be
`@Comment`s added to custom classes or their fields are ignored and won't be
displayed in the configuration file.
## How-to
You can find a step-by-step tutorial here:
[Tutorial](https://github.com/Exlll/ConfigLib/wiki/Tutorial)
#### Creating a configuration
To create a new configuration, create a class which extends `Configuration`. Fields which are
added to this class and which are not `final`, `static` or `transient` can automatically be saved
to the corresponding configuration file.
not `final`, `static` or `transient` and whose type is one of the above can automatically be saved
to the corresponding configuration file.
#### Saving and loading a configuration
Instances of your configuration class have a `load`, `save` and `loadAndSave` method:
- `load` updates all fields of an instance with the values read from the configuration file.
@ -58,6 +64,7 @@ will be thrown.
For more information, consult the official
[documentation](https://bitbucket.org/asomov/snakeyaml/wiki/Documentation).
## Examples
Step-by-step tutorial: [Tutorial](https://github.com/Exlll/ConfigLib/wiki/Tutorial)
#### Example of a custom class
```java
public class Credentials {
@ -108,11 +115,10 @@ public class ExamplePlugin extends JavaPlugin {
DatabaseConfig config = new DatabaseConfig(configPath);
try {
config.loadAndSave();
System.out.println(config.getPort());
} catch (IOException e) {
/* do something with exception */
}
int port = config.getPort();
}
}
```
@ -122,36 +128,36 @@ public class ExamplePlugin extends JavaPlugin {
```xml
<repository>
<id>de.exlll</id>
<url>https://repo.exlll.de/artifactory/snapshots/</url>
<url>https://repo.exlll.de/artifactory/releases/</url>
</repository>
<!-- for Bukkit plugins -->
<dependency>
<groupId>de.exlll</groupId>
<artifactId>configlib-bukkit</artifactId>
<version>1.3.0-SNAPSHOT</version>
<version>1.3.0</version>
</dependency>
<!-- for Bungee plugins -->
<dependency>
<groupId>de.exlll</groupId>
<artifactId>configlib-bungee</artifactId>
<version>1.3.0-SNAPSHOT</version>
<version>1.3.0</version>
</dependency>
```
#### Gradle
```groovy
repositories {
maven {
url 'https://repo.exlll.de/artifactory/snapshots/'
url 'https://repo.exlll.de/artifactory/releases/'
}
}
dependencies {
// for Bukkit plugins
compile group: 'de.exlll', name: 'configlib-bukkit', version: '1.3.0-SNAPSHOT'
compile group: 'de.exlll', name: 'configlib-bukkit', version: '1.3.0'
// for Bungee plugins
compile group: 'de.exlll', name: 'configlib-bungee', version: '1.3.0-SNAPSHOT'
compile group: 'de.exlll', name: 'configlib-bungee', version: '1.3.0'
}
```
Additionally, you either have to import the Bukkit or BungeeCord API
@ -159,4 +165,4 @@ or disable transitive lookups. This project uses both of these APIs, so if you
need an example of how to import them with Gradle, take a look at the `build.gradle`.
If, for some reason, you have SSL errors that you're unable to resolve, you can
use `http://exlll.de:8081/artifactory/snapshots/` as the repository instead.
use `http://exlll.de:8081/artifactory/releases/` as the repository instead.

@ -1,6 +1,6 @@
allprojects {
group 'de.exlll'
version '1.3.0-SNAPSHOT'
version '1.3.0'
}
subprojects {
apply plugin: 'java'

@ -0,0 +1,226 @@
## Creating a configuration
Let's say you want to create the following web chat configuration:
```yaml
# This is the default WebChat configuration
# Author: John Doe
ipAddress: 127.0.0.1
port: 12345
# Usernames mapped to users
users:
User1:
email: user1@example.com
credentials:
username: User1
password: '12345'
User2:
email: user2@example.com
credentials:
username: User2
password: '54321'
User3:
email: user3@example.com
credentials:
username: User3
password: '51423'
channels:
- id: 1
name: Channel1
owner: User1
members:
- User2
- User3
- id: 2
name: Channel2
owner: User2
members:
- User1
```
### 1. Create a configuration
Create a class which extends `de.exlll.configlib.Configuration`.
```java
import de.exlll.configlib.Configuration;
import java.nio.file.Path;
public final class WebchatConfig extends Configuration {
public WebchatConfig(Path configPath) {
super(configPath);
}
}
```
### 2. Create custom classes
Create some classes to hold the necessary information and assign default values to their
fields. Be aware that custom classes must have a no-arguments constructor.
```java
import de.exlll.configlib.Configuration;
import java.nio.file.Path;
import java.util.List;
public final class WebchatConfig extends Configuration {
public WebchatConfig(Path configPath) {
super(configPath);
}
public static final class User {
private String email = "";
private Credentials credentials = new Credentials();
}
public static final class Credentials {
private String username = "";
private String password = "";
}
public static final class Channel {
private int id; // channel id
private String name = ""; // channel name
private String owner = ""; // username of the owner
private List<String> members = new ArrayList<>(); // other usernames
}
}
```
### 3. Add fields
You can add fields to a configuration class whose type is one of the following:
- a simple type, which are all primitive types (e.g. `boolean`, `int`), their wrapper types (e.g.
`Boolean`, `Integer`) and strings
- `List`s, `Set`s and `Map`s of simple types (e.g `List<Double>`) or other lists, sets and maps
(e.g. `List<List<Map<String, Integer>>>`)
- custom types which have a no-argument constructor,
- `ConfigList`s, `ConfigSet`s and `ConfigMap`s of custom types
If you want to use lists, sets or maps containing objects of custom types,
you have to use `ConfigList`, `ConfigSet` or `ConfigMap`, respectively. If you don't use these
special classes for storing custom objects, the stored objects won't be properly (de-)serialized.
If you don't want a field to be serialized, make it `final`, `static` or `transient`.
**NOTE:** all field values _must_ be non-null. If any value is `null`, serialization
will fail with a `NullPointerException`.
```java
import de.exlll.configlib.ConfigList;
import de.exlll.configlib.ConfigMap;
import de.exlll.configlib.Configuration;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
public final class WebchatConfig extends Configuration {
// private String s; // fails with a NullPointerException if not assigned a value
private String ipAddress = "127.0.0.1";
private int port = 12345;
private Map<String, User> users = new ConfigMap<>(String.class, User.class);
private List<Channel> channels = new ConfigList<>(Channel.class);
public WebchatConfig(Path configPath) {
super(configPath);
}
/* other classes and methods */
}
```
### 4. Add comments
Comments can only be added to the configuration class or its fields.
Comments you add to other custom classes or their fields will be ignored.
```java
import de.exlll.configlib.Comment;
import de.exlll.configlib.ConfigList;
import de.exlll.configlib.ConfigMap;
import de.exlll.configlib.Configuration;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
@Comment({
"This is the default WebChat configuration",
"Author: John Doe"
})
public final class WebchatConfig extends Configuration {
private String ipAddress = "127.0.0.1";
private int port = 12345;
@Comment("Usernames mapped to users")
private Map<String, User> users = new ConfigMap<>(String.class, User.class);
private List<Channel> channels = new ConfigList<>(Channel.class);
public WebchatConfig(Path configPath) {
super(configPath);
}
/* other classes and methods */
}
```
### 5. Add default values
Add some default values for lists, sets and maps.
```java
/* imports */
public final class WebchatConfig extends Configuration {
/* fields */
public WebchatConfig(Path configPath) {
super(configPath);
Channel channel1 = createNewChannel(1, "Channel1", "User1",
Arrays.asList("User2", "User3"));
Channel channel2 = createNewChannel(2, "Channel2", "User2",
Arrays.asList("User1"));
channels.add(channel1);
channels.add(channel2);
User user1 = createNewUser("user1@example.com", "User1", "12345");
User user2 = createNewUser("user2@example.com", "User2", "54321");
User user3 = createNewUser("user3@example.com", "User3", "51423");
users.put(user1.credentials.username, user1);
users.put(user2.credentials.username, user2);
users.put(user3.credentials.username, user3);
}
private Channel createNewChannel(int id, String name, String owner,
List<String> members) {
Channel channel = new Channel();
channel.id = id;
channel.name = name;
channel.owner = owner;
channel.members = members;
return channel;
}
private User createNewUser(String email, String username, String password) {
User user = new User();
user.email = email;
user.credentials.username = username;
user.credentials.password = password;
return user;
}
/* other classes and methods */
}
```
### 6. Create an instance of your configuration
Create a `java.nio.file.Path` object and pass it to the configuration constructor.
```java
/* imports */
public final class WebchatConfig extends Configuration {
/*...*/
public static void main(String[] args) {
File configFolder = new File("folder");
File configFile = new File(configFolder, "config.yml");
Path path = configFile.toPath();
/* of course, you can skip the above steps and directly
* create a Path object using Paths.get(...) */
WebchatConfig config = new WebchatConfig(path);
try {
config.loadAndSave();
System.out.println(config.getIpAddress());
System.out.println(config.getPort());
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
Loading…
Cancel
Save