-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
28c948f
commit 9271aeb
Showing
5 changed files
with
344 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
version 'unspecified' | ||
|
||
apply plugin: 'java' | ||
|
||
sourceCompatibility = 1.5 | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
testCompile group: 'junit', name: 'junit', version: '4.11' | ||
} |
57 changes: 57 additions & 0 deletions
57
hocon/src/main/java/com/electronwill/nightconfig/hocon/HoconConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package com.electronwill.nightconfig.hocon; | ||
|
||
import com.electronwill.nightconfig.core.Config; | ||
import com.electronwill.nightconfig.core.MapConfig; | ||
import com.electronwill.nightconfig.core.serialization.*; | ||
import java.io.*; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
|
||
/** | ||
* @author TheElectronWill | ||
*/ | ||
public final class HoconConfig extends MapConfig implements FileConfig { | ||
|
||
private static final HashSet<Class<?>> SUPPORTED_TYPES = new HashSet<>(); | ||
|
||
static { | ||
SUPPORTED_TYPES.add(Integer.class); | ||
SUPPORTED_TYPES.add(Long.class); | ||
SUPPORTED_TYPES.add(Float.class); | ||
SUPPORTED_TYPES.add(Double.class); | ||
SUPPORTED_TYPES.add(Boolean.class); | ||
SUPPORTED_TYPES.add(String.class); | ||
SUPPORTED_TYPES.add(List.class); | ||
SUPPORTED_TYPES.add(Config.class); | ||
} | ||
|
||
@Override | ||
public boolean supportsType(Class<?> type) { | ||
return SUPPORTED_TYPES.contains(type) || List.class.isAssignableFrom(type) || Config.class.isAssignableFrom(type); | ||
} | ||
|
||
@Override | ||
public HoconConfig createEmptyConfig() { | ||
return new HoconConfig(); | ||
} | ||
|
||
@Override | ||
public void writeTo(File file) throws IOException { | ||
try (Writer fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { | ||
CharacterOutput output = new WriterOutput(fileWriter); | ||
HoconWriter jsonWriter = new HoconWriter(output); | ||
jsonWriter.writeJsonObject(this); | ||
}//finally closes the writer | ||
} | ||
|
||
@Override | ||
public void readFrom(File file) throws IOException { | ||
try (Reader fileReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { | ||
CharacterInput input = new ReaderInput(fileReader); | ||
HoconParser jsonParser = new HoconParser(input); | ||
this.asMap().clear();//clears the config | ||
jsonParser.parseJsonObject(this);//reads the value from the file to the config | ||
}//finally closes the reader | ||
} | ||
} |
164 changes: 164 additions & 0 deletions
164
hocon/src/main/java/com/electronwill/nightconfig/hocon/HoconParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package com.electronwill.nightconfig.hocon; | ||
|
||
import com.electronwill.nightconfig.core.serialization.CharacterInput; | ||
import com.electronwill.nightconfig.core.serialization.CharsWrapper; | ||
import com.electronwill.nightconfig.core.serialization.ParsingException; | ||
import com.electronwill.nightconfig.core.serialization.Utils; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
/** | ||
* @author TheElectronWill | ||
*/ | ||
public final class HoconParser { | ||
private static final char[] SPACES = {' ', '\t', '\n', '\r'}; | ||
private static final char[] TRUE_LAST = {'r', 'u', 'e'}, FALSE_LAST = {'a', 'l', 's', 'e'}; | ||
private static final char[] NULL_LAST = {'u', 'l', 'l'}; | ||
private static final char[] NUMBER_END = {',', '}', ']', ' ', '\t', '\n', '\r'}; | ||
|
||
private final CharacterInput input; | ||
|
||
public HoconParser(CharacterInput input) { | ||
this.input = input; | ||
} | ||
|
||
public HoconConfig parseHoconObject() { | ||
HoconConfig config = new HoconConfig(); | ||
parseHoconObject(config); | ||
return config; | ||
} | ||
|
||
public void parseHoconObject(HoconConfig config) { | ||
char firstChar = input.readCharAndSkip(SPACES); | ||
if (firstChar != '{') | ||
throw new ParsingException("Invalid first character for a json object: " + firstChar); | ||
parseObject(config); | ||
} | ||
|
||
private HoconConfig parseObject(HoconConfig config) { | ||
while (true) { | ||
char keyFirst = input.readCharAndSkip(SPACES); | ||
if (keyFirst != '"') | ||
throw new ParsingException("Invalid beginning of a key: " + keyFirst); | ||
|
||
String key = parseString(); | ||
char separator = input.readCharAndSkip(SPACES); | ||
if (separator != ':') | ||
throw new ParsingException("Invalid key/value separator: " + separator); | ||
|
||
char valueFirst = input.readCharAndSkip(SPACES); | ||
Object value = parseValue(valueFirst); | ||
config.setValue(key, value); | ||
|
||
char next = input.readCharAndSkip(SPACES); | ||
if (next == '}')//end of the object | ||
return config; | ||
else if (next != ',') | ||
throw new ParsingException("Invalid value separator: " + next); | ||
} | ||
} | ||
|
||
private List<Object> parseArray() { | ||
final List<Object> list = new ArrayList<>(); | ||
while (true) { | ||
char valueFirst = input.readCharAndSkip(SPACES); | ||
Object value = parseValue(valueFirst); | ||
list.add(value); | ||
|
||
char next = input.readCharAndSkip(SPACES); | ||
if (next == ']')//end of the array | ||
return list; | ||
else if (next != ',')//invalid separator | ||
throw new ParsingException("Invalid value separator: " + valueFirst); | ||
} | ||
} | ||
|
||
private Object parseValue(char firstChar) { | ||
switch (firstChar) { | ||
case '"': | ||
return parseString(); | ||
case '{': | ||
return parseObject(new HoconConfig()); | ||
case '[': | ||
return parseArray(); | ||
case 't': | ||
return parseTrue(); | ||
case 'f': | ||
return parseFalse(); | ||
case 'n': | ||
return parseNull(); | ||
default: | ||
return parseNumber(); | ||
} | ||
} | ||
|
||
private Number parseNumber() { | ||
CharsWrapper chars = input.readCharUntil(NUMBER_END); | ||
if (chars.contains('.') || chars.contains('e') || chars.contains('E')) {//must be a double | ||
return Utils.parseDouble(chars); | ||
} | ||
return Utils.parseLong(chars, 10); | ||
} | ||
|
||
private boolean parseTrue() { | ||
char[] chars = input.readChars(3); | ||
if (!Arrays.equals(chars, TRUE_LAST)) | ||
throw new ParsingException("Invalid value: t" + new CharsWrapper(chars) + " - expected boolean true"); | ||
return true; | ||
} | ||
|
||
private boolean parseFalse() { | ||
char[] chars = input.readChars(4); | ||
if (!Arrays.equals(chars, FALSE_LAST)) | ||
throw new ParsingException("Invalid value: f" + new CharsWrapper(chars) + " - expected boolean false"); | ||
return false; | ||
} | ||
|
||
private Object parseNull() { | ||
char[] chars = input.readChars(3); | ||
if (!Arrays.equals(chars, NULL_LAST)) | ||
throw new ParsingException("Invaid value: n" + new CharsWrapper(chars) + " - expected null"); | ||
return null; | ||
} | ||
|
||
private String parseString() { | ||
StringBuilder builder = new StringBuilder(); | ||
boolean escape = false; | ||
for (char c = input.readChar(); c != '"' || escape; c = input.readChar()) { | ||
if (escape) { | ||
builder.append(escape(c)); | ||
escape = false; | ||
} else if (c == '\\') { | ||
escape = true; | ||
} else { | ||
builder.append(c); | ||
} | ||
} | ||
return builder.toString(); | ||
} | ||
|
||
private char escape(char c) { | ||
switch (c) { | ||
case '"': | ||
case '\\': | ||
case '/': | ||
return c; | ||
case 'b': | ||
return '\b'; | ||
case 'f': | ||
return '\f'; | ||
case 'n': | ||
return '\n'; | ||
case 'r': | ||
return '\r'; | ||
case 't': | ||
return '\t'; | ||
case 'u': | ||
char[] chars = input.readChars(4); | ||
return (char)Utils.parseInt(chars, 16); | ||
default: | ||
throw new ParsingException("Invalid escapement: \\" + c); | ||
} | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
hocon/src/main/java/com/electronwill/nightconfig/hocon/HoconWriter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package com.electronwill.nightconfig.hocon; | ||
|
||
import com.electronwill.nightconfig.core.Config; | ||
import com.electronwill.nightconfig.core.serialization.CharacterOutput; | ||
import com.electronwill.nightconfig.core.serialization.SerializationException; | ||
import com.electronwill.nightconfig.core.serialization.Utils; | ||
import java.util.Iterator; | ||
import java.util.Map; | ||
|
||
/** | ||
* @author TheElectronWill | ||
*/ | ||
public final class HoconWriter { | ||
static final char[] NULL_CHARS = {'n', 'u', 'l', 'l'}; | ||
static final char[] TRUE_CHARS = {'t', 'r', 'u', 'e'}; | ||
static final char[] FALSE_CHARS = {'f', 'a', 'l', 's', 'e'}; | ||
static final char[] TO_ESCAPE = {'"', '\n', '\r', '\t', '\\'}; | ||
static final char[] ESCAPED = {'"', 'n', 'r', 't', '\\'}; | ||
|
||
private final CharacterOutput output; | ||
|
||
public HoconWriter(CharacterOutput output) { | ||
this.output = output; | ||
} | ||
|
||
public void writeJsonObject(Config config) { | ||
writeObject(config); | ||
} | ||
|
||
private void writeObject(Config config) { | ||
output.write('{'); | ||
final Iterator<Map.Entry<String, Object>> it = config.asMap().entrySet().iterator(); | ||
do { | ||
final Map.Entry<String, Object> entry = it.next(); | ||
final String key = entry.getKey(); | ||
final Object value = entry.getValue(); | ||
writeString(key); | ||
output.write(':'); | ||
writeValue(value); | ||
if (it.hasNext()) | ||
output.write(','); | ||
else | ||
break; | ||
|
||
} while (true); | ||
output.write('}'); | ||
} | ||
|
||
private void writeValue(Object v) { | ||
if (v == null) | ||
writeNull(); | ||
else if (v instanceof CharSequence) | ||
writeString((CharSequence)v); | ||
else if (v instanceof Number) | ||
output.write(v.toString()); | ||
else if (v instanceof Config) | ||
writeObject((Config)v); | ||
else if (v instanceof Iterable) | ||
writeArray((Iterable)v); | ||
else if (v instanceof Boolean) | ||
writeBoolean((boolean)v); | ||
else | ||
throw new SerializationException("Unsupported value type: " + v.getClass()); | ||
} | ||
|
||
private void writeArray(Iterable<?> iterable) { | ||
output.write('['); | ||
final Iterator<?> it = iterable.iterator(); | ||
do { | ||
Object value = it.next(); | ||
writeValue(value); | ||
if (it.hasNext()) | ||
output.write(','); | ||
else | ||
break; | ||
} while (true); | ||
output.write(']'); | ||
} | ||
|
||
private void writeBoolean(boolean b) { | ||
if (b) | ||
output.write(TRUE_CHARS); | ||
else | ||
output.write(FALSE_CHARS); | ||
} | ||
|
||
private void writeNull() { | ||
output.write(NULL_CHARS); | ||
} | ||
|
||
private void writeString(CharSequence s) { | ||
output.write('"'); | ||
final int length = s.length(); | ||
for (int i = 0; i < length; i++) { | ||
char c = s.charAt(i); | ||
int escapeIndex = Utils.arrayIndexOf(TO_ESCAPE, c); | ||
if (escapeIndex != -1) { | ||
char escaped = ESCAPED[escapeIndex]; | ||
output.write('\\'); | ||
output.write(escaped); | ||
} else { | ||
output.write(c); | ||
} | ||
} | ||
output.write('"'); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,5 @@ rootProject.name = 'Night Config' | |
include 'core' | ||
include 'json' | ||
include 'toml' | ||
include 'hocon' | ||
|