Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 0 additions & 71 deletions .github/workflows/codeql-analysis.yml

This file was deleted.

25 changes: 21 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ It looks better to organize config in a YAML file since it makes sense to mainta

**YamlConfig** helps read configuration for a java project from a YAML config file and access them via dotted notation.

## ⚠ 1.3.0 Breaking changes:


Version **1.3.0** introduces breaking changes as follows:

```
YamlConfig config = YamlConfig.load(resource);
YamlConfig config = YamlConfig.load(yaml, resource);
```

is now:

```
YamlConfig config = new YamlConfig(resource);
YamlConfig config = new YamlConfig(yaml, resource);
```

## Features
- Uses SnakeYAML for reading YAML, so it can handle any data recognizable by SnakeYAML.
- Ease of access using dotted notation to read properties
Expand All @@ -19,13 +36,13 @@ If you use Maven for Dependency management, you can include this using below dep
<dependency>
<groupId>com.github.jsixface</groupId>
<artifactId>yamlconfig</artifactId>
<version>1.2.0</version>
<version>1.3.0</version>
</dependency>
```
Or if you use Gradle, you can include this using below dependency.

```
implementation 'com.github.jsixface:yamlconfig:1.2.0'
implementation 'com.github.jsixface:yamlconfig:1.3.0'
```

## Usage - internal Yaml
Expand All @@ -36,7 +53,7 @@ InputStream resource = getClass()
.getClassLoader()
.getResourceAsStream("config.yml");

YamlConfig config = YamlConfig.load(resource);
YamlConfig config = new YamlConfig(resource);
```

Assume the contents of `config.yml` is as below:
Expand Down Expand Up @@ -78,7 +95,7 @@ InputStream resource = getClass()
.getResourceAsStream("config.yml");

# pass the lot to YamlConfig
YamlConfig config = YamlConfig.load(yaml, resource);
YamlConfig config = new YamlConfig(yaml, resource);

# and now you can use fully qualified dot notation keys:
config.getString("service.db.someKey");
Expand Down
155 changes: 100 additions & 55 deletions src/main/java/com/github/jsixface/YamlConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,65 +20,57 @@

