2013-02-26 07:57:26 +00:00
|
|
|
package com.moandjiezana.toml;
|
|
|
|
|
2014-04-08 14:37:02 +00:00
|
|
|
import java.io.BufferedReader;
|
2013-02-26 07:57:26 +00:00
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileNotFoundException;
|
2014-04-08 14:37:02 +00:00
|
|
|
import java.io.FileReader;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.InputStreamReader;
|
|
|
|
import java.io.Reader;
|
2014-04-09 08:49:41 +00:00
|
|
|
import java.math.BigDecimal;
|
|
|
|
import java.math.BigInteger;
|
|
|
|
import java.net.URL;
|
2014-04-07 11:10:44 +00:00
|
|
|
import java.util.ArrayList;
|
2014-08-12 20:44:24 +00:00
|
|
|
import java.util.Arrays;
|
2014-04-08 10:52:56 +00:00
|
|
|
import java.util.Collections;
|
2013-02-26 07:57:26 +00:00
|
|
|
import java.util.Date;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2014-04-09 08:49:41 +00:00
|
|
|
import java.util.Set;
|
2014-04-09 13:02:41 +00:00
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
2013-02-26 07:57:26 +00:00
|
|
|
|
2014-04-07 11:10:44 +00:00
|
|
|
import com.google.gson.Gson;
|
2014-07-22 13:50:39 +00:00
|
|
|
import com.google.gson.JsonElement;
|
2014-04-07 11:10:44 +00:00
|
|
|
|
2013-02-26 07:57:26 +00:00
|
|
|
/**
|
2014-04-08 10:52:56 +00:00
|
|
|
* <p>Provides access to the keys and tables in a TOML data source.</p>
|
2013-02-26 07:57:26 +00:00
|
|
|
*
|
2014-04-08 10:52:56 +00:00
|
|
|
* <p>All getters can fall back to default values if they have been provided.
|
|
|
|
* Getters for simple values (String, Date, etc.) will return null if no matching key exists.
|
|
|
|
* {@link #getList(String, Class)}, {@link #getTable(String)} and {@link #getTables(String)} return empty values if there is no matching key.</p>
|
|
|
|
*
|
|
|
|
* <p>Example usage:</p>
|
|
|
|
* <code><pre>
|
|
|
|
* Toml toml = new Toml().parse(getTomlFile());
|
|
|
|
* String name = toml.getString("name");
|
|
|
|
* Long port = toml.getLong("server.ip"); // compound key. Is equivalent to:
|
|
|
|
* Long port2 = toml.getTable("server").getLong("ip");
|
|
|
|
* MyConfig config = toml.to(MyConfig.class);
|
|
|
|
* </pre></code>
|
2013-02-26 07:57:26 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
public class Toml {
|
|
|
|
|
2014-07-22 13:50:39 +00:00
|
|
|
private static final Gson DEFAULT_GSON = new Gson();
|
|
|
|
private static final Pattern ARRAY_INDEX_PATTERN = Pattern.compile("(.*)\\[(\\d+)\\]");
|
|
|
|
|
2013-02-26 19:20:00 +00:00
|
|
|
private Map<String, Object> values = new HashMap<String, Object>();
|
2013-02-26 07:57:26 +00:00
|
|
|
private final Toml defaults;
|
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
/**
|
|
|
|
* Creates Toml instance with no defaults.
|
|
|
|
*/
|
2013-02-26 19:20:00 +00:00
|
|
|
public Toml() {
|
2014-04-09 13:02:41 +00:00
|
|
|
this(null);
|
2013-02-26 07:57:26 +00:00
|
|
|
}
|
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
/**
|
|
|
|
* @param defaults fallback values used when the requested key or table is not present.
|
|
|
|
*/
|
2013-02-26 19:20:00 +00:00
|
|
|
public Toml(Toml defaults) {
|
2014-04-09 13:02:41 +00:00
|
|
|
this(defaults, new HashMap<String, Object>());
|
2013-02-26 19:20:00 +00:00
|
|
|
}
|
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
/**
|
2014-04-08 14:37:02 +00:00
|
|
|
* Populates the current Toml instance with values from file.
|
2014-04-08 10:52:56 +00:00
|
|
|
*
|
|
|
|
* @param file
|
|
|
|
* @return this instance
|
|
|
|
* @throws IllegalStateException If file contains invalid TOML
|
|
|
|
*/
|
2013-02-26 19:20:00 +00:00
|
|
|
public Toml parse(File file) {
|
|
|
|
try {
|
2014-04-08 14:37:02 +00:00
|
|
|
return parse(new FileReader(file));
|
2013-02-26 19:20:00 +00:00
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
throw new RuntimeException(e);
|
2014-04-08 14:37:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Populates the current Toml instance with values from inputStream.
|
|
|
|
*
|
|
|
|
* @param inputStream
|
|
|
|
* @return this instance
|
|
|
|
* @throws IllegalStateException If file contains invalid TOML
|
|
|
|
*/
|
|
|
|
public Toml parse(InputStream inputStream) {
|
|
|
|
return parse(new InputStreamReader(inputStream));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Populates the current Toml instance with values from reader.
|
|
|
|
*
|
|
|
|
* @param reader
|
|
|
|
* @return this instance
|
|
|
|
* @throws IllegalStateException If file contains invalid TOML
|
|
|
|
*/
|
|
|
|
public Toml parse(Reader reader) {
|
|
|
|
BufferedReader bufferedReader = null;
|
|
|
|
try {
|
|
|
|
bufferedReader = new BufferedReader(reader);
|
|
|
|
|
|
|
|
StringBuilder w = new StringBuilder();
|
|
|
|
String line = bufferedReader.readLine();
|
|
|
|
while (line != null) {
|
|
|
|
w.append(line).append('\n');
|
|
|
|
line = bufferedReader.readLine();
|
2014-04-08 10:52:56 +00:00
|
|
|
}
|
2014-04-08 14:37:02 +00:00
|
|
|
parse(w.toString());
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
} finally {
|
|
|
|
try {
|
|
|
|
bufferedReader.close();
|
|
|
|
} catch (IOException e) {}
|
2013-02-26 19:20:00 +00:00
|
|
|
}
|
2014-04-08 14:37:02 +00:00
|
|
|
return this;
|
2013-02-26 07:57:26 +00:00
|
|
|
}
|
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
/**
|
|
|
|
* Populates the current Toml instance with values from tomlString.
|
|
|
|
*
|
|
|
|
* @param tomlString
|
|
|
|
* @return this instance
|
|
|
|
* @throws IllegalStateException If tomlString is not valid TOML
|
|
|
|
*/
|
|
|
|
public Toml parse(String tomlString) throws IllegalStateException {
|
2014-08-12 14:17:12 +00:00
|
|
|
Results results = new TomlParser().run(tomlString);
|
2013-02-26 07:57:26 +00:00
|
|
|
if (results.errors.length() > 0) {
|
|
|
|
throw new IllegalStateException(results.errors.toString());
|
|
|
|
}
|
|
|
|
|
2014-08-05 20:18:04 +00:00
|
|
|
this.values = results.consume();
|
2013-02-26 19:20:00 +00:00
|
|
|
|
|
|
|
return this;
|
2013-02-26 07:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public String getString(String key) {
|
|
|
|
return (String) get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Long getLong(String key) {
|
|
|
|
return (Long) get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public <T> List<T> getList(String key, Class<T> itemClass) {
|
2014-04-08 10:52:56 +00:00
|
|
|
List<T> list = (List<T>) get(key);
|
|
|
|
|
|
|
|
if (list == null) {
|
|
|
|
return Collections.emptyList();
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
2013-02-26 07:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public Boolean getBoolean(String key) {
|
|
|
|
return (Boolean) get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Date getDate(String key) {
|
|
|
|
return (Date) get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Double getDouble(String key) {
|
|
|
|
return (Double) get(key);
|
|
|
|
}
|
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
/**
|
|
|
|
* If no value is found for key, an empty Toml instance is returned.
|
|
|
|
*
|
|
|
|
* @param key
|
|
|
|
*/
|
2013-02-26 07:57:26 +00:00
|
|
|
@SuppressWarnings("unchecked")
|
2014-04-06 20:25:21 +00:00
|
|
|
public Toml getTable(String key) {
|
2014-04-09 13:02:41 +00:00
|
|
|
return new Toml(null, (Map<String, Object>) get(key));
|
2013-02-26 07:57:26 +00:00
|
|
|
}
|
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
/**
|
|
|
|
* If no value is found for key, an empty list is returned.
|
|
|
|
* @param key
|
|
|
|
*/
|
2014-04-07 11:10:44 +00:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public List<Toml> getTables(String key) {
|
2014-04-08 10:52:56 +00:00
|
|
|
List<Map<String, Object>> tableArray = (List<Map<String, Object>>) get(key);
|
|
|
|
|
|
|
|
if (tableArray == null) {
|
|
|
|
return Collections.emptyList();
|
|
|
|
}
|
|
|
|
|
2014-04-07 11:10:44 +00:00
|
|
|
ArrayList<Toml> tables = new ArrayList<Toml>();
|
2014-04-09 13:02:41 +00:00
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
for (Map<String, Object> table : tableArray) {
|
2014-04-09 13:02:41 +00:00
|
|
|
tables.add(new Toml(null, table));
|
2014-04-07 11:10:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return tables;
|
|
|
|
}
|
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
/**
|
|
|
|
* <p>Populates an instance of targetClass with the values of this Toml instance.
|
|
|
|
* The target's field names must match keys or tables.
|
|
|
|
* Keys not present in targetClass will be ignored.</p>
|
|
|
|
*
|
2014-04-09 08:49:41 +00:00
|
|
|
* <p>Tables are recursively converted to custom classes or to {@link Map Map<String, Object>}.</p>
|
|
|
|
*
|
|
|
|
* <p>In addition to straight-forward conversion of TOML primitives, the following are also available:</p>
|
|
|
|
*
|
|
|
|
* <ul>
|
|
|
|
* <li>TOML string to {@link Character}, {@link URL} or enum</li>
|
|
|
|
* <li>TOML number to any primitive (or wrapper), {@link BigInteger} or {@link BigDecimal}</li>
|
|
|
|
* <li>TOML array to {@link Set}</li>
|
|
|
|
* </ul>
|
2014-04-08 10:52:56 +00:00
|
|
|
*
|
|
|
|
* @param targetClass
|
|
|
|
*/
|
|
|
|
public <T> T to(Class<T> targetClass) {
|
2014-07-22 13:50:39 +00:00
|
|
|
return to(targetClass, DEFAULT_GSON);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Should not be used directly, except for testing purposes
|
|
|
|
*/
|
|
|
|
<T> T to(Class<T> targetClass, Gson gson) {
|
2014-04-08 10:52:56 +00:00
|
|
|
HashMap<String, Object> valuesCopy = new HashMap<String, Object>(values);
|
2014-04-09 13:02:41 +00:00
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
if (defaults != null) {
|
|
|
|
for (Map.Entry<String, Object> entry : defaults.values.entrySet()) {
|
|
|
|
if (!valuesCopy.containsKey(entry.getKey())) {
|
|
|
|
valuesCopy.put(entry.getKey(), entry.getValue());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-04-09 13:02:41 +00:00
|
|
|
|
2014-07-22 13:50:39 +00:00
|
|
|
JsonElement json = gson.toJsonTree(valuesCopy);
|
|
|
|
|
|
|
|
if (targetClass == JsonElement.class) {
|
|
|
|
return targetClass.cast(json);
|
|
|
|
}
|
|
|
|
|
2014-04-08 10:52:56 +00:00
|
|
|
return gson.fromJson(json, targetClass);
|
|
|
|
}
|
|
|
|
|
2013-02-26 07:57:26 +00:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
private Object get(String key) {
|
|
|
|
String[] split = key.split("\\.");
|
|
|
|
Object current = new HashMap<String, Object>(values);
|
2014-08-12 20:44:24 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < split.length; i++) {
|
|
|
|
if (i == 0 && values.containsKey(key)) {
|
|
|
|
return values.get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
String keyWithDot = join(Arrays.copyOfRange(split, i, split.length));
|
|
|
|
if (current instanceof Map && ((Map<String, Object>) current).containsKey(keyWithDot)) {
|
|
|
|
return ((Map<String, Object>) current).get(keyWithDot);
|
|
|
|
}
|
|
|
|
|
|
|
|
String splitKey = split[i];
|
2014-04-09 13:02:41 +00:00
|
|
|
Matcher matcher = ARRAY_INDEX_PATTERN.matcher(splitKey);
|
|
|
|
int index = -1;
|
|
|
|
|
|
|
|
if (matcher.find()) {
|
|
|
|
splitKey = matcher.group(1);
|
|
|
|
index = Integer.parseInt(matcher.group(2), 10);
|
2013-02-26 07:57:26 +00:00
|
|
|
}
|
2014-04-09 13:02:41 +00:00
|
|
|
|
|
|
|
current = ((Map<String, Object>) current).get(splitKey);
|
|
|
|
|
|
|
|
if (index > -1 && current != null) {
|
|
|
|
current = ((List<?>) current).get(index);
|
2013-02-26 07:57:26 +00:00
|
|
|
}
|
2014-04-09 13:02:41 +00:00
|
|
|
|
2013-02-26 07:57:26 +00:00
|
|
|
if (current == null) {
|
2014-04-09 13:02:41 +00:00
|
|
|
return defaults != null ? defaults.get(key) : null;
|
2013-02-26 07:57:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return current;
|
|
|
|
}
|
2014-08-12 20:44:24 +00:00
|
|
|
|
|
|
|
private String join(String[] strings) {
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
for (String string : strings) {
|
|
|
|
sb.append(string).append('.');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sb.length() > 0) {
|
|
|
|
sb.deleteCharAt(sb.length() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sb.toString();
|
|
|
|
}
|
2013-02-26 07:57:26 +00:00
|
|
|
|
2014-04-09 13:02:41 +00:00
|
|
|
private Toml(Toml defaults, Map<String, Object> values) {
|
2014-04-08 10:52:56 +00:00
|
|
|
this.values = values != null ? values : Collections.<String, Object>emptyMap();
|
2014-04-09 13:02:41 +00:00
|
|
|
this.defaults = defaults;
|
2013-02-26 07:57:26 +00:00
|
|
|
}
|
|
|
|
}
|