import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class YamlConfig {
private Object content;
private final Pattern arrayKeyPattern = Pattern.compile("^([a-zA-Z][a-zA-Z0-9]+)\\[([0-9]+)]$");

private YamlConfig() {
}
private final Pattern keyPattern = Pattern.compile("^([a-zA-Z][a-zA-Z0-9]+)\\[([0-9]+)]$");
private final Object content;

/**
* Create configuration from Reader
*
* @param reader the reader to read config from
* @return YamlConfig instance
*/
public static YamlConfig load(Reader reader) {
YamlConfig instance = new YamlConfig();
Yaml yml = new Yaml();
instance.content = yml.load(reader);
return instance;
public YamlConfig(Reader reader) {
final Yaml yaml = new Yaml();
this.content = yaml.load(reader);
}

/**
* Create configuration from input stream
* @param in the Input stream to read from
* @return YamlConfig instance
*
* @param inputStream the Input stream to read from
*/
public static YamlConfig load(InputStream in) {
YamlConfig instance = new YamlConfig();
Yaml yml = new Yaml();
instance.content = yml.load(in);
return instance;
public YamlConfig(InputStream inputStream) {
final Yaml yaml = new Yaml();
this.content = yaml.load(inputStream);
}

/**
* Create configuration from input stream, using your yaml instance
* @param yaml the Yaml instance to use
* @param in the Input stream to read from
* @return YamlConfig instance
*
* @param yaml the Yaml instance to use
* @param inputStream the Input stream to read from
*/
public static YamlConfig load(Yaml yaml, InputStream in) {
YamlConfig instance = new YamlConfig();
instance.content = yaml.load(in);
return instance;
public YamlConfig(Yaml yaml, InputStream inputStream) {
this.content = yaml.load(inputStream);
}

/**
* Gets the String value for the specified key from the config.
*
* @param key Key in dotted notation like <code>first.second[2].third</code>
* @return The String value of property. <br ><code>null</code> if the key is not present
* or not a leaf node. <code>Boolean</code> or <code>Integer</code> or other format
* are converted to String.
* @param key key in dotted notation like <code>first.second[2].third</code>
* @return the String value of property.
* <p>
* <code>null</code> if the key is not present or not a leaf node.
* <p>
* <code>Boolean</code> or <code>Integer</code> or another format is converted to String.
*/
public String getString(String key) {
Object foundNode = getNode(key, content);
Object foundNode = getNode(key);
if (foundNode != null && !(foundNode instanceof Collection)) {
return foundNode.toString();
}
Expand All @@ -88,45 +80,98 @@ public String getString(String key) {
/**
* Gets the Integer value for the specified key from the config.
*
* @param key Key in dotted notation like <code>first.second[2].third</code>
* @return The Integer value of property. <br ><code>null</code> if the key is not present
* or not a leaf node.
* @param key key in dotted notation like <code>first.second[2].third</code>
* @return the Integer value of property.
* <p>
* <code>null</code> if the key is not present or not a leaf node.
*/
public Integer getInt(String key) {
Object foundNode = getNode(key, content);
if (foundNode instanceof Integer) {
return (Integer) foundNode;
Object node = getNode(key);
if (node instanceof Integer) {
return (Integer) node;
}
return null;
}

/**
* Gets a string list for the specified key from the config.
*
* @param key key in dotted notation like <code>first.second</code>
* @return the type list value of property.
* <p>
* <code>null</code> if the key is not present or not a leaf node.
*/
public ArrayList<String> getStringList(String key) {
return getList(key, String.class);
}

/**
* Gets a generic list for the specified key from the config.
*
* @param key key in dotted notation like <code>first.second</code>
* @return the generic list.
* <p>
* <code>null</code> if the key is not present or not a leaf node.
*/
public <T> ArrayList<T> getList(String key, Class<T> type) {
final ArrayList<?> node = (ArrayList<?>) getNode(key);
final ArrayList<T> typeList = new ArrayList<>();
if (node != null) {
try {
node.forEach(o -> typeList.add(type.cast(o)));
return typeList;
} catch (ClassCastException exception) {
return null;
}
}
return null;
}

private Object getNode(String key, Object foundNode) {
String[] parts = decompose(key);
/**
* Gets a node by the key.
* The key follows this pattern: my.key[index].entry
*
* @param key The key to find
* @return the found node or <code>null</code> if not found
*/
private Object getNode(String key) {
final String[] parts = splitByDot(key);

Object node = content;
for (String part : parts) {
int arrayNum = -1;
Matcher matcher = arrayKeyPattern.matcher(part);
if (matcher.matches()) {
part = matcher.group(1);
arrayNum = Integer.parseInt(matcher.group(2));
final Matcher arrayKeyPatternMatcher = arrayKeyPattern.matcher(part);
final Matcher keyPatternMatcher = keyPattern.matcher(part);
if (arrayKeyPatternMatcher.matches()) {
part = arrayKeyPatternMatcher.group(1);
arrayNum = Integer.parseInt(arrayKeyPatternMatcher.group(2));
} else if (keyPatternMatcher.matches()) {
part = keyPatternMatcher.group(1);
}
if (foundNode instanceof Map) {
if (((Map<?, ?>) foundNode).containsKey(part)) {
foundNode = ((Map<?, ?>) foundNode).get(part);
if (node instanceof Map) {
if (((Map<?, ?>) node).containsKey(part)) {
node = ((Map<?, ?>) node).get(part);
if (arrayNum >= 0) {
if (foundNode instanceof ArrayList
&& ((ArrayList<?>) foundNode).size() > arrayNum) {
foundNode = ((ArrayList<?>) foundNode).get(arrayNum);
} else
return null;
if (node instanceof ArrayList<?> && ((ArrayList<?>) node).size() > arrayNum) {
node = ((ArrayList<?>) node).get(arrayNum);
}
}
} else
} else {
return null;
}
}
}
return foundNode;
return node;
}

private String[] decompose(String key) {

/**
* Splits a key by the dot character.
*
* @param key The key to split
* @return the split key path.
*/
private String[] splitByDot(String key) {
return key.split("\\.");
}
}
Loading