Compare commits

...

328 commits

Author SHA1 Message Date
Telesphoreo 23cc28b7b2
Update README.md 2022-05-05 22:29:02 -05:00
Telesphoreo d66054552a Add GitHub workflow 2022-05-05 21:12:07 -05:00
Telesphoreo 72179d67a8 Update Gradle + add Maven publishing 2022-05-05 21:11:15 -05:00
Telesphoreo 928a60487a Fix compilation errors 2022-05-05 21:04:31 -05:00
Telesphoreo 48df906a43 Update (done by @taahanis, I'm just repushing) 2022-05-05 20:51:35 -05:00
Moandji Ezana 1f32acc597
Merge pull request #71 from RedsTom/master
Added gradle to the dependency example
2021-07-27 07:04:38 +02:00
RedsTom 9f39895380
Added gradle to the dependency example 2021-07-17 13:35:17 +02:00
Moandji Ezana 4d206e4af6 [maven-release-plugin] prepare for next development iteration 2017-08-05 15:56:58 +02:00
Moandji Ezana 7f59ab2e51 [maven-release-plugin] prepare release toml4j-0.7.2 2017-08-05 15:56:52 +02:00
Moandji Ezana 885e536836 Change build to be able to release entirely from command line 2017-08-05 15:55:00 +02:00
Moandji Ezana 1fdfe4b4ad Update changelog 2017-08-05 15:23:23 +02:00
Moandji Ezana 3e29b470d4 Merge branch 'dependency-update' of git://github.com/DanilaFe/toml4j into DanilaFe-dependency-update 2017-08-05 15:18:19 +02:00
Moandji Ezana 293f512068 Fix https://github.com/mwanji/toml4j/issues/46 2017-08-05 15:12:28 +02:00
Danila Fedorin dd4d1b08bc Update dependencies to current versions.
I've run the tests provided in the maven file, and found no failures.
This should be fine.
2017-07-28 19:06:51 -07:00
Moandji Ezana e2b212e0a5 Add unit tests for spaces and strings in table names 2016-08-04 12:49:27 +02:00
Moandji Ezana b919f5f446 Bump version number in README 2016-07-27 15:39:30 +02:00
Moandji Ezana e6b66d2612 [maven-release-plugin] prepare for next development iteration 2016-07-27 15:37:39 +02:00
Moandji Ezana 3fee15dd9c [maven-release-plugin] prepare release toml4j-0.7.1 2016-07-27 15:37:34 +02:00
Moandji Ezana 214143fe84 Support single quotes in table name
Fixes #36
2016-07-27 15:35:20 +02:00
Moandji Ezana 2d2a2c2708 Merge branch 'wip' 2016-07-18 08:43:59 +02:00
Moandji Ezana 644c23f17d Clarify how to serialise a top-level table array.
Fixes #35
2016-07-18 08:43:28 +02:00
Moandji Ezana e0ce9c67e7 [maven-release-plugin] prepare for next development iteration 2016-07-12 13:18:33 +02:00
Moandji Ezana cc039ff25f [maven-release-plugin] prepare release toml4j-0.7.0 2016-07-12 13:18:28 +02:00
Moandji Ezana bede018b3d Bump version in documentation 2016-07-12 13:13:32 +02:00
Moandji Ezana 120d1396fd Add Toml#read(Toml)
Fixes #32
2016-07-12 13:12:13 +02:00
Moandji Ezana e27ba3e8fb Rename TomlParseTest to TomlReadTest 2016-07-12 12:18:17 +02:00
Moandji Ezana 40f866f5cd Mention in README that transient fields are not serialised 2016-06-15 09:42:47 -04:00
Moandji Ezana ddeab75689 [maven-release-plugin] prepare for next development iteration 2016-06-15 09:34:07 -04:00
Moandji Ezana 0a5b5190b9 [maven-release-plugin] prepare release toml4j-0.6.0 2016-06-15 09:32:05 -04:00
Moandji Ezana ea845af1de Update Maven build to latest plugin versions and current recommendations 2016-06-15 09:04:45 -04:00
Moandji Ezana 55d5b91e1d Merge branch 'wip' 2016-06-14 20:22:05 -04:00
Moandji Ezana d977160e1b Normalise line endings on Windows in tests 2016-06-14 20:19:33 -04:00
Moandji Ezana f0da6d4c36 Bump version to 0.6.0 2016-06-14 20:01:22 -04:00
Moandji Ezana b7d3b34619 Support positive offset in datetime
Fixes #31
2016-06-14 19:58:13 -04:00
Moandji Ezana a37dfd80a8 Transient fields are not written to TOML files by TomlWriter
Fixes #30
2016-06-14 19:51:22 -04:00
Moandji Ezana ab05197c9d Add Toml#toMap()
Fixes #29
2016-05-09 22:58:46 -04:00
moandji.ezana 15666880a8 [maven-release-plugin] prepare for next development iteration 2016-01-25 09:39:56 -04:00
moandji.ezana 963507ae61 [maven-release-plugin] prepare release toml4j-0.5.1 2016-01-25 09:39:52 -04:00
moandji.ezana 6318013c81 Specify Java 1.6 and above 2016-01-25 09:35:56 -04:00
moandji.ezana acca966946 Merge branch 'wip' 2016-01-25 09:33:42 -04:00
moandji.ezana a5e34f464f Update version number in README 2016-01-24 22:14:16 -04:00
moandji.ezana d9b3c191fb Merge branch 'wip' of git@github.com:mwanji/toml4j.git into wip 2016-01-24 22:12:26 -04:00
moandji.ezana e024b51b0c Fix handling of tables with same name in different table array items.
Fixes https://github.com/mwanji/toml4j/issues/26
2016-01-24 22:10:35 -04:00
Moandji Ezana 2f45ce657c Merge pull request #25 from dilecti/wip
Update README for the 0.5.0 release.
2016-01-24 15:43:11 -04:00
Jonathan Wood aacd2bf319 Update README for the 0.5.0 release. 2016-01-14 08:23:11 -08:00
moandji.ezana b2daf2475f [maven-release-plugin] prepare for next development iteration 2015-12-10 12:26:42 -04:00
moandji.ezana a5f0c6aec7 [maven-release-plugin] prepare release toml4j-0.5.0 2015-12-10 12:26:37 -04:00
moandji.ezana b1436c557d Rename Toml#containsKey() to containsPrimitive 2015-12-10 12:23:08 -04:00
moandji.ezana 41b5438707 Prepare for 0.5.0 release 2015-12-10 11:50:30 -04:00
moandji.ezana 175c8d645f Add link to contribution guidelines 2015-12-10 11:47:46 -04:00
moandji.ezana 23567297fb Switch back to Java 6 2015-12-10 11:46:20 -04:00
moandji.ezana c88f283b3c Use Java 8 2015-10-28 20:44:01 -04:00
moandji.ezana 5740936619 Update docs 2015-10-28 20:42:00 -04:00
moandji.ezana 4773638a12 Read files as UTF-8. Fixes https://github.com/mwanji/toml4j/issues/22 2015-08-15 02:12:44 +02:00
moandji.ezana d60736e104 Rename parse to read and ValueConverter to ValueReader 2015-08-14 11:12:08 +02:00
moandji.ezana 039b487115 Set OutputStreamWriter to UTF-8. Document UTF-8 requirements. 2015-07-29 13:45:24 +02:00
Jonathan Wood b21391add7 Improve readability for special char code.
When populating specialCharacterEscapes, use character representations
rather than hex-formatted numbers.
2015-07-18 12:25:58 -07:00
moandji.ezana 212bd49bf6 Delete commented code 2015-07-14 22:30:44 +02:00
moandji.ezana 97e0133450 Write Unicode symbols instead of escaping them 2015-07-14 22:29:54 +02:00
moandji.ezana c90d4e1e71 TomlWriter#write() throws IllegalArgumentException when source object is
of an invalid type.
2015-07-14 01:51:09 +02:00
moandji.ezana 16915c8edb Merge branch 'writer' of git@github.com:mwanji/toml4j.git into writer
Conflicts:
	src/main/java/com/moandjiezana/toml/TomlWriter.java
	src/test/java/com/moandjiezana/toml/TomlWriterTest.java
2015-07-14 01:44:46 +02:00
moandji.ezana dbbd2f5e0e Do not write types that would not produce valid TOML 2015-07-14 01:33:11 +02:00
moandji.ezana 30d4898938 Document TomlWriter threadsafety in readme. 2015-07-13 23:46:09 +02:00
Jonathan Wood 568a77b1bf Refuse to write TOML fragments. 2015-07-04 17:50:52 -07:00
Jonathan Wood 8a8319b5f9 Write empty nested arrays properly. 2015-07-03 13:57:09 -07:00
moandji.ezana 50522d6a8c Include final fields but ignore constants 2015-07-02 08:22:31 +02:00
moandji.ezana 912ab61158 Test that keys are quoted when writing from an object and in table names 2015-07-02 07:59:30 +02:00
moandji.ezana 9358162a58 Remove ValueWriters#write(Object, WriterContext) 2015-07-02 07:41:22 +02:00
moandji.ezana 95e7f28cfa Remove warnings 2015-07-02 07:31:51 +02:00
moandji.ezana 47bd5022f1 Rename WriterIndentationPolicy to IndentationPolicy 2015-07-02 07:21:48 +02:00
moandji.ezana 7d5e429e12 Remove unused code 2015-07-01 13:01:16 +02:00
moandji.ezana fa591a0f0c Improve test coverage with test on error message for nested heterogenous
arrays
2015-07-01 12:59:12 +02:00
moandji.ezana 1244fdbfb3 Reduce visibility of public methods on WriterContext 2015-07-01 12:47:28 +02:00
moandji.ezana dbab02ce52 TomlWriter can display fractional seconds 2015-07-01 12:25:40 +02:00
moandji.ezana b59a246b86 Timezone can be set on TomlWriter.
DateConverterJdk6 class handles Java 6 date formatting limitations.
2015-07-01 11:22:29 +02:00
moandji.ezana a6e31da5ca Streamlined writing dates in UTC timezone. 2015-07-01 10:21:10 +02:00
moandji.ezana d9abbe7bc3 Updated encoding documentation 2015-07-01 09:32:16 +02:00
moandji.ezana ea36399255 Streamline WriterContext constructors. Make instance variables final
where possible.
2015-06-30 12:31:57 +02:00
moandji.ezana 6340d39ba7 Reduce visibility of TomlWriter#getIndentationPolicy() 2015-06-30 12:23:28 +02:00
moandji.ezana 5af1ca6f06 Removed unnecessary SuppressWarnings 2015-06-30 12:19:40 +02:00
moandji.ezana 56be048ab1 Created TomlWriter.Builder, so TomlWriter and IndentationPolicy can be
immutable
2015-06-30 11:37:22 +02:00
moandji.ezana 0f42d37f60 Updated changelog 2015-06-29 21:30:02 +02:00
moandji.ezana d8a02ce356 Support for special cases: char, Character, URL, URI and enums. 2015-06-29 20:52:32 +02:00
moandji.ezana 210d9dcb3c Renamed ValueWriterTest to TomlWriterTest 2015-06-29 20:11:59 +02:00
moandji.ezana d9964fd61a Added an example for each TomlWriter#write method 2015-06-29 20:10:43 +02:00
moandji.ezana 3a3915416a Added finally block to TomlWriter#write(Object, File) to close
FileWriter
2015-06-29 19:41:44 +02:00
moandji.ezana feb3aec259 TomlWriter#write methods delegate to TomlWriter#write(Object, Writer) to
take advantage of streaming when possible.
2015-06-29 18:49:34 +02:00
moandji.ezana 123aa21aed Moved BurntSushi encoder tests to own class to make it easier to see
which tests have been implemented.
2015-06-29 10:13:12 +02:00
moandji.ezana 9968add0ae Removed ValueWriter#isTable() 2015-06-29 09:57:05 +02:00
moandji.ezana 121db7ef05 Merge branch 'wip' of git://github.com/dilecti/toml4j into writer 2015-06-29 08:30:43 +02:00
Jonathan Wood a879385f4c Tweak test for openjdk-6. 2015-06-28 20:18:27 -07:00
Jonathan Wood 99c48231c2 Refuse to write heterogeneous arrays. 2015-06-28 14:35:39 -07:00
Jonathan Wood 7ce548e616 Note writer options in the readme. 2015-06-28 14:15:38 -07:00
Jonathan Wood f3dca7d24f Remove extraneous tests.
The burntsushi tests provide adequate coverage of these.
2015-06-28 14:09:20 -07:00
Jonathan Wood b550e638c7 Run burntsushi tests on TomlWriter, where possible. 2015-06-28 13:54:29 -07:00
Jonathan Wood 1a0d38bc72 Improve wording for array whitespace controls 2015-06-28 13:37:47 -07:00
Jonathan Wood 77f7b922be Fix escaping of back slashes. 2015-06-28 13:35:34 -07:00
Jonathan Wood 3d90260d32 Add methods to control date formatting and time zones. 2015-06-28 11:52:40 -07:00
Jonathan Wood ecbd9f048d Add a way to control spacing around array brackets. 2015-06-28 11:26:49 -07:00
Jonathan Wood 72941c146d Add writer indentation policy controls.
Set the default indentation to 0.
2015-06-28 10:30:35 -07:00
Jonathan Wood 24a6503d19 Conform to the project's singleton pattern. 2015-06-27 14:35:26 -07:00
Jonathan Wood 8b3652a309 Remove classes merged into Converters. 2015-06-27 14:27:38 -07:00
Jonathan Wood bcc52f5cb0 Use new 'write' vocabulary in the docs. 2015-06-27 14:23:52 -07:00
Jonathan Wood ec30be7901 Move Toml.write() to TomlWriter.
Add TomlWriter methods for writing to File, Writer, and OutputStream
targets.
2015-06-27 14:19:40 -07:00
Jonathan Wood acc952a572 Remove Toml.serialize(). 2015-06-27 13:11:52 -07:00
Jonathan Wood c076099ddd Use 'write' rather than 'serialize'.
Also refactor ValueWriter implementations into ValueConverter
implementations, where possible.
2015-06-27 12:50:15 -07:00
Jonathan Wood ca6e043926 Change bouncy caps to underscores in test names. 2015-06-25 09:31:36 -07:00
Jonathan Wood b729e450ab Fix thread-safety issue in DateSerializer. 2015-06-25 09:21:36 -07:00
Jonathan Wood 1216a009cb 4 -> 2 spaces 2015-06-25 08:01:34 -07:00
Jonathan Wood 4ed3740732 Fix Date serialization testing.
The test can be run in any time zone, so always test with a Date instance
from the local time zone where the test is being run.
2015-06-25 07:56:04 -07:00
Moandji Ezana ad8a9fd932 Switched build status badge to wip branch 2015-06-25 11:09:19 +02:00
Moandji Ezana f210ad02fb Switched dependency status badge to wip branch 2015-06-25 11:03:33 +02:00
Moandji Ezana 2fec857f14 Changed coverage status badge to wip branch 2015-06-25 10:56:11 +02:00
Jonathan Wood 64afb09d96 Java-6 style date/tz formatting 2015-06-24 23:35:19 -07:00
Jonathan Wood 9f717384a4 Don't use java.util.function.Predicate.
It is a Java-8 addition.
2015-06-24 22:14:42 -07:00
Jonathan Wood bb0d9b1146 Add serialization to changelog. 2015-06-24 21:55:37 -07:00
Jonathan Wood f8f55a6f16 Merge remote-tracking branch 'upstream/wip' into merge
Conflicts:
	README.md
	src/main/java/com/moandjiezana/toml/Toml.java
2015-06-24 21:49:24 -07:00
Jonathan Wood e7d7de7ae5 Implement serialization.
This provides functionality to convert populated Toml instances
and arbitrary objects into TOML.
2015-06-24 21:40:06 -07:00
moandji.ezana 8571d258ca Updated coveralls plugin command in Travis CI config 2015-06-24 01:07:58 +02:00
moandji.ezana 38666593dd Added Toml#contains(String) and Toml#containsXxx(String) methods 2015-06-24 01:07:34 +02:00
moandji.ezana 0cea9ae187 Added test cases for iterating over empty TOML and key containing empty
list
2015-06-24 00:49:41 +02:00
moandji.ezana 1b2aa582d9 Removed trailing newlines from error messages.
Fixes https://github.com/mwanji/toml4j/issues/18
2015-06-23 23:46:35 +02:00
moandji.ezana 9f29f6951f Updated outdated dependencies and removed unused fest-reflect dependency 2015-06-23 23:24:48 +02:00
moandji.ezana 1c3e0642ae Renamed IteratorTest -> IterationTest 2015-06-23 23:05:18 +02:00
moandji.ezana 80008fca15 Updated Reflection section of readme 2015-06-23 23:05:18 +02:00
moandji.ezana 4074242e0b Using a private implementation of Map.Entry instead of public Toml.Entry
class
2015-06-23 23:05:18 +02:00
moandji.ezana 68513b0851 Added to Toml#entrySet() documentation 2015-06-23 23:05:18 +02:00
moandji.ezana 15c014a327 Added documentation 2015-06-23 23:05:18 +02:00
moandji.ezana a0bd307f58 Added Toml#entrySet() 2015-06-23 23:05:18 +02:00
moandji.ezana 5402ce22f3 Updated change log 2015-04-29 22:25:41 +02:00
moandji.ezana ae2911c82c Toml#getTables() returns null for missing keys and indices 2015-04-29 22:10:30 +02:00
moandji.ezana 24a5134c2c getTable() and getTables() return null for missing keys 2015-04-29 21:46:22 +02:00
moandji.ezana a210896477 Improved a test's assertion semantics 2015-04-29 21:36:44 +02:00
moandji.ezana a342b1fef6 Toml#getList() returns null if key is missing 2015-04-29 20:30:11 +02:00
moandji.ezana 4d32f77ce0 Removed Toml#to(Class, Gson) 2015-04-29 00:19:39 +02:00
moandji.ezana 0817b40d8f Simplified test names 2015-04-13 13:24:52 +02:00
moandji.ezana 8cae6c2e32 Improved error message when key overwrites table 2015-04-13 13:23:58 +02:00
moandji.ezana 0795127266 New error message when table overwrites existing key definition 2015-04-13 13:04:10 +02:00
moandji.ezana 1a6ddbd5ac Moved tests to more appropriate classes 2015-04-13 11:42:05 +02:00
moandji.ezana 1e19f0847f Updated contributon guidelines 2015-03-30 23:00:10 +02:00
moandji.ezana af5a2a5867 Updated docs and changelog 2015-03-30 22:57:14 +02:00
moandji.ezana 439d1c600d Fixed typo 2015-03-30 22:46:13 +02:00
moandji.ezana d958baebb5 Added getArray(String, List) 2015-03-30 22:45:15 +02:00
moandji.ezana c0eafb9f17 DRY-er date creation in tests 2015-03-30 22:31:13 +02:00
moandji.ezana d31792e5a8 Added test to show that constructor defaults take precedence over method
defaults
2015-03-30 22:09:12 +02:00
moandji.ezana 7f11f97020 Merge branch 'default_values' of git://github.com/udiabon/toml4j into default_values 2015-03-30 21:51:33 +02:00
moandji.ezana f5e19dbb4b Merge branch 'master' into wip 2015-03-30 21:48:27 +02:00
Nick Jiang 414bd396fa Call original method to avoid duplicated work. 2015-03-27 10:13:09 +08:00
Nick Jiang 3773c4eadf Remove the ignores use global .gitignore instead. 2015-03-27 09:41:43 +08:00
moandji.ezana 5a608cad02 Clarified that PRs should be against wip branch 2015-03-26 11:25:26 +02:00
Nick Jiang bc04eec538 Returns the default value if the key was not found. 2015-03-26 16:50:48 +08:00
Nick Jiang 7f0ea04e99 Ignore the folder "target". 2015-03-26 16:46:39 +08:00
moandji.ezana ffb0e08290 Fixed typo 2015-03-11 23:06:55 +02:00
moandji.ezana 8d183061ec Fixed typo 2015-03-11 23:01:31 +02:00
moandji.ezana baec3a1697 Added VersionEye badge to README 2015-02-19 21:26:27 +02:00
moandji.ezana 598c319ef3 Bumped version and updated change log
[ci skip]
2015-02-17 10:28:27 +02:00
moandji.ezana 252a704a61 Merge branch 'master' into wip 2015-02-17 10:26:27 +02:00
moandji.ezana 8c66284960 In number, underscore after dot is invalid 2015-02-17 10:24:45 +02:00
moandji.ezana 7aa496582c Added v0.4.0 example 2015-02-17 10:21:57 +02:00
moandji.ezana a3edb55e9c Merge branch 'underscores_in_numbers' into wip. Fixed version example
test.

Conflicts:
	src/main/java/com/moandjiezana/toml/NumberConverter.java
2015-02-17 10:21:17 +02:00
moandji.ezana 8d9f08e93f [maven-release-plugin] prepare for next development iteration 2015-02-16 14:56:43 +02:00
moandji.ezana 8528c22b93 [maven-release-plugin] prepare release toml4j-0.4.0 2015-02-16 14:56:33 +02:00
moandji.ezana 58e4f9191c Updates for release 2015-02-16 14:54:08 +02:00
moandji.ezana 7e88ad0f93 Merge branch 'wip'
Conflicts:
	README.md
2015-02-16 14:49:21 +02:00
moandji.ezana 4e22176f1e Improved duplicate table detection. Improved line reporting. 2015-02-16 13:33:02 +02:00
moandji.ezana 2b9af91bf4 Improved detection of invalid use of inline tables. 2015-02-15 01:44:13 +02:00
moandji.ezana 40f12ca3b1 Throw exception when duplicate keys are defined inside an inline table 2015-02-14 02:39:39 +02:00
moandji.ezana 14896a6033 Code coverage improvements 2015-02-14 01:36:14 +02:00
moandji.ezana ba98daeb85 Added Toml#isEmpty()
Added tests for navigation to non-existant keys
2015-02-13 09:09:05 +02:00
moandji.ezana ca7fcc766b Removed unused methods and variables 2015-02-13 08:44:54 +02:00
moandji.ezana be85328555 Javadoc improvements 2015-02-13 08:43:35 +02:00
moandji.ezana cdef758f4c Made TomlParser#run(String) static 2015-02-13 08:19:40 +02:00
moandji.ezana b69f8514cb Deleted unused constant 2015-02-13 08:18:56 +02:00
moandji.ezana 96220b7a88 The TOML string is never transformed into an array 2015-02-12 23:25:10 +02:00
moandji.ezana 8388e394ed Removed unused methods 2015-02-12 23:12:50 +02:00
moandji.ezana 6e4c8fd022 Documentation clean up for version 0.4.0 2015-02-12 22:56:45 +02:00
moandji.ezana d468ec1892 Updated copyright date 2015-02-12 22:55:55 +02:00
moandji.ezana d28fffa4e6 Added support for Unicode characters in quoted keys 2015-02-12 22:38:40 +02:00
moandji.ezana 1c87a9e85a Improved bare key validation and quoted key support 2015-02-12 22:38:17 +02:00
moandji.ezana 8a6ca61101 Cleaned up bare key handling and started cleaning up identifier
validation
2015-02-12 11:21:00 +02:00
moandji.ezana 7292fe5468 Added Results.Errors to Context constructor and Context.with(Identifier) 2015-02-12 08:33:43 +02:00
moandji.ezana 786529fce6 Moved ValueConverterUtils.isComment(String) to ValueConverters 2015-02-12 08:31:34 +02:00
moandji.ezana c0e78db681 Improved error message reporting 2015-02-11 17:59:20 +02:00
moandji.ezana c4027ed2d5 Merge branch 'inline_tables' into wip
Conflicts:
	src/main/java/com/moandjiezana/toml/Results.java
	src/main/java/com/moandjiezana/toml/TomlParser.java
	src/test/java/com/moandjiezana/toml/ErrorMessagesTest.java
2015-02-11 16:43:48 +02:00
moandji.ezana 5567f6c8bd Updated change log 2015-02-11 16:02:43 +02:00
moandji.ezana 66f7946542 Added test for mixed inline tables in array 2015-02-11 16:02:30 +02:00
moandji.ezana 21c5dfd235 Improved error messages 2015-02-11 15:55:02 +02:00
moandji.ezana 3e2d9fad37 Simplified TomlParser 2015-02-11 15:54:51 +02:00
moandji.ezana d1d7145a03 Streamlined array and inline table converters to re-use other converters
[ci skip]
2015-02-11 00:59:48 +02:00
moandji.ezana ddb061b9f9 Added support for nested inline tables 2015-02-10 20:06:46 +02:00
moandji.ezana 5bd87a6b56 Added support for arrays in inline tables 2015-02-10 14:32:44 +02:00
moandji.ezana a5a6ab22dc Support for very basic inline tables 2015-02-09 22:15:37 +02:00
moandji.ezana de1bcbdca2 Reduced method visibility 2015-02-09 22:14:58 +02:00
moandji.ezana 91e5c65577 Improved error handling code and line numbers included in error messages 2015-02-09 22:14:25 +02:00
moandji.ezana d32ead52d4 Improved error handling code and line numbers included in error messages 2015-02-09 16:29:20 +02:00
moandji.ezana 55d598c3b1 Added inceptionYear and ciManagement 2015-02-09 09:46:54 +02:00
moandji.ezana 9093fec59b Added support for underscores in numbers 2015-02-09 09:41:04 +02:00
moandji.ezana 1bc8d4bcc5 Removed misc. section of change log. Added to CONTRIBUTING.md. Clarified
javadoc.

[ci skip]
2015-02-08 23:56:54 +02:00
moandji.ezana 1a8e991f95 Added CONTRIBUTING.md 2015-02-08 23:32:59 +02:00
moandji.ezana 4615ed50f7 Cleaned up warnings 2015-02-08 23:28:09 +02:00
moandji.ezana 7b7906d0ed Updated change log and readme 2015-02-08 23:16:06 +02:00
moandji.ezana 5a9dd3c58c Merge branch 'getList_Parameter' of git://github.com/d3xter/toml4j into shorter_getList 2015-02-08 23:00:59 +02:00
moandji.ezana b70661a683 Removed errant main method 2015-02-08 22:55:06 +02:00
moandji.ezana 5d17559728 Consolidated all number parsing into NumberConverter 2015-02-08 22:54:28 +02:00
Andreas Sinz 0baf7ea32e Removed unnecessary Class parameter from getList() 2015-02-08 11:23:57 +01:00
moandji.ezana 0dc5f6d206 Added support for whitespace around table and table array name segments 2015-01-25 14:31:17 +02:00
moandji.ezana 0f198e5a5e Arrays handle mixed string types. Removed Parboiled dependency. 2015-01-25 14:18:44 +02:00
moandji.ezana 84de939b7c Removed StringBuilder from literal string processors 2015-01-23 22:22:29 +02:00
moandji.ezana 77788d3c87 Multiline literal strings no longer handled by Parboiled 2015-01-23 22:03:55 +02:00
moandji.ezana 40cab5c499 Literal strings no longer handled by Parboiled 2015-01-23 16:36:15 +02:00
moandji.ezana bde639951a Exponents no longer handled by Parboiled 2015-01-23 16:06:06 +02:00
moandji.ezana b6b0d733a5 Integers no longer handled by Parboiled 2015-01-23 15:48:09 +02:00
moandji.ezana 7380d74592 Booleans no longer handled by Parboiled 2015-01-23 15:27:48 +02:00
moandji.ezana e0762bf82a Removed duplicate comment checks 2015-01-23 15:22:46 +02:00
moandji.ezana b7b546dc83 Improved handling of bare and quoted keys 2015-01-23 15:17:50 +02:00
moandji.ezana f804c99534 Table array name parsing no longer handled by Parboiled 2015-01-23 14:17:39 +02:00
moandji.ezana 537c75757e Removed support for escaping a slash 2015-01-22 16:02:32 +02:00
moandji.ezana 9a8b85c6ea Updated dependencies and plugins 2015-01-22 15:40:55 +02:00
moandji.ezana 3ca947785d Added test for example-v0.3.0.toml 2015-01-22 15:18:03 +02:00
moandji.ezana 6ad2743276 Removed unnecessary code 2015-01-22 15:16:54 +02:00
moandji.ezana ac3c0fd230 Adapted hard_example to bare/quoted key restrictions 2015-01-22 15:06:11 +02:00
moandji.ezana be3b027d1e Fixed exponent handling 2015-01-22 15:05:47 +02:00
moandji.ezana ba84494583 Fixed handling of short-form unicode escapes 2015-01-22 14:54:50 +02:00
moandji.ezana 1380c72d48 Limited the range of characters bare keys can accept 2015-01-22 14:39:08 +02:00
moandji.ezana 89d2c70b15 Merge branch 'quoted_keys' into wip
Conflicts:
	README.md
2015-01-22 12:20:06 +02:00
moandji.ezana df235e5a29 Added support for quoted keys with index arrays 2015-01-22 10:38:41 +02:00
moandji.ezana 62b2718a17 Matched README to wip branch 2015-01-15 11:02:22 +02:00
moandji.ezana 49f0d4b6c0 Fixed typo
[ci skip]
2015-01-15 01:57:36 +02:00
moandji.ezana 0026bb3467 Improved display of TOML -> Java mappings
[ci skip]
2015-01-15 01:48:51 +02:00
moandji.ezana 6906dea7d0 Improved display of TOML code blocks 2015-01-15 01:32:51 +02:00
moandji.ezana 112d56d60c Improved display of TOML code blocks 2015-01-15 01:30:34 +02:00
moandji.ezana 8fd7e6691c Changed badges to match wip branch
[ci skip]
2015-01-14 10:07:26 +02:00
moandji.ezana cd94968907 Updated version number and improved badges
[ci skip]
2015-01-14 10:04:15 +02:00
moandji.ezana 9f89f6ee6e Added license badge
[ci skip]
2015-01-14 09:53:19 +02:00
moandji.ezana db950ab054 Added support for quoted keys
https://github.com/toml-lang/toml/pull/283
2015-01-14 09:46:36 +02:00
moandji.ezana 98701694c4 [maven-release-plugin] prepare for next development iteration 2014-12-16 01:14:16 +02:00
moandji.ezana 51fcf76e4e [maven-release-plugin] prepare release toml4j-0.3.1 2014-12-16 01:14:07 +02:00
moandji.ezana 28c57069c6 Fixed Javadoc for Java 8 2014-12-16 01:07:59 +02:00
moandji.ezana e425f81011 [maven-release-plugin] prepare for next development iteration 2014-12-16 01:06:46 +02:00
moandji.ezana 7805561133 [maven-release-plugin] prepare release toml4j-0.3.1 2014-12-16 01:06:37 +02:00
moandji.ezana 8a16f487f5 Fixed Travis CI badge 2014-12-16 00:49:00 +02:00
moandji.ezana 3cec32eb8e Added Maven Central to badges 2014-12-16 00:47:10 +02:00
moandji.ezana b0e933fb28 Prepared README for release 2014-12-16 00:37:20 +02:00
moandji.ezana 14684e07d2 Updated changelog and readme 2014-12-16 00:31:26 +02:00
moandji.ezana 5ed9a4155e Test that key name cannot start with [ 2014-12-15 23:47:06 +02:00
moandji.ezana 714fb81330 Support for unicode characters starting with \U 2014-12-15 16:02:49 +02:00
moandji.ezana 1b3c08a433 Empty table names cause parsing to fail 2014-12-15 15:58:19 +02:00
moandji.ezana 12e73fb314 Added support for RFC 3339 dates. 2014-12-15 15:44:03 +02:00
moandji.ezana 6267ac6c9d Split tests into more manageable chunks 2014-12-15 13:01:48 +02:00
moandji.ezana 28ef345bba Updated tests from BurntSushi/toml-test 2014-12-15 13:01:17 +02:00
moandji.ezana 1390487d3f Support for exponents and doubles with a plus sign 2014-11-10 23:02:10 +02:00
moandji.ezana 16e9fa16f0 Support integer with leading plus sign 2014-11-10 22:01:46 +02:00
moandji.ezana 53aba456ce Updated to Gson 2.3 2014-11-06 03:42:43 +02:00
moandji.ezana 729e0e4ed9 Fail on unterminated multiline strings 2014-11-02 18:13:58 +02:00
moandji.ezana 4c9d28bfc4 Deleted unused code 2014-11-02 18:13:26 +02:00
moandji.ezana 7a780b58b3 Moved test file to correct folder
(I didn't see it before because it failed in Maven but not in Eclipse)
2014-11-02 13:26:49 +02:00
moandji.ezana 39300478f1 Added multiline and raw string tests from BurntSushi 2014-11-02 13:22:20 +02:00
moandji.ezana 4eb948b694 Updated README for wip-specific information 2014-10-10 12:27:38 +02:00
moandji.ezana 16c8b409e6 Added test to confirm that out-of-order table arrays are supported.
cf. https://github.com/toml-lang/toml/issues/252
2014-10-10 11:45:22 +02:00
moandji.ezana ccca314649 Parse multiline string literals and all multiline strings with text on
the first line
2014-08-22 23:59:02 +02:00
moandji.ezana 1f290c94b8 Removed unused variable and added static imports. 2014-08-22 22:30:18 +02:00
moandji.ezana fcd77dca6b Renamed ValueParser#String() to ValueParser#StringToken() to avoid
conflicts/confusion with Parboiled method.
2014-08-13 20:24:58 +02:00
moandji.ezana dfd5501132 Introduced Multiline enum to clean TomlParser up a bit 2014-08-13 20:22:34 +02:00
moandji.ezana b6af2c6d48 ValueConverterUtils.parse() and ValueConverterUtils.parser() make it
easier to use Parboiled.
2014-08-13 16:01:31 +02:00
moandji.ezana 2b8bf136a9 Renamed StatementParser to ValueParser 2014-08-13 14:36:21 +02:00
moandji.ezana 697349e9a2 Renamed ValueParser to ValueConverter and associated changes 2014-08-13 14:31:50 +02:00
moandji.ezana c841c4e1fe Removed regexes from BooleanParser and IntegerParser and using Parboiled
instead
2014-08-13 14:27:23 +02:00
moandji.ezana cd7b7b68d9 Loop over parsers 2014-08-13 12:09:04 +02:00
moandji.ezana 7e101ce3a3 Added support for literal strings 2014-08-13 11:28:18 +02:00
moandji.ezana 8f877776d6 Support for multiline strings 2014-08-13 00:28:12 +02:00
moandji.ezana 2d76120562 Changelog lists changes to upcoming version 2014-08-12 23:20:23 +02:00
moandji.ezana 2667e2c20d Test that key names in table arrays can contain dots 2014-08-12 23:17:07 +02:00
moandji.ezana 1dd40b14ed Separated error messages with a new line. 2014-08-12 22:45:37 +02:00
moandji.ezana ff4c0cbc16 Key names can have dots in them 2014-08-12 22:44:24 +02:00
moandji.ezana b2888519fb Incorporated all of validator's valid tests into JUnit test suite 2014-08-12 18:27:14 +02:00
moandji.ezana 0bca81e5da Split BurntSushiTest into valid and invalid. 2014-08-12 18:17:15 +02:00
moandji.ezana feba3c7e1a Added documentation to ValueParser 2014-08-12 18:09:11 +02:00
moandji.ezana 86e921cb4b Improved handling of malformed keys.
Incorporated all of the validator's invalid tests.
2014-08-12 18:08:37 +02:00
moandji.ezana bc4ffcac47 Merge branch 'master' into wip 2014-08-12 17:42:14 +02:00
moandji.ezana eeb9e7734a Added changelog 2014-08-12 17:41:45 +02:00
moandji.ezana bc6fa8e736 Moved all classes to single package to reduce visibility.
Only Toml class is public.
2014-08-12 17:38:23 +02:00
moandji.ezana 0a4ac0d875 ValueAnalysis renamed to ValueConverter 2014-08-12 17:30:44 +02:00
moandji.ezana e2a085d310 Improved handling of invalid integers.
Type parsers broken out into separate package
2014-08-12 17:29:56 +02:00
moandji.ezana 010bc88a20 Improved invalid boolean handling.
Introducing new ValueParser system
2014-08-12 16:44:47 +02:00
moandji.ezana 5253a80748 Renamed parsers 2014-08-12 16:17:12 +02:00
moandji.ezana bdfe33382a Merge branch 'regex_parser' into wip 2014-08-12 16:04:59 +02:00
moandji.ezana adbfeae05e Improved handling of invalid strings and array entries 2014-08-12 15:59:53 +02:00
moandji.ezana 155781b5e2 Improved float handling 2014-08-12 15:42:18 +02:00
moandji.ezana 922408e979 Improved handling of invalid key names 2014-08-12 13:15:12 +02:00
moandji.ezana 6b67baf3bf Improved handling of nested arrays 2014-08-12 13:02:46 +02:00
moandji.ezana 01f2ff7d76 Improved handling of arrays, tables and table arrays 2014-08-12 12:53:18 +02:00
moandji.ezana 39e828ff10 Handle nested arrays 2014-08-11 00:51:21 +02:00
moandji.ezana 3ac6916678 Merge branch 'master' into wip 2014-08-09 01:20:00 +02:00
moandji.ezana 946416be97 Mention wip branch in README 2014-08-09 01:17:46 +02:00
moandji.ezana 329b56e7d3 Improved comment handling 2014-08-09 01:10:30 +02:00
moandji.ezana 055480ee0b Initial work to implement regex-based parser 2014-08-05 22:18:04 +02:00
moandji.ezana 14d424e573 Added failing test on invalid number 2014-08-03 14:24:31 +02:00
moandji.ezana 60cbf7ea36 Added datetime-malformed-no-leads test 2014-08-03 14:23:46 +02:00
moandji.ezana 59a408314f Changed "key group" to "table" in error message 2014-08-03 14:22:04 +02:00
moandji.ezana 96016c02d2 Improved comment handling and disallowed mixed arrays 2014-07-22 15:56:07 +02:00
moandji.ezana d9e1ccc98a Added Toml#to(Class, Gson) to allow for test output as specified by
BurntSushi/toml-test
2014-07-22 15:50:39 +02:00
moandji.ezana e36b8dbe8e Read empty string followed by new line.
Fix #3 https://github.com/mwanji/toml4j/issues/3
2014-07-21 13:07:36 +02:00
Paul Koerbitz 14ec051e22 Add 'EmptyStringTest.java' which shows problem with empty strings 2014-06-30 18:29:04 +02:00
moandji.ezana 9e075203e4 [maven-release-plugin] prepare for next development iteration 2014-04-10 15:34:13 +02:00
moandji.ezana de956a842f [maven-release-plugin] prepare release toml4j-0.2.0 2014-04-10 15:34:01 +02:00
moandji.ezana 9eef0d02d1 Prepared README for 0.2.0 2014-04-10 15:21:40 +02:00
moandji.ezana 1e97e7602b Improved test coverage 2014-04-10 15:07:45 +02:00
moandji.ezana 6193e68a8f Restored Coveralls to Travis CI 2014-04-10 15:07:28 +02:00
moandji.ezana 90df434b5b Removed coveralls from Travis build to see if it works again 2014-04-09 20:58:08 +02:00
moandji.ezana 14a0aa68ac Added code coverage via JaCoCo and Coveralls 2014-04-09 20:41:17 +02:00
moandji.ezana 6329f9cd62 Tested that TOML array can be converted to Java array 2014-04-09 15:08:57 +02:00
moandji.ezana 35c76d85a0 Tested table array conversion to Java class 2014-04-09 15:03:44 +02:00
moandji.ezana 0af30b90ac Indexed navigation in compound keys and shallow merging of defaults 2014-04-09 15:02:41 +02:00
moandji.ezana 2f355b47f3 Tested and documented extra possibilities of conversion to Java 2014-04-09 11:04:28 +02:00
moandji.ezana b0a8de8b19 Added Toml#parse(Reader) and Toml#parse(InputStream) 2014-04-08 16:37:02 +02:00
moandji.ezana 86257278b7 Added javadoc. Better null-handling and return empty containers instead
of null.
2014-04-08 12:52:56 +02:00
moandji.ezana 1773bd5d1e Added LICENSE file
[ci skip]
2014-04-08 12:29:19 +02:00
moandji.ezana 8b8722596b Improved error message formatting
[ci skip]
2014-04-08 11:27:44 +02:00
moandji.ezana a8e1acdba7 Added UTF-8 test. 2014-04-07 15:42:49 +02:00
moandji.ezana ecf83ff8a3 Improved documentation
[ci skip]
2014-04-07 15:41:14 +02:00
moandji.ezana f7f3d37ef0 Added travis-ci badge to README
[ci skip]
2014-04-07 14:54:48 +02:00
moandji.ezana 8810f558df Added travis-ci config 2014-04-07 14:49:01 +02:00
moandji.ezana 453ce84fc5 Support for nested table arrays 2014-04-07 14:38:34 +02:00
moandji.ezana 095e33a03b Added support for simple table array 2014-04-07 13:17:56 +02:00
moandji.ezana 5c3c868682 Changed key group to table in tests 2014-04-06 22:55:56 +02:00
moandji.ezana bc78ff64b5 Replaced "key group" by "table" 2014-04-06 22:25:21 +02:00
moandji.ezana 66a18af38a Restored proper URL 2014-04-06 22:24:54 +02:00
moandji.ezana 03d022cb5b [maven-release-plugin] prepare for next development iteration 2014-04-06 21:57:49 +02:00
61 changed files with 3909 additions and 1180 deletions

18
.github/workflows/gradle.yml vendored Normal file
View file

@ -0,0 +1,18 @@
name: Gradle
on: [ push ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
distribution: temurin
java-version: 17
cache: gradle
- name: Build with Gradle
run: chmod a+x gradlew && ./gradlew build --no-daemon

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/.gradle/
/build/
/.idea/

94
CHANGELOG.md Normal file
View file

@ -0,0 +1,94 @@
# toml4j Changelog
## 0.7.3 / 2022-05-05
## Updated
* Added SeriaizedName annotation support
* Add proper indentation for array types
* Append object fields instead of overwriting the whole file
## 0.7.2 / 2017-08-05
## Updated
* [Update Gson to 2.8.1 and various Maven plugins to latest versions](https://github.com/mwanji/toml4j/pull/45) (thanks to __[DanilaFe](https://github.com/DanilaFe)__)
## Fixed
* [tomlWriter.write NullPointerException in JDK9](https://github.com/mwanji/toml4j/issues/46) (thanks to __[iwangxiaodong](https://github.com/iwangxiaodong)__)
* Change build to be able to release a new version entirely from the command line
## 0.7.1 / 2016-07-27
* [Support literal strings in table names](https://github.com/mwanji/toml4j/issues/36) (thanks to __[bruno-medeiros](https://github.com/bruno-medeiros)__)
## 0.7.0 / 2016-07-12
## Added
* Toml#read(Toml) merges two Toml instances (thanks to __[gustavkarlsson](https://github.com/gustavkarlsson)__)
## 0.6.0 / 2016-06-14
## Added
* Toml#toMap() convenience method (thanks to __[andytill](https://github.com/andytill)__ and __[Gyscos](https://github.com/Gyscos))
## Fixed
* Transient fields are not written to TOML files (thanks to __[lare96](https://github.com/lare96)__)
* Support positive timezone offset in datetime (thanks to __[aloyse](https://github.com/aloyse)__)
## 0.5.1 / 2016-01-24
### Fixed
* [Handling of tables with same name in different table array items](https://github.com/mwanji/toml4j/issues/26) (thanks to __[stanofujdiar](https://github.com/stanofujdiar)__)
## 0.5.0 / 2015-12-10
### Changed
* __BREAKING:__ Toml#parse methods renamed to read
* __BREAKING:__ Toml#getList(String), Toml#getTable(String) and Toml#getTables(String) return null when key is not found
* Removed trailing newline from error messages (thanks to __[Zero3](https://github.com/Zero3)__)
* Toml#read(File) forces encoding to UTF-8 (thanks to __[Bruno Medeiros](https://github.com/bruno-medeiros)__)
### Added
* Support for writing objects to TOML with TomlWriter (thanks to __[dilecti](https://github.com/dilecti)__)
* Support for underscores in numbers (the feature branch had accidentally not been merged into 0.4.0! :( )
* Set<Map.Entry> Toml#entrySet() cf. Reflection section in README (thanks __[Zero3](https://github.com/Zero3)__ and __[d3xter](https://github.com/d3xter)__)
* Overloaded getters that take a default value (thanks to __[udiabon](https://github.com/udiabon)__)
* Toml#contains(String) and Toml#containsXXX(String) methods to check for existence of keys
## 0.4.0 / 2015-02-16
### Changed
* __BREAKING:__ Toml#getList(String) replaced Toml#getList(String, Class)
* Dropped dependency on Parboiled and its significant transitive dependencies
### Added
* Support for [TOML 0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
* Toml#isEmpty()
* More detailed error messages, including line numbers
### Fixed
* Short-form Unicode escape handling
* Exponent handling
## 0.3.1 / 2014-12-16
* Support for [TOML 0.3.1](https://github.com/toml-lang/toml/tree/v0.3.1) spec
* Pass TOML validator (https://github.com/BurntSushi/toml-test), which uncovered many bugs.
* Reduced visibility of internal classes, so that only Toml class is visible to users.
* Refactored parsing into several steps.
## 0.2 / 2014-04-10
* Support for [TOML 0.2](https://github.com/toml-lang/toml/tree/v0.2.0) spec, most notably table arrays.
## 0.1 / 2014-04-06
* Support for [TOML 0.1](https://github.com/toml-lang/toml/tree/v0.1.0) spec.

8
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,8 @@
Thank you for taking the time to contribute to toml4j! Here are a few guidelines to streamline the process.
* Pull Requests should be opened against the wip branch. Master changes only when there is a new release.
* Amend README.md as necessary
* Use 2 spaces for indentation
* Opening braces, parentheses, etc. are not on a new line
If you are unsure about how something should be implemented, open a pull request and we'll discuss it.

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2022 Plexus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

345
README.md
View file

@ -1,91 +1,340 @@
# toml4j
# toml4j [![Build Status](https://ci.plex.us.org/job/toml4j/badge/icon)](https://ci.plex.us.org/job/toml4j/)
toml4j is a [TOML 0.1.0](https://github.com/mojombo/toml) parser for Java that uses the [Parboiled](http://www.parboiled.org) PEG parser.
toml4j is a [TOML 0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md) parser for Java.
## Installation
Add the following dependency to your POM:
Add the following dependency to your POM (or equivalent for other dependency managers):
````xml
```xml
<dependency>
<groupId>com.moandjiezana.toml</groupId>
<groupId>dev.plex</groupId>
<artifactId>toml4j</artifactId>
<version>0.1.0</version>
<version>0.7.3</version>
</dependency>
````
```
If you use Gradle, you can use the following code:
```gradle
repositories {
mavenCentral()
maven {
url = uri("https://nexus.telesphoreo.me/repository/totalfreedom/")
}
}
dependencies {
...
implementation("dev.plex:toml4j:0.7.3")
}
```
Requires Java 17 above.
## Quick start
```java
Toml toml = new Toml().read(getTomlFile());
String someValue = toml.getString("someKey");
Date someDate = toml.getDate("someTable.someDate");
MyClass myClass = toml.to(MyClass.class);
```
## Usage
A `dev.plex.Toml` instance is populated by calling one of `read(File)`, `read(InputStream)`, `read(Reader)`, `read(String)` or `read(Toml)`.
```java
Toml toml = new Toml().read("a=1");
```
An exception is thrown if the source is not valid TOML.
The data can then be accessed either by converting the Toml instance to your own class or by accessing tables and keys by name.
### Maps
`Toml#toMap()` is a quick way to turn a Toml instance into a `Map<String, Object>`.
```java
Map<String, Object> map = new Toml().read("a=1").toMap();
```
### Custom classes
A Toml object can be mapped to a custom class with the `Toml#to(Class<T>)` method.
`Toml#to(Class<T>)` maps a Toml instance to the given class.
Any keys not found in both the TOML and the class are ignored.
Key groups can be mapped to other custom classes. Fields may be private.
````
```toml
name = "Mwanji Ezana"
[address]
street = "123 A Street"
city = "AnyVille"
````
[contacts]
"email address" = "me@example.com"
```
````java
class Address {
String street;
String city;
}
```java
class Address {
String street;
String city;
}
class User {
String name;
Address address;
}
````
class User {
String name;
Address address;
Map<String, Object> contacts;
}
```
````java
Toml toml = new Toml().parse(tomlFile);
User user = toml.to(User.class);
```java
User user = new Toml().read(tomlFile).to(User.class);
assert user.name.equals("Mwanji Ezana");
assert user.address.street.equals("123 A Street");
````
assert user.contacts.get("\"email address\"").equals("me@example.com");
```
When defaults are present, a shallow merge is performed.
Any keys not found in both the TOML and the class are ignored. Fields may be private.
Quoted keys cannot be mapped directly to a Java object, but they can be used as keys within a `Map`.
TOML primitives can be mapped to a number of Java types:
TOML | Java
---- | ----
Integer | `int`, `long` (or wrapper), `java.math.BigInteger`
Float | `float`, `double` (or wrapper), `java.math.BigDecimal`
String | `String`, enum, `java.net.URI`, `java.net.URL`
One-letter String | `char`, `Character`
Multiline and Literal Strings | `String`
Array | `List`, `Set`, array. The generic type can be anything that can be converted.
Table | Custom class, `Map<String, Object>`
Custom classes, Maps and collections thereof can be nested to any level. See [TomlToClassTest#should_convert_fruit_table_array()](src/test/java/com/moandjiezana/toml/TomlToClassTest.java) for an example.
### Key names
1. Create a `com.moandjiezana.toml.Toml` instance
2. Call the `parse` method of your choice
3. Use the getters to retrieve the data
Use the getters to retrieve the data:
````java
Toml toml = new Toml().parse(getTomlFile()); // throws an Exception if the TOML is incorrect
* `getString(String)`
* `getDate(String)`
* `getBoolean(String)`
* `getLong(String)`
* `getDouble(String)`
* `getList(String)`
* `getTable(String)` returns a new Toml instance containing only the keys in that table.
* `getTables(String)`, for table arrays, returns `List<Toml>`.
String title = toml.getString("title"); // if a key doesn't exist, returns null
Boolean enabled = toml.getBoolean("database.enabled"); // gets the value of enabled from the database key group
Toml servers = toml.getKeyGroup("servers"); // returns a new Toml instance containing only the key group's values
````
You can also navigate values within a table with a compound key of the form `table.key`. Use a zero-based index such as `tableArray[0].key` to navigate table arrays.
Non-existent keys return null.
When retrieving quoted keys, the quotes must be used and the key must be spelled exactly the same way, including quotes and whitespace. The only exceptions are Unicode escapes: `"\u00B1" = "value"` would be retrieved with `toml.getString("\"±\"")`.
```toml
title = "TOML Example"
"sub title" = "Now with quoted keys"
[database]
ports = [ 8001, 8001, 8002 ]
enabled = true
[database.credentials]
password = "password"
[servers]
cluster = "hyades"
[servers.alpha]
ip = "10.0.0.1"
[[networks]]
name = "Level 1"
[networks.status]
bandwidth = 10
[[networks]]
name = "Level 2"
[[networks]]
name = "Level 3"
[[networks.operators]]
location = "Geneva"
[[networks.operators]]
location = "Paris"
```
```java
Toml toml = new Toml().read(getTomlFile());
String title = toml.getString("title");
String subTitle = toml.getString("\"sub title\"");
Boolean enabled = toml.getBoolean("database.enabled");
List<Long> ports = toml.getList("database.ports");
String password = toml.getString("database.credentials.password");
Toml servers = toml.getTable("servers");
String cluster = servers.getString("cluster"); // navigation is relative to current Toml instance
String ip = servers.getString("alpha.ip");
Toml network1 = toml.getTable("networks[0]");
String network2Name = toml.getString("networks[1].name"); // "Level 2"
List<Toml> network3Operators = toml.getTables("networks[2].operators");
String network3Operator2Location = toml.getString("networks[2].operators[1].location"); // "Paris"
```
### Defaults
The constructor can be given a set of default values that will be used if necessary.
The constructor can be given a set of default values that will be used as fallbacks. For tables and table arrays, a shallow merge is performed.
````java
Toml defaults = new Toml().parse("a = 2\nb = 3");
Toml toml = new Toml(defaults).parse("a = 1");
`Toml#read(Toml)` is used to merge two Toml instances:
```java
Toml toml1 = new Toml().read("a=1");
Toml toml2 = new Toml().read(getTomlFile());
Toml mergedToml = new Toml(toml1).read(toml2);
```
You can also call an overloaded version of the getters that take a default value. Note that the default value provided in the constructor take precedence over the one provided by the getter.
```toml
# defaults
a = 2
b = 3
[table]
c = 4
d = 5
```
```toml
a = 1
[table]
c = 2
[[array]]
d = 3
```
```java
Toml defaults = new Toml().read(getDefaultsFile());
Toml toml = new Toml(defaults).read(getTomlFile());
Long a = toml.getLong("a"); // returns 1, not 2
Long b = toml.getLong("b"); // returns 3
Long b = toml.getLong("b"); // returns 3, taken from defaults provided to constructor
Long bPrefersConstructor = toml.getLong("b", 5); // returns 3, not 5
Long c = toml.getLong("c"); // returns null
````
Long cWithDefault = toml.getLong("c", 5); // returns 5
Long tableC = toml.getLong("table.c"); // returns 2, not 4
Long tableD = toml.getLong("table.d"); // returns null, not 5, because of shallow merge
Long arrayD = toml.getLong("array[0].d"); // returns 3
```
## TODO
### Reflection
* Fail on invalid definitions
`Toml#entrySet()` returns a Set of [Map.Entry](http://docs.oracle.com/javase/6/docs/api/java/util/Map.Entry.html) instances. Modifications to the returned Set are not reflected in the Toml instance. Note that Map.Entry#setValue() will throw an UnsupportedOperationException.
```java
for (Map.Entry<String, Object> entry : myToml.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
```
`Toml#contains(String)` verifies that the instance contains a key of any type (primitive, table or array of tables) of the given name. `Toml#containsPrimitive(String)`, `Toml#containsTable(String)` and `Toml#containsTableArray(String)` return true only if a key exists and is a primitive, table or array of tables, respectively. Compound keys can be used to check existence at any depth.
```java
Toml toml = new Toml().read("a = 1");
toml.contains("a"); // true
toml.conatinsKey("a"); // true
toml.containsTable("a"); // false
toml.containsTableArray("a"); // false
```
### Converting Objects To TOML
You can write `Map`s and custom objects to a TOML `String`, `File`, `Writer`, or `OutputStream` with a `TomlWriter`. Each TomlWriter instance is customisable, immutable and threadsafe, so it can be reused and passed around. Constants and transient fields are ignored.
To write a `List` of objects as a table array, put the list in a `Map` or in a custom object.
```java
class AClass {
int anInt = 1;
int[] anArray = { 2, 3 };
}
TomlWriter tomlWriter = new TomlWriter();
AClass obj = new AClass();
Map<String, Object> map = new HashMap<String, Object>();
int[] intArray = { 2, 3 };
map.put("anInt", 1);
map.put("anArray", intArray);
String tomlString = tomlWriter.write(obj);
tomlString = tomlWriter.write(map);
tomlWriter.write(obj, new File("path/to/file"));
tomlWriter.write(obj, new ByteArrayOutputStream());
tomlWriter.write(obj, new OutputStreamWriter(anOutputStream));
/*
All methods output:
anInt = 1
anArray = [2, 3]
*/
```
You can customise formatting with a TomlWriter.Builder:
```java
class BClass {
Map<String, ?> aMap = new HashMap<String, Object>();
}
BClass obj = new BClass();
obj.aMap.put("item", 1);
TomlWriter tomlWriter = new TomlWriter.Builder()
.indentValuesBy(2)
.indentTablesBy(4)
.padArrayDelimitersBy(3)
.build();
String tomlString = tomlWriter.write(obj);
/*
Output:
[aMap]
item = 1
[aMap.a]
anInt = 1
anArray = [ 2, 3 ]
*/
```
### Threadsafety
`TomlWriter` is threadsafe, however the JDK's streams and `Writer` are not. Take care not to write to the same stream in parallel.
### Limitations
Date precision is limited to milliseconds.
## Changelog
Please see the [changelog](CHANGELOG.md).
## Contributing
Please see the [contribution guidelines](CONTRIBUTING.md).
## License
toml4j is copyright of Moandji Ezana and is licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php)
toml4j is copyright (c) 2013-2015 Moandji Ezana and is licensed under the [MIT License](LICENSE)

37
build.gradle.kts Normal file
View file

@ -0,0 +1,37 @@
plugins {
java
`maven-publish`
}
group = "dev.plex"
version = "0.7.3"
repositories {
mavenCentral()
}
dependencies {
implementation("com.google.code.gson:gson:2.9.0")
implementation("org.projectlombok:lombok:1.18.24")
implementation("org.jetbrains:annotations:23.0.0")
annotationProcessor("org.projectlombok:lombok:1.18.24")
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
repositories {
maven {
val releasesRepoUrl = uri("https://nexus.telesphoreo.me/repository/plex-releases")
val snapshotsRepoUrl = uri("https://nexus.telesphoreo.me/repository/plex-snapshots")
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
credentials {
username = System.getenv("plexUser")
password = System.getenv("plexPassword")
}
}
}
}
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
gradlew vendored Normal file
View file

@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

89
gradlew.bat vendored Normal file
View file

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

102
pom.xml
View file

@ -1,102 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
<groupId>com.moandjiezana.toml</groupId>
<artifactId>toml4j</artifactId>
<version>0.1.0</version>
<name>toml4j</name>
<description>A parser for TOML</description>
<url>http://moandjiezana.com/toml/</url>
<developers>
<developer>
<id>moandji.ezana</id>
<name>Moandji Ezana</name>
<email>mwanji@gmail.com</email>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.parboiled</groupId>
<artifactId>parboiled-java</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<licenses>
<license>
<name>The MIT License</name>
<url>http://www.opensource.org/licenses/mit-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<connection>scm:git:git@github.com:mwanji/toml4j.git</connection>
<developerConnection>scm:git:git@github.com:mwanji/toml4j.git</developerConnection>
<url>https://github.com/mwanji/toml4j</url>
</scm>
<issueManagement>
<system>Github Issues</system>
<url>https://github.com/mwanji/toml4j/issues</url>
</issueManagement>
<profiles>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

2
settings.gradle.kts Normal file
View file

@ -0,0 +1,2 @@
rootProject.name = "toml4j"

View file

@ -1,135 +0,0 @@
package com.moandjiezana.toml;
import com.google.gson.Gson;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import org.parboiled.Parboiled;
import org.parboiled.parserunners.RecoveringParseRunner;
import org.parboiled.support.ParsingResult;
/**
*
* All getters can fall back to default values if they have been provided and will return null if no matching key exists.
*
*/
public class Toml {
private Map<String, Object> values = new HashMap<String, Object>();
private final Toml defaults;
public Toml() {
this((Toml) null);
}
public Toml(Toml defaults) {
this.defaults = defaults;
}
public Toml parse(File file) {
try {
return parse(new Scanner(file).useDelimiter("\\Z").next());
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
public Toml parse(String tomlString) {
TomlParser parser = Parboiled.createParser(TomlParser.class);
ParsingResult<Object> result = new RecoveringParseRunner<Object>(parser.Toml()).run(tomlString);
// ParsingResult<Object> parsingResult = new ReportingParseRunner<Object>(parser.Toml()).run(tomlString);
// System.out.println(ParseTreeUtils.printNodeTree(parsingResult));
TomlParser.Results results = (TomlParser.Results) result.valueStack.peek(result.valueStack.size() - 1);
if (results.errors.length() > 0) {
throw new IllegalStateException(results.errors.toString());
}
this.values = results.values;
return this;
}
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) {
return (List<T>) get(key);
}
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);
}
@SuppressWarnings("unchecked")
public Toml getKeyGroup(String key) {
return new Toml((Map<String, Object>) get(key));
}
@SuppressWarnings("unchecked")
private Object get(String key) {
String[] split = key.split("\\.");
Object current = new HashMap<String, Object>(values);
Object currentDefaults = defaults != null ? defaults.values : null;
for (String splitKey : split) {
current = ((Map<String, Object>) current).get(splitKey);
if (currentDefaults != null) {
currentDefaults = ((Map<String, Object>) currentDefaults).get(splitKey);
if (current instanceof Map && currentDefaults instanceof Map) {
for (Map.Entry<String, Object> entry : ((Map<String, Object>) currentDefaults).entrySet()) {
if (!((Map<String, Object>) current).containsKey(entry.getKey())) {
((Map<String, Object>) current).put(entry.getKey(), entry.getValue());
}
}
}
}
if (current == null && currentDefaults != null) {
current = currentDefaults;
}
if (current == null) {
return null;
}
}
return current;
}
private Toml(Map<String, Object> values) {
this.values = values;
this.defaults = null;
}
public <T> T to(Class<T> targetClass) {
HashMap<String, Object> valuesCopy = new HashMap<String, Object>(values);
if (defaults != null) {
for (Map.Entry<String, Object> entry : defaults.values.entrySet()) {
if (!valuesCopy.containsKey(entry.getKey())) {
valuesCopy.put(entry.getKey(), entry.getValue());
}
}
}
Gson gson = new Gson();
String json = gson.toJson(valuesCopy);
return gson.fromJson(json, targetClass);
}
}

View file

@ -1,288 +0,0 @@
package com.moandjiezana.toml;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.parboiled.BaseParser;
import org.parboiled.Rule;
import org.parboiled.annotations.BuildParseTree;
import org.parboiled.annotations.SuppressNode;
@BuildParseTree
class TomlParser extends BaseParser<Object> {
static class Results {
public Map<String, Object> values = new HashMap<String, Object>();
public Set<String> keyGroups = new HashSet<String>();
public StringBuilder errors = new StringBuilder();
}
public Rule Toml() {
return Sequence(push(new TomlParser.Results()), push(((TomlParser.Results) peek()).values), OneOrMore(FirstOf(KeyGroup(), '\n', Comment(), Key())));
}
Rule KeyGroup() {
return Sequence(Sequence(KeyGroupDelimiter(), KeyGroupName(), addKeyGroup((String) pop()), KeyGroupDelimiter(), Spacing()), checkKeyGroup(match()));
}
boolean checkKeyGroup(String definition) {
String afterBracket = definition.substring(definition.indexOf(']') + 1);
for (char character : afterBracket.toCharArray()) {
if (character == '#') {
return true;
}
if (!Character.isWhitespace(character)) {
results().errors.append("Invalid key group definition: ").append(definition).append(". You may have forgotten a #");
}
}
return true;
}
Rule Key() {
return Sequence(Spacing(), KeyName(), EqualsSign(), VariableValues(), Spacing(), swap(), addKey((String) pop(), pop()));
}
Rule KeyGroupName() {
return Sequence(OneOrMore(TestNot(KeyGroupDelimiter()), FirstOf(Letter(), Digit(), ANY)), push(match()));
}
Rule KeyName() {
return Sequence(OneOrMore(TestNot(EqualsSign()), ANY), push(match()));
}
Rule VariableValues() {
return FirstOf(ArrayValue(), DateValue(), BooleanValue(), NumberValue(), StringValue());
}
Rule ArrayValue() {
return Sequence(push(ArrayList.class), '[', Spacing(), ZeroOrMore(VariableValues(), Optional(ArrayDelimiter())), Spacing(), ']', pushList());
}
Rule DateValue() {
return Sequence(Sequence(Year(), '-', Month(), '-', Day(), 'T', Digit(), Digit(), ':', Digit(), Digit(), ':', Digit(), Digit(), 'Z'), pushDate(match()));
}
Rule BooleanValue() {
return Sequence(FirstOf("true", "false"), push(Boolean.valueOf(match())));
}
Rule NumberValue() {
return Sequence(Sequence(Optional('-'), OneOrMore(FirstOf(Digit(), '.'))), pushNumber(match()));
}
Rule StringValue() {
return Sequence(push(new StringBuilder()), '"', OneOrMore(TestNot('"'), FirstOf(UnicodeCharacter(), SpecialCharacter(), AnyCharacter())), pushString(((StringBuilder) pop()).toString()), '"');
}
Rule Year() {
return Sequence(Digit(), Digit(), Digit(), Digit());
}
Rule Month() {
return Sequence(CharRange('0', '1'), Digit());
}
Rule Day() {
return Sequence(CharRange('0', '3'), Digit());
}
Rule Digit() {
return CharRange('0', '9');
}
Rule Letter() {
return CharRange('a', 'z');
}
Rule UnicodeCharacter() {
return Sequence(Sequence('\\', 'u', OneOrMore(FirstOf(CharRange('0', '9'), CharRange('A', 'F')))), pushCharacter(match()));
}
Rule SpecialCharacter() {
return Sequence(Sequence('\\', ANY), pushCharacter(match()));
}
Rule AnyCharacter() {
return Sequence(ANY, pushCharacter(match()));
}
@SuppressNode
Rule KeyGroupDelimiter() {
return AnyOf("[]");
}
@SuppressNode
Rule EqualsSign() {
return Sequence(Spacing(), '=', Spacing());
}
@SuppressNode
Rule Spacing() {
return ZeroOrMore(FirstOf(Comment(), Whitespace(), NewLine(), AnyOf("\f")));
}
Rule IllegalCharacters() {
return Sequence(ZeroOrMore(Whitespace()), OneOrMore(TestNot('#', NewLine()), ANY));
//return Sequence(ZeroOrMore(Whitespace()), TestNot('#', NewLine()), OneOrMore(ANY));
}
@SuppressNode
Rule Whitespace() {
return AnyOf(" \t");
}
@SuppressNode
Rule NewLine() {
return AnyOf("\r\n");
}
@SuppressNode
Rule ArrayDelimiter() {
return Sequence(Spacing(), ',', Spacing());
}
@SuppressNode
Rule Comment() {
return Sequence('#', ZeroOrMore(TestNot(NewLine()), ANY), FirstOf(NewLine(), EOI));
}
@SuppressWarnings("unchecked")
boolean addKeyGroup(String name) {
String[] split = name.split("\\.");
while (getContext().getValueStack().size() > 2) {
drop();
}
Map<String, Object> newKeyGroup = (Map<String, Object>) getContext().getValueStack().peek();
if (!results().keyGroups.add(name)) {
results().errors.append("Could not create key group ").append(name).append(": key group already exists!\n");
return true;
}
for (String splitKey : split) {
if (!newKeyGroup.containsKey(splitKey)) {
newKeyGroup.put(splitKey, new HashMap<String, Object>());
}
Object currentValue = newKeyGroup.get(splitKey);
if (!(currentValue instanceof Map)) {
results().errors.append("Could not create key group ").append(name).append(": key already has a value!\n");
return true;
}
newKeyGroup = (Map<String, Object>) currentValue;
}
push(newKeyGroup);
return true;
}
boolean addKey(String key, Object value) {
if (key.contains(".")) {
results().errors.append(key).append(" is invalid: key names may not contain a dot!\n");
return true;
}
putValue(key, value);
return true;
}
boolean pushList() {
ArrayList<Object> list = new ArrayList<Object>();
while (peek() != ArrayList.class) {
list.add(0, pop());
}
poke(list);
return true;
}
boolean pushDate(String dateString) {
String s = dateString.replace("Z", "+00:00");
try {
s = s.substring(0, 22) + s.substring(23);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
dateFormat.setLenient(false);
Date date = dateFormat.parse(s);
push(date);
return true;
} catch (Exception e) {
results().errors.append("Invalid date: ").append(dateString).append("\n");
return false;
}
}
boolean pushNumber(String number) {
if (number.contains(".")) {
push(Double.valueOf(number));
} else {
push(Long.valueOf(number));
}
return true;
}
boolean pushString(String line) {
StringBuilder builder = new StringBuilder();
String[] split = line.split("\\\\n");
for (String string : split) {
builder.append(string).append('\n');
}
builder.deleteCharAt(builder.length() - 1);
push(builder.toString());
return true;
}
boolean pushCharacter(String sc) {
StringBuilder sb = (StringBuilder) peek();
if (sc.equals("\\n")) {
sb.append('\n');
} else if (sc.equals("\\\"")) {
sb.append('\"');
} else if (sc.equals("\\t")) {
sb.append('\t');
} else if (sc.equals("\\r")) {
sb.append('\r');
} else if (sc.equals("\\\\")) {
sb.append('\\');
} else if (sc.equals("\\/")) {
sb.append('/');
} else if (sc.equals("\\b")) {
sb.append('\b');
} else if (sc.equals("\\f")) {
sb.append('\f');
} else if (sc.startsWith("\\u")) {
sb.append(Character.toChars(Integer.parseInt(sc.substring(2), 16)));
} else if (sc.startsWith("\\")) {
results().errors.append(sc + " is a reserved special character and cannot be used!\n");
} else {
sb.append(sc);
}
return true;
}
@SuppressWarnings("unchecked")
void putValue(String name, Object value) {
Map<String, Object> values = (Map<String, Object>) peek();
if (values.containsKey(name)) {
results().errors.append("Key ").append(name).append(" already exists!\n");
return;
}
values.put(name, value);
}
TomlParser.Results results() {
return (Results) peek(getContext().getValueStack().size() - 1);
}
}

View file

@ -0,0 +1,77 @@
package dev.plex.toml;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class ArrayValueReader implements ValueReader {
public static final ArrayValueReader ARRAY_VALUE_READER = new ArrayValueReader();
@Override
public boolean canRead(String s) {
return s.startsWith("[");
}
@Override
public Object read(String s, AtomicInteger index, dev.plex.toml.Context context) {
AtomicInteger line = context.line;
int startLine = line.get();
int startIndex = index.get();
List<Object> arrayItems = new ArrayList<Object>();
boolean terminated = false;
boolean inComment = false;
dev.plex.toml.Results.Errors errors = new dev.plex.toml.Results.Errors();
for (int i = index.incrementAndGet(); i < s.length(); i = index.incrementAndGet()) {
char c = s.charAt(i);
if (c == '#' && !inComment) {
inComment = true;
} else if (c == '\n') {
inComment = false;
line.incrementAndGet();
} else if (inComment || Character.isWhitespace(c) || c == ',') {
continue;
} else if (c == '[') {
Object converted = read(s, index, context);
if (converted instanceof dev.plex.toml.Results.Errors) {
errors.add((dev.plex.toml.Results.Errors) converted);
} else if (!isHomogenousArray(converted, arrayItems)) {
errors.heterogenous(context.identifier.getName(), line.get());
} else {
arrayItems.add(converted);
}
continue;
} else if (c == ']') {
terminated = true;
break;
} else {
Object converted = ValueReaders.VALUE_READERS.convert(s, index, context);
if (converted instanceof dev.plex.toml.Results.Errors) {
errors.add((dev.plex.toml.Results.Errors) converted);
} else if (!isHomogenousArray(converted, arrayItems)) {
errors.heterogenous(context.identifier.getName(), line.get());
} else {
arrayItems.add(converted);
}
}
}
if (!terminated) {
errors.unterminated(context.identifier.getName(), s.substring(startIndex, s.length()), startLine);
}
if (errors.hasErrors()) {
return errors;
}
return arrayItems;
}
private boolean isHomogenousArray(Object o, List<?> values) {
return values.isEmpty() || values.get(0).getClass().isAssignableFrom(o.getClass()) || o.getClass().isAssignableFrom(values.get(0).getClass());
}
private ArrayValueReader() {}
}

View file

@ -0,0 +1,66 @@
package dev.plex.toml;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import static dev.plex.toml.ValueWriters.WRITERS;
public abstract class ArrayValueWriter implements dev.plex.toml.ValueWriter
{
static protected boolean isArrayish(Object value) {
return value instanceof Collection || value.getClass().isArray();
}
@Override
public boolean isPrimitiveType() {
return false;
}
static boolean isArrayOfPrimitive(Object array) {
Object first = peek(array);
if (first != null) {
dev.plex.toml.ValueWriter valueWriter = WRITERS.findWriterFor(first);
return valueWriter.isPrimitiveType() || isArrayish(first);
}
return true;
}
@SuppressWarnings("unchecked")
protected Collection<?> normalize(Object value) {
Collection<Object> collection;
if (value.getClass().isArray()) {
// Arrays.asList() interprets an array as a single element,
// so convert it to a list by hand
collection = new ArrayList<Object>(Array.getLength(value));
for (int i = 0; i < Array.getLength(value); i++) {
Object elem = Array.get(value, i);
collection.add(elem);
}
} else {
collection = (Collection<Object>) value;
}
return collection;
}
private static Object peek(Object value) {
if (value.getClass().isArray()) {
if (Array.getLength(value) > 0) {
return Array.get(value, 0);
} else {
return null;
}
} else {
Collection<?> collection = (Collection<?>) value;
if (collection.size() > 0) {
return collection.iterator().next();
}
}
return null;
}
}

View file

@ -0,0 +1,48 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
class BooleanValueReaderWriter implements ValueReader, ValueWriter {
static final BooleanValueReaderWriter BOOLEAN_VALUE_READER_WRITER = new BooleanValueReaderWriter();
@Override
public boolean canRead(String s) {
return s.startsWith("true") || s.startsWith("false");
}
@Override
public Object read(String s, AtomicInteger index, Context context) {
s = s.substring(index.get());
Boolean b = s.startsWith("true") ? Boolean.TRUE : Boolean.FALSE;
int endIndex = b == Boolean.TRUE ? 4 : 5;
index.addAndGet(endIndex - 1);
return b;
}
@Override
public boolean canWrite(Object value) {
return Boolean.class.isInstance(value);
}
@Override
public void write(Object value, WriterContext context) {
context.write(value.toString());
}
@Override
public boolean isPrimitiveType() {
return true;
}
private BooleanValueReaderWriter() {}
@Override
public String toString() {
return "boolean";
}
}

View file

@ -0,0 +1,122 @@
package dev.plex.toml;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class Container {
abstract boolean accepts(String key);
abstract void put(String key, Object value);
abstract Object get(String key);
abstract boolean isImplicit();
static class Table extends Container {
private final Map<String, Object> values = new HashMap<String, Object>();
final String name;
final boolean implicit;
Table() {
this(null, false);
}
public Table(String name) {
this(name, false);
}
public Table(String tableName, boolean implicit) {
this.name = tableName;
this.implicit = implicit;
}
@Override
boolean accepts(String key) {
return !values.containsKey(key) || values.get(key) instanceof TableArray;
}
@Override
void put(String key, Object value) {
values.put(key, value);
}
@Override
Object get(String key) {
return values.get(key);
}
boolean isImplicit() {
return implicit;
}
/**
* This modifies the Table's internal data structure, such that it is no longer usable.
*
* Therefore, this method must only be called when all data has been gathered.
* @return A Map-and-List-based of the TOML data
*/
Map<String, Object> consume() {
for (Map.Entry<String, Object> entry : values.entrySet()) {
if (entry.getValue() instanceof Table) {
entry.setValue(((Table) entry.getValue()).consume());
} else if (entry.getValue() instanceof TableArray) {
entry.setValue(((TableArray) entry.getValue()).getValues());
}
}
return values;
}
@Override
public String toString() {
return values.toString();
}
}
static class TableArray extends Container {
private final List<Table> values = new ArrayList<Table>();
TableArray() {
values.add(new Table());
}
@Override
boolean accepts(String key) {
return getCurrent().accepts(key);
}
@Override
void put(String key, Object value) {
values.add((Table) value);
}
@Override
Object get(String key) {
throw new UnsupportedOperationException();
}
boolean isImplicit() {
return false;
}
List<Map<String, Object>> getValues() {
ArrayList<Map<String, Object>> unwrappedValues = new ArrayList<Map<String,Object>>();
for (Table table : values) {
unwrappedValues.add(table.consume());
}
return unwrappedValues;
}
Table getCurrent() {
return values.get(values.size() - 1);
}
@Override
public String toString() {
return values.toString();
}
}
private Container() {}
}

View file

@ -0,0 +1,19 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
public class Context {
final dev.plex.toml.Identifier identifier;
final AtomicInteger line;
final Results.Errors errors;
public Context(dev.plex.toml.Identifier identifier, AtomicInteger line, Results.Errors errors) {
this.identifier = identifier;
this.line = line;
this.errors = errors;
}
public Context with(dev.plex.toml.Identifier identifier) {
return new Context(identifier, line, errors);
}
}

View file

@ -0,0 +1,22 @@
package dev.plex.toml;
import java.util.TimeZone;
public class DatePolicy {
private final TimeZone timeZone;
private final boolean showFractionalSeconds;
DatePolicy(TimeZone timeZone, boolean showFractionalSeconds) {
this.timeZone = timeZone;
this.showFractionalSeconds = showFractionalSeconds;
}
TimeZone getTimeZone() {
return timeZone;
}
boolean isShowFractionalSeconds() {
return showFractionalSeconds;
}
}

View file

@ -0,0 +1,160 @@
package dev.plex.toml;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DateValueReaderWriter implements ValueReader, ValueWriter {
static final DateValueReaderWriter DATE_VALUE_READER_WRITER = new DateValueReaderWriter();
static final DateValueReaderWriter DATE_PARSER_JDK_6 = new DateConverterJdk6();
private static final Pattern DATE_REGEX = Pattern.compile("(\\d{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9])(\\.\\d*)?(Z|(?:[+\\-]\\d{2}:\\d{2}))(.*)");
@Override
public boolean canRead(String s) {
if (s.length() < 5) {
return false;
}
for (int i = 0; i < 5; i++) {
char c = s.charAt(i);
if (i < 4) {
if (!Character.isDigit(c)) {
return false;
}
} else if (c != '-') {
return false;
}
}
return true;
}
@Override
public Object read(String original, AtomicInteger index, Context context) {
StringBuilder sb = new StringBuilder();
for (int i = index.get(); i < original.length(); i = index.incrementAndGet()) {
char c = original.charAt(i);
if (Character.isDigit(c) || c == '-' || c == '+' || c == ':' || c == '.' || c == 'T' || c == 'Z') {
sb.append(c);
} else {
index.decrementAndGet();
break;
}
}
String s = sb.toString();
Matcher matcher = DATE_REGEX.matcher(s);
if (!matcher.matches()) {
Results.Errors errors = new Results.Errors();
errors.invalidValue(context.identifier.getName(), s, context.line.get());
return errors;
}
String dateString = matcher.group(1);
String zone = matcher.group(3);
String fractionalSeconds = matcher.group(2);
String format = "yyyy-MM-dd'T'HH:mm:ss";
if (fractionalSeconds != null && !fractionalSeconds.isEmpty()) {
format += ".SSS";
dateString += fractionalSeconds;
}
format += "Z";
if ("Z".equals(zone)) {
dateString += "+0000";
} else if (zone.contains(":")) {
dateString += zone.replace(":", "");
}
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
dateFormat.setLenient(false);
return dateFormat.parse(dateString);
} catch (Exception e) {
Results.Errors errors = new Results.Errors();
errors.invalidValue(context.identifier.getName(), s, context.line.get());
return errors;
}
}
@Override
public boolean canWrite(Object value) {
return value instanceof Date;
}
@Override
public void write(Object value, WriterContext context) {
DateFormat formatter = getFormatter(context.getDatePolicy());
context.write(formatter.format(value));
}
@Override
public boolean isPrimitiveType() {
return true;
}
private DateFormat getFormatter(DatePolicy datePolicy) {
boolean utc = "UTC".equals(datePolicy.getTimeZone().getID());
String format;
if (utc && datePolicy.isShowFractionalSeconds()) {
format = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
} else if (utc) {
format = "yyyy-MM-dd'T'HH:mm:ss'Z'";
} else if (datePolicy.isShowFractionalSeconds()) {
format = getTimeZoneAndFractionalSecondsFormat();
} else {
format = getTimeZoneFormat();
}
SimpleDateFormat formatter = new SimpleDateFormat(format);
formatter.setTimeZone(datePolicy.getTimeZone());
return formatter;
}
String getTimeZoneFormat() {
return "yyyy-MM-dd'T'HH:mm:ssXXX";
}
String getTimeZoneAndFractionalSecondsFormat() {
return "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
}
private DateValueReaderWriter() {}
private static class DateConverterJdk6 extends DateValueReaderWriter {
@Override
public void write(Object value, WriterContext context) {
DateFormat formatter = super.getFormatter(context.getDatePolicy());
String date = formatter.format(value);
if ("UTC".equals(context.getDatePolicy().getTimeZone().getID())) {
context.write(date);
} else {
int insertionIndex = date.length() - 2;
context.write(date.substring(0, insertionIndex)).write(':').write(date.substring(insertionIndex));
}
}
@Override
String getTimeZoneAndFractionalSecondsFormat() {
return "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
}
@Override
String getTimeZoneFormat() {
return "yyyy-MM-dd'T'HH:mm:ssZ";
}
}
@Override
public String toString() {
return "datetime";
}
}

View file

@ -0,0 +1,331 @@
package dev.plex.toml;
public class Identifier
{
static final Identifier INVALID = new Identifier("", null);
private static final String ALLOWED_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_-";
private final String name;
private final Type type;
static Identifier from(String name, Context context)
{
Type type;
boolean valid;
name = name.trim();
if (name.startsWith("[["))
{
type = Type.TABLE_ARRAY;
valid = isValidTableArray(name, context);
} else if (name.startsWith("["))
{
type = Type.TABLE;
valid = isValidTable(name, context);
} else
{
type = Type.KEY;
valid = isValidKey(name, context);
}
if (!valid)
{
return Identifier.INVALID;
}
return new Identifier(extractName(name), type);
}
private Identifier(String name, Type type)
{
this.name = name;
this.type = type;
}
String getName()
{
return name;
}
String getBareName()
{
if (isKey())
{
return name;
}
if (isTable())
{
return name.substring(1, name.length() - 1);
}
return name.substring(2, name.length() - 2);
}
boolean isKey()
{
return type == Type.KEY;
}
boolean isTable()
{
return type == Type.TABLE;
}
boolean isTableArray()
{
return type == Type.TABLE_ARRAY;
}
private static enum Type
{
KEY, TABLE, TABLE_ARRAY;
}
private static String extractName(String raw)
{
boolean quoted = false;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < raw.length(); i++)
{
char c = raw.charAt(i);
if (c == '"' && (i == 0 || raw.charAt(i - 1) != '\\'))
{
quoted = !quoted;
sb.append('"');
} else if (quoted || !Character.isWhitespace(c))
{
sb.append(c);
}
}
return StringValueReaderWriter.STRING_VALUE_READER_WRITER.replaceUnicodeCharacters(sb.toString());
}
private static boolean isValidKey(String name, Context context)
{
if (name.trim().isEmpty())
{
context.errors.invalidKey(name, context.line.get());
return false;
}
boolean quoted = false;
for (int i = 0; i < name.length(); i++)
{
char c = name.charAt(i);
if (c == '"' && (i == 0 || name.charAt(i - 1) != '\\'))
{
if (!quoted && i > 0 && name.charAt(i - 1) != '.')
{
context.errors.invalidKey(name, context.line.get());
return false;
}
quoted = !quoted;
} else if (!quoted && (ALLOWED_CHARS.indexOf(c) == -1))
{
context.errors.invalidKey(name, context.line.get());
return false;
}
}
return true;
}
private static boolean isValidTable(String name, Context context)
{
boolean valid = true;
if (!name.endsWith("]"))
{
valid = false;
}
String trimmed = name.substring(1, name.length() - 1).trim();
if (trimmed.isEmpty() || trimmed.charAt(0) == '.' || trimmed.endsWith("."))
{
valid = false;
}
if (!valid)
{
context.errors.invalidTable(name, context.line.get());
return false;
}
boolean quoted = false;
boolean dotAllowed = false;
boolean quoteAllowed = true;
boolean charAllowed = true;
for (int i = 0; i < trimmed.length(); i++)
{
char c = trimmed.charAt(i);
if (!valid)
{
break;
}
if (Keys.isQuote(c))
{
if (!quoteAllowed)
{
valid = false;
} else if (quoted && trimmed.charAt(i - 1) != '\\')
{
charAllowed = false;
dotAllowed = true;
quoteAllowed = false;
} else if (!quoted)
{
quoted = true;
quoteAllowed = true;
}
} else if (quoted)
{
continue;
} else if (c == '.')
{
if (dotAllowed)
{
charAllowed = true;
dotAllowed = false;
quoteAllowed = true;
} else
{
context.errors.emptyImplicitTable(name, context.line.get());
return false;
}
} else if (Character.isWhitespace(c))
{
char prev = trimmed.charAt(i - 1);
if (!Character.isWhitespace(prev) && prev != '.')
{
charAllowed = false;
dotAllowed = true;
quoteAllowed = true;
}
} else
{
if (charAllowed && ALLOWED_CHARS.indexOf(c) > -1)
{
charAllowed = true;
dotAllowed = true;
quoteAllowed = false;
} else
{
valid = false;
}
}
}
if (!valid)
{
context.errors.invalidTable(name, context.line.get());
return false;
}
return true;
}
private static boolean isValidTableArray(String line, Context context)
{
boolean valid = true;
if (!line.endsWith("]]"))
{
valid = false;
}
String trimmed = line.substring(2, line.length() - 2).trim();
if (trimmed.isEmpty() || trimmed.charAt(0) == '.' || trimmed.endsWith("."))
{
valid = false;
}
if (!valid)
{
context.errors.invalidTableArray(line, context.line.get());
return false;
}
boolean quoted = false;
boolean dotAllowed = false;
boolean quoteAllowed = true;
boolean charAllowed = true;
for (int i = 0; i < trimmed.length(); i++)
{
char c = trimmed.charAt(i);
if (!valid)
{
break;
}
if (c == '"')
{
if (!quoteAllowed)
{
valid = false;
} else if (quoted && trimmed.charAt(i - 1) != '\\')
{
charAllowed = false;
dotAllowed = true;
quoteAllowed = false;
} else if (!quoted)
{
quoted = true;
quoteAllowed = true;
}
} else if (quoted)
{
continue;
} else if (c == '.')
{
if (dotAllowed)
{
charAllowed = true;
dotAllowed = false;
quoteAllowed = true;
} else
{
context.errors.emptyImplicitTable(line, context.line.get());
return false;
}
} else if (Character.isWhitespace(c))
{
char prev = trimmed.charAt(i - 1);
if (!Character.isWhitespace(prev) && prev != '.' && prev != '"')
{
charAllowed = false;
dotAllowed = true;
quoteAllowed = true;
}
} else
{
if (charAllowed && ALLOWED_CHARS.indexOf(c) > -1)
{
charAllowed = true;
dotAllowed = true;
quoteAllowed = false;
} else
{
valid = false;
}
}
}
if (!valid)
{
context.errors.invalidTableArray(line, context.line.get());
return false;
}
return true;
}
}

View file

@ -0,0 +1,62 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
public class IdentifierConverter {
static final IdentifierConverter IDENTIFIER_CONVERTER = new IdentifierConverter();
Identifier convert(String s, AtomicInteger index, Context context) {
boolean quoted = false;
StringBuilder name = new StringBuilder();
boolean terminated = false;
boolean isKey = s.charAt(index.get()) != '[';
boolean isTableArray = !isKey && s.length() > index.get() + 1 && s.charAt(index.get() + 1) == '[';
boolean inComment = false;
for (int i = index.get(); i < s.length(); i = index.incrementAndGet()) {
char c = s.charAt(i);
if (Keys.isQuote(c) && (i == 0 || s.charAt(i - 1) != '\\')) {
quoted = !quoted;
name.append(c);
} else if (c == '\n') {
index.decrementAndGet();
break;
} else if (quoted) {
name.append(c);
} else if (c == '=' && isKey) {
terminated = true;
break;
} else if (c == ']' && !isKey) {
if (!isTableArray || s.length() > index.get() + 1 && s.charAt(index.get() + 1) == ']') {
terminated = true;
name.append(']');
if (isTableArray) {
name.append(']');
}
}
} else if (terminated && c == '#') {
inComment = true;
} else if (terminated && !Character.isWhitespace(c) && !inComment) {
terminated = false;
break;
} else if (!terminated) {
name.append(c);
}
}
if (!terminated) {
if (isKey) {
context.errors.unterminatedKey(name.toString(), context.line.get());
} else {
context.errors.invalidKey(name.toString(), context.line.get());
}
return Identifier.INVALID;
}
return Identifier.from(name.toString(), context);
}
private IdentifierConverter() {}
}

View file

@ -0,0 +1,30 @@
package dev.plex.toml;
/**
* Controls how a {@link TomlWriter} indents tables and key/value pairs.
*
* The default policy is to not indent.
*/
public class IndentationPolicy {
private final int tableIndent;
private final int keyValueIndent;
private final int arrayDelimiterPadding;
IndentationPolicy(int keyIndentation, int tableIndentation, int arrayDelimiterPadding) {
this.keyValueIndent = keyIndentation;
this.tableIndent = tableIndentation;
this.arrayDelimiterPadding = arrayDelimiterPadding;
}
int getTableIndent() {
return tableIndent;
}
int getKeyValueIndent() {
return keyValueIndent;
}
int getArrayDelimiterPadding() {
return arrayDelimiterPadding;
}
}

View file

@ -0,0 +1,76 @@
package dev.plex.toml;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
class InlineTableValueReader implements dev.plex.toml.ValueReader
{
static final InlineTableValueReader INLINE_TABLE_VALUE_READER = new InlineTableValueReader();
@Override
public boolean canRead(String s) {
return s.startsWith("{");
}
@Override
public Object read(String s, AtomicInteger sharedIndex, dev.plex.toml.Context context) {
AtomicInteger line = context.line;
int startLine = line.get();
int startIndex = sharedIndex.get();
boolean inKey = true;
boolean inValue = false;
boolean terminated = false;
StringBuilder currentKey = new StringBuilder();
HashMap<String, Object> results = new HashMap<String, Object>();
dev.plex.toml.Results.Errors errors = new dev.plex.toml.Results.Errors();
for (int i = sharedIndex.incrementAndGet(); sharedIndex.get() < s.length(); i = sharedIndex.incrementAndGet()) {
char c = s.charAt(i);
if (inValue && !Character.isWhitespace(c)) {
Object converted = dev.plex.toml.ValueReaders.VALUE_READERS.convert(s, sharedIndex, context.with(dev.plex.toml.Identifier.from(currentKey.toString(), context)));
if (converted instanceof dev.plex.toml.Results.Errors) {
errors.add((dev.plex.toml.Results.Errors) converted);
return errors;
}
String currentKeyTrimmed = currentKey.toString().trim();
Object previous = results.put(currentKeyTrimmed, converted);
if (previous != null) {
errors.duplicateKey(currentKeyTrimmed, context.line.get());
return errors;
}
currentKey = new StringBuilder();
inValue = false;
} else if (c == ',') {
inKey = true;
inValue = false;
currentKey = new StringBuilder();
} else if (c == '=') {
inKey = false;
inValue = true;
} else if (c == '}') {
terminated = true;
break;
} else if (inKey) {
currentKey.append(c);
}
}
if (!terminated) {
errors.unterminated(context.identifier.getName(), s.substring(startIndex), startLine);
}
if (errors.hasErrors()) {
return errors;
}
return results;
}
private InlineTableValueReader() {}
}

View file

@ -0,0 +1,69 @@
package dev.plex.toml;
import java.util.ArrayList;
import java.util.List;
class Keys {
static class Key {
final String name;
final int index;
final String path;
Key(String name, int index, Key next) {
this.name = name;
this.index = index;
if (next != null) {
this.path = name + "." + next.path;
} else {
this.path = name;
}
}
}
static Key[] split(String key) {
List<Key> splitKey = new ArrayList<Key>();
StringBuilder current = new StringBuilder();
boolean quoted = false;
boolean indexable = true;
boolean inIndex = false;
int index = -1;
for (int i = key.length() - 1; i > -1; i--) {
char c = key.charAt(i);
if (c == ']' && indexable) {
inIndex = true;
continue;
}
indexable = false;
if (c == '[' && inIndex) {
inIndex = false;
index = Integer.parseInt(current.toString());
current = new StringBuilder();
continue;
}
if (isQuote(c) && (i == 0 || key.charAt(i - 1) != '\\')) {
quoted = !quoted;
indexable = false;
}
if (c != '.' || quoted) {
current.insert(0, c);
} else {
splitKey.add(0, new Key(current.toString(), index, !splitKey.isEmpty() ? splitKey.get(0) : null));
indexable = true;
index = -1;
current = new StringBuilder();
}
}
splitKey.add(0, new Key(current.toString(), index, !splitKey.isEmpty() ? splitKey.get(0) : null));
return splitKey.toArray(new Key[0]);
}
static boolean isQuote(char c) {
return c == '"' || c == '\'';
}
private Keys() {}
}

View file

@ -0,0 +1,47 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
public class LiteralStringValueReader implements ValueReader
{
public static final LiteralStringValueReader LITERAL_STRING_VALUE_READER = new LiteralStringValueReader();
@Override
public boolean canRead(String s)
{
return s.startsWith("'");
}
@Override
public Object read(String s, AtomicInteger index, dev.plex.toml.Context context)
{
int startLine = context.line.get();
boolean terminated = false;
int startIndex = index.incrementAndGet();
for (int i = index.get(); i < s.length(); i = index.incrementAndGet())
{
char c = s.charAt(i);
if (c == '\'')
{
terminated = true;
break;
}
}
if (!terminated)
{
Results.Errors errors = new Results.Errors();
errors.unterminated(context.identifier.getName(), s.substring(startIndex), startLine);
return errors;
}
String substring = s.substring(startIndex, index.get());
return substring;
}
private LiteralStringValueReader()
{
}
}

View file

@ -0,0 +1,167 @@
package dev.plex.toml;
import java.io.File;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class MapValueWriter implements dev.plex.toml.ValueWriter
{
static final dev.plex.toml.ValueWriter MAP_VALUE_WRITER = new MapValueWriter();
private static final Pattern REQUIRED_QUOTING_PATTERN = Pattern.compile("^.*[^A-Za-z\\d_-].*$");
@Override
public boolean canWrite(Object value)
{
return value instanceof Map;
}
@Override
public void write(Object value, WriterContext context)
{
File file = null;
if (context.file != null)
{
file = context.file;
}
Map<?, ?> from = (Map<?, ?>) value;
Toml toml = null;
if (file != null)
{
toml = new Toml().read(file);
}
if (hasPrimitiveValues(from, context))
{
if (context.hasRun)
{
if (toml != null)
{
if (!toml.getValues().containsKey(context.key))
{
context.writeKey();
}
} else
{
context.writeKey();
}
}
}
// Render primitive types and arrays of primitive first so they are
// grouped under the same table (if there is one)
for (Map.Entry<?, ?> entry : from.entrySet())
{
Object key = entry.getKey();
Object fromValue = entry.getValue();
if (fromValue == null)
{
continue;
}
if (context.hasRun && toml != null)
{
if (context.key != null)
{
if (key.toString().equalsIgnoreCase(context.key)) continue;
if (toml.contains(context.key + "." + key)) continue;
}
}
dev.plex.toml.ValueWriter valueWriter = dev.plex.toml.ValueWriters.WRITERS.findWriterFor(fromValue);
if (valueWriter.isPrimitiveType())
{
context.indent();
context.write(quoteKey(key)).write(" = ");
valueWriter.write(fromValue, context);
context.write('\n');
} else if (valueWriter == dev.plex.toml.PrimitiveArrayValueWriter.PRIMITIVE_ARRAY_VALUE_WRITER)
{
context.indent();
context.setArrayKey(key.toString());
context.write(quoteKey(key)).write(" = ");
valueWriter.write(fromValue, context);
context.write('\n');
}
}
// Now render (sub)tables and arrays of tables
for (Object key : from.keySet())
{
Object fromValue = from.get(key);
if (fromValue == null)
{
continue;
}
if (context.hasRun && toml != null)
{
if (context.key != null)
{
if (key.toString().equalsIgnoreCase(context.key)) continue;
if (toml.contains(context.key + "." + key)) continue;
}
}
dev.plex.toml.ValueWriter valueWriter = dev.plex.toml.ValueWriters.WRITERS.findWriterFor(fromValue);
if (valueWriter == this || valueWriter == dev.plex.toml.ObjectValueWriter.OBJECT_VALUE_WRITER || valueWriter == dev.plex.toml.TableArrayValueWriter.TABLE_ARRAY_VALUE_WRITER)
{
WriterContext context1 = context.pushTable(quoteKey(key));
context1.parentName = key.toString();
context1.hasRun = true;
context1.file = context.file;
valueWriter.write(fromValue, context1);
}
}
}
@Override
public boolean isPrimitiveType()
{
return false;
}
private static String quoteKey(Object key)
{
String stringKey = key.toString();
Matcher matcher = REQUIRED_QUOTING_PATTERN.matcher(stringKey);
if (matcher.matches())
{
stringKey = "\"" + stringKey + "\"";
}
return stringKey;
}
private static boolean hasPrimitiveValues(Map<?, ?> values, WriterContext context)
{
for (Object key : values.keySet())
{
Object fromValue = values.get(key);
if (fromValue == null)
{
continue;
}
dev.plex.toml.ValueWriter valueWriter = dev.plex.toml.ValueWriters.WRITERS.findWriterFor(fromValue);
if (valueWriter.isPrimitiveType() || valueWriter == dev.plex.toml.PrimitiveArrayValueWriter.PRIMITIVE_ARRAY_VALUE_WRITER)
{
return true;
}
}
return false;
}
private MapValueWriter()
{
}
}

View file

@ -0,0 +1,52 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
class MultilineLiteralStringValueReader implements ValueReader
{
static final MultilineLiteralStringValueReader MULTILINE_LITERAL_STRING_VALUE_READER = new MultilineLiteralStringValueReader();
@Override
public boolean canRead(String s) {
return s.startsWith("'''");
}
@Override
public Object read(String s, AtomicInteger index, Context context) {
AtomicInteger line = context.line;
int startLine = line.get();
int originalStartIndex = index.get();
int startIndex = index.addAndGet(3);
int endIndex = -1;
if (s.charAt(startIndex) == '\n') {
startIndex = index.incrementAndGet();
line.incrementAndGet();
}
for (int i = startIndex; i < s.length(); i = index.incrementAndGet()) {
char c = s.charAt(i);
if (c == '\n') {
line.incrementAndGet();
}
if (c == '\'' && s.length() > i + 2 && s.charAt(i + 1) == '\'' && s.charAt(i + 2) == '\'') {
endIndex = i;
index.addAndGet(2);
break;
}
}
if (endIndex == -1) {
Results.Errors errors = new Results.Errors();
errors.unterminated(context.identifier.getName(), s.substring(originalStartIndex), startLine);
return errors;
}
return s.substring(startIndex, endIndex);
}
private MultilineLiteralStringValueReader() {}
}

View file

@ -0,0 +1,57 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
class MultilineStringValueReader implements ValueReader
{
static final MultilineStringValueReader MULTILINE_STRING_VALUE_READER = new MultilineStringValueReader();
@Override
public boolean canRead(String s) {
return s.startsWith("\"\"\"");
}
@Override
public Object read(String s, AtomicInteger index, dev.plex.toml.Context context) {
AtomicInteger line = context.line;
int startLine = line.get();
int originalStartIndex = index.get();
int startIndex = index.addAndGet(3);
int endIndex = -1;
if (s.charAt(startIndex) == '\n') {
startIndex = index.incrementAndGet();
line.incrementAndGet();
}
for (int i = startIndex; i < s.length(); i = index.incrementAndGet()) {
char c = s.charAt(i);
if (c == '\n') {
line.incrementAndGet();
} else if (c == '"' && s.length() > i + 2 && s.charAt(i + 1) == '"' && s.charAt(i + 2) == '"') {
endIndex = i;
index.addAndGet(2);
break;
}
}
if (endIndex == -1) {
dev.plex.toml.Results.Errors errors = new dev.plex.toml.Results.Errors();
errors.unterminated(context.identifier.getName(), s.substring(originalStartIndex), startLine);
return errors;
}
s = s.substring(startIndex, endIndex);
s = s.replaceAll("\\\\\\s+", "");
s = dev.plex.toml.StringValueReaderWriter.STRING_VALUE_READER_WRITER.replaceUnicodeCharacters(s);
s = dev.plex.toml.StringValueReaderWriter.STRING_VALUE_READER_WRITER.replaceSpecialCharacters(s);
return s;
}
private MultilineStringValueReader() {
}
}

View file

@ -0,0 +1,106 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
class NumberValueReaderWriter implements dev.plex.toml.ValueReader, dev.plex.toml.ValueWriter
{
static final NumberValueReaderWriter NUMBER_VALUE_READER_WRITER = new NumberValueReaderWriter();
@Override
public boolean canRead(String s) {
char firstChar = s.charAt(0);
return firstChar == '+' || firstChar == '-' || Character.isDigit(firstChar);
}
@Override
public Object read(String s, AtomicInteger index, dev.plex.toml.Context context) {
boolean signable = true;
boolean dottable = false;
boolean exponentable = false;
boolean terminatable = false;
boolean underscorable = false;
String type = "";
StringBuilder sb = new StringBuilder();
for (int i = index.get(); i < s.length(); i = index.incrementAndGet()) {
char c = s.charAt(i);
boolean notLastChar = s.length() > i + 1;
if (Character.isDigit(c)) {
sb.append(c);
signable = false;
terminatable = true;
if (type.isEmpty()) {
type = "integer";
dottable = true;
}
underscorable = notLastChar;
exponentable = !type.equals("exponent");
} else if ((c == '+' || c == '-') && signable && notLastChar) {
signable = false;
terminatable = false;
if (c == '-') {
sb.append('-');
}
} else if (c == '.' && dottable && notLastChar) {
sb.append('.');
type = "float";
terminatable = false;
dottable = false;
exponentable = false;
underscorable = false;
} else if ((c == 'E' || c == 'e') && exponentable && notLastChar) {
sb.append('E');
type = "exponent";
terminatable = false;
signable = true;
dottable = false;
exponentable = false;
underscorable = false;
} else if (c == '_' && underscorable && notLastChar && Character.isDigit(s.charAt(i + 1))) {
underscorable = false;
} else {
if (!terminatable) {
type = "";
}
index.decrementAndGet();
break;
}
}
if (type.equals("integer")) {
return Long.valueOf(sb.toString());
} else if (type.equals("float")) {
return Double.valueOf(sb.toString());
} else if (type.equals("exponent")) {
String[] exponentString = sb.toString().split("E");
return Double.parseDouble(exponentString[0]) * Math.pow(10, Double.parseDouble(exponentString[1]));
} else {
dev.plex.toml.Results.Errors errors = new dev.plex.toml.Results.Errors();
errors.invalidValue(context.identifier.getName(), sb.toString(), context.line.get());
return errors;
}
}
@Override
public boolean canWrite(Object value) {
return Number.class.isInstance(value);
}
@Override
public void write(Object value, dev.plex.toml.WriterContext context) {
context.write(value.toString());
}
@Override
public boolean isPrimitiveType() {
return true;
}
@Override
public String toString() {
return "number";
}
}

View file

@ -0,0 +1,88 @@
package dev.plex.toml;
import com.google.gson.annotations.SerializedName;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
class ObjectValueWriter implements ValueWriter
{
static final ValueWriter OBJECT_VALUE_WRITER = new ObjectValueWriter();
@Override
public boolean canWrite(Object value)
{
return true;
}
@Override
public void write(Object value, WriterContext context)
{
Map<String, Object> to = new LinkedHashMap<String, Object>();
Set<Field> fields = getFields(value.getClass());
for (Field field : fields)
{
if (field.isAnnotationPresent(SerializedName.class))
{
to.put(field.getDeclaredAnnotation(SerializedName.class).value(), getFieldValue(field, value));
} else {
to.put(field.getName(), getFieldValue(field, value));
}
}
MapValueWriter.MAP_VALUE_WRITER.write(to, context);
}
@Override
public boolean isPrimitiveType()
{
return false;
}
private static Set<Field> getFields(Class<?> cls)
{
Set<Field> fields = new LinkedHashSet<Field>(Arrays.asList(cls.getDeclaredFields()));
while (cls != Object.class)
{
fields.addAll(Arrays.asList(cls.getDeclaredFields()));
cls = cls.getSuperclass();
}
removeConstantsAndSyntheticFields(fields);
return fields;
}
private static void removeConstantsAndSyntheticFields(Set<Field> fields)
{
Iterator<Field> iterator = fields.iterator();
while (iterator.hasNext())
{
Field field = iterator.next();
if ((Modifier.isFinal(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) || field.isSynthetic() || Modifier.isTransient(field.getModifiers()))
{
iterator.remove();
}
}
}
private static Object getFieldValue(Field field, Object o)
{
boolean isAccessible = field.isAccessible();
field.setAccessible(true);
Object value = null;
try
{
value = field.get(o);
} catch (IllegalAccessException ignored)
{
}
field.setAccessible(isAccessible);
return value;
}
private ObjectValueWriter()
{
}
}

View file

@ -0,0 +1,53 @@
package dev.plex.toml;
import java.util.Collection;
class PrimitiveArrayValueWriter extends ArrayValueWriter
{
static final ValueWriter PRIMITIVE_ARRAY_VALUE_WRITER = new PrimitiveArrayValueWriter();
@Override
public boolean canWrite(Object value) {
return isArrayish(value) && isArrayOfPrimitive(value);
}
@Override
public void write(Object o, WriterContext context) {
Collection<?> values = normalize(o);
context.write('[');
context.writeArrayDelimiterPadding();
boolean first = true;
ValueWriter firstWriter = null;
for (Object value : values) {
if (first) {
firstWriter = ValueWriters.WRITERS.findWriterFor(value);
first = false;
} else {
ValueWriter writer = ValueWriters.WRITERS.findWriterFor(value);
if (writer != firstWriter) {
throw new IllegalStateException(
context.getContextPath() +
": cannot write a heterogeneous array; first element was of type " + firstWriter +
" but found " + writer
);
}
context.write(", ");
}
ValueWriters.WRITERS.findWriterFor(value).write(value, context);
}
context.writeArrayDelimiterPadding();
context.write(']');
}
private PrimitiveArrayValueWriter() {}
@Override
public String toString() {
return "primitive-array";
}
}

View file

@ -0,0 +1,290 @@
package dev.plex.toml;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
class Results {
static class Errors {
private final StringBuilder sb = new StringBuilder();
void duplicateTable(String table, int line) {
sb.append("Duplicate table definition on line ")
.append(line)
.append(": [")
.append(table)
.append("]");
}
public void tableDuplicatesKey(String table, AtomicInteger line) {
sb.append("Key already exists for table defined on line ")
.append(line.get())
.append(": [")
.append(table)
.append("]");
}
public void keyDuplicatesTable(String key, AtomicInteger line) {
sb.append("Table already exists for key defined on line ")
.append(line.get())
.append(": ")
.append(key);
}
void emptyImplicitTable(String table, int line) {
sb.append("Invalid table definition due to empty implicit table name: ")
.append(table);
}
void invalidTable(String table, int line) {
sb.append("Invalid table definition on line ")
.append(line)
.append(": ")
.append(table)
.append("]");
}
void duplicateKey(String key, int line) {
sb.append("Duplicate key");
if (line > -1) {
sb.append(" on line ")
.append(line);
}
sb.append(": ")
.append(key);
}
void invalidTextAfterIdentifier(dev.plex.toml.Identifier identifier, char text, int line) {
sb.append("Invalid text after key ")
.append(identifier.getName())
.append(" on line ")
.append(line)
.append(". Make sure to terminate the value or add a comment (#).");
}
void invalidKey(String key, int line) {
sb.append("Invalid key on line ")
.append(line)
.append(": ")
.append(key);
}
void invalidTableArray(String tableArray, int line) {
sb.append("Invalid table array definition on line ")
.append(line)
.append(": ")
.append(tableArray);
}
void invalidValue(String key, String value, int line) {
sb.append("Invalid value on line ")
.append(line)
.append(": ")
.append(key)
.append(" = ")
.append(value);
}
void unterminatedKey(String key, int line) {
sb.append("Key is not followed by an equals sign on line ")
.append(line)
.append(": ")
.append(key);
}
void unterminated(String key, String value, int line) {
sb.append("Unterminated value on line ")
.append(line)
.append(": ")
.append(key)
.append(" = ")
.append(value.trim());
}
public void heterogenous(String key, int line) {
sb.append(key)
.append(" becomes a heterogeneous array on line ")
.append(line);
}
boolean hasErrors() {
return sb.length() > 0;
}
@Override
public String toString() {
return sb.toString();
}
public void add(Errors other) {
sb.append(other.sb);
}
}
final Errors errors = new Errors();
private final Set<String> tables = new HashSet<String>();
private final Deque<Container> stack = new ArrayDeque<Container>();
Results() {
stack.push(new Container.Table(""));
}
void addValue(String key, Object value, AtomicInteger line) {
Container currentTable = stack.peek();
if (value instanceof Map) {
String path = getInlineTablePath(key);
if (path == null) {
startTable(key, line);
} else if (path.isEmpty()) {
startTables(dev.plex.toml.Identifier.from(key, null), line);
} else {
startTables(dev.plex.toml.Identifier.from(path, null), line);
}
@SuppressWarnings("unchecked")
Map<String, Object> valueMap = (Map<String, Object>) value;
for (Map.Entry<String, Object> entry : valueMap.entrySet()) {
addValue(entry.getKey(), entry.getValue(), line);
}
stack.pop();
} else if (currentTable.accepts(key)) {
currentTable.put(key, value);
} else {
if (currentTable.get(key) instanceof Container) {
errors.keyDuplicatesTable(key, line);
} else {
errors.duplicateKey(key, line != null ? line.get() : -1);
}
}
}
void startTableArray(dev.plex.toml.Identifier identifier, AtomicInteger line) {
String tableName = identifier.getBareName();
while (stack.size() > 1) {
stack.pop();
}
dev.plex.toml.Keys.Key[] tableParts = dev.plex.toml.Keys.split(tableName);
for (int i = 0; i < tableParts.length; i++) {
String tablePart = tableParts[i].name;
Container currentContainer = stack.peek();
if (currentContainer.get(tablePart) instanceof Container.TableArray) {
Container.TableArray currentTableArray = (Container.TableArray) currentContainer.get(tablePart);
stack.push(currentTableArray);
if (i == tableParts.length - 1) {
currentTableArray.put(tablePart, new Container.Table());
}
stack.push(currentTableArray.getCurrent());
currentContainer = stack.peek();
} else if (currentContainer.get(tablePart) instanceof Container.Table && i < tableParts.length - 1) {
Container nextTable = (Container) currentContainer.get(tablePart);
stack.push(nextTable);
} else if (currentContainer.accepts(tablePart)) {
Container newContainer = i == tableParts.length - 1 ? new Container.TableArray() : new Container.Table();
addValue(tablePart, newContainer, line);
stack.push(newContainer);
if (newContainer instanceof Container.TableArray) {
stack.push(((Container.TableArray) newContainer).getCurrent());
}
} else {
errors.duplicateTable(tableName, line.get());
break;
}
}
}
void startTables(dev.plex.toml.Identifier id, AtomicInteger line) {
String tableName = id.getBareName();
while (stack.size() > 1) {
stack.pop();
}
dev.plex.toml.Keys.Key[] tableParts = dev.plex.toml.Keys.split(tableName);
for (int i = 0; i < tableParts.length; i++) {
String tablePart = tableParts[i].name;
Container currentContainer = stack.peek();
if (currentContainer.get(tablePart) instanceof Container) {
Container nextTable = (Container) currentContainer.get(tablePart);
if (i == tableParts.length - 1 && !nextTable.isImplicit()) {
errors.duplicateTable(tableName, line.get());
return;
}
stack.push(nextTable);
if (stack.peek() instanceof Container.TableArray) {
stack.push(((Container.TableArray) stack.peek()).getCurrent());
}
} else if (currentContainer.accepts(tablePart)) {
startTable(tablePart, i < tableParts.length - 1, line);
} else {
errors.tableDuplicatesKey(tablePart, line);
break;
}
}
}
/**
* Warning: After this method has been called, this instance is no longer usable.
*/
Map<String, Object> consume() {
Container values = stack.getLast();
stack.clear();
return ((Container.Table) values).consume();
}
private Container startTable(String tableName, AtomicInteger line) {
Container newTable = new Container.Table(tableName);
addValue(tableName, newTable, line);
stack.push(newTable);
return newTable;
}
private Container startTable(String tableName, boolean implicit, AtomicInteger line) {
Container newTable = new Container.Table(tableName, implicit);
addValue(tableName, newTable, line);
stack.push(newTable);
return newTable;
}
private String getInlineTablePath(String key) {
Iterator<Container> descendingIterator = stack.descendingIterator();
StringBuilder sb = new StringBuilder();
while (descendingIterator.hasNext()) {
Container next = descendingIterator.next();
if (next instanceof Container.TableArray) {
return null;
}
Container.Table table = (Container.Table) next;
if (table.name == null) {
break;
}
if (sb.length() > 0) {
sb.append('.');
}
sb.append(table.name);
}
if (sb.length() > 0) {
sb.append('.');
}
sb.append(key)
.insert(0, '[')
.append(']');
return sb.toString();
}
}

View file

@ -0,0 +1,129 @@
package dev.plex.toml;
import java.net.URI;
import java.net.URL;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class StringValueReaderWriter implements ValueReader, ValueWriter
{
static final StringValueReaderWriter STRING_VALUE_READER_WRITER = new StringValueReaderWriter();
private static final Pattern UNICODE_REGEX = Pattern.compile("\\\\[uU](.{4})");
static private final String[] specialCharacterEscapes = new String[93];
static {
specialCharacterEscapes['\b'] = "\\b";
specialCharacterEscapes['\t'] = "\\t";
specialCharacterEscapes['\n'] = "\\n";
specialCharacterEscapes['\f'] = "\\f";
specialCharacterEscapes['\r'] = "\\r";
specialCharacterEscapes['"'] = "\\\"";
specialCharacterEscapes['\\'] = "\\\\";
}
@Override
public boolean canRead(String s) {
return s.startsWith("\"");
}
@Override
public Object read(String s, AtomicInteger index, Context context) {
int startIndex = index.incrementAndGet();
int endIndex = -1;
for (int i = index.get(); i < s.length(); i = index.incrementAndGet()) {
char ch = s.charAt(i);
if (ch == '"' && s.charAt(i - 1) != '\\') {
endIndex = i;
break;
}
}
if (endIndex == -1) {
Results.Errors errors = new Results.Errors();
errors.unterminated(context.identifier.getName(), s.substring(startIndex - 1), context.line.get());
return errors;
}
String raw = s.substring(startIndex, endIndex);
s = replaceUnicodeCharacters(raw);
s = replaceSpecialCharacters(s);
if (s == null) {
Results.Errors errors = new Results.Errors();
errors.invalidValue(context.identifier.getName(), raw, context.line.get());
return errors;
}
return s;
}
String replaceUnicodeCharacters(String value) {
Matcher unicodeMatcher = UNICODE_REGEX.matcher(value);
while (unicodeMatcher.find()) {
value = value.replace(unicodeMatcher.group(), new String(Character.toChars(Integer.parseInt(unicodeMatcher.group(1), 16))));
}
return value;
}
String replaceSpecialCharacters(String s) {
for (int i = 0; i < s.length() - 1; i++) {
char ch = s.charAt(i);
char next = s.charAt(i + 1);
if (ch == '\\' && next == '\\') {
i++;
} else if (ch == '\\' && !(next == 'b' || next == 'f' || next == 'n' || next == 't' || next == 'r' || next == '"' || next == '\\')) {
return null;
}
}
return s.replace("\\n", "\n")
.replace("\\\"", "\"")
.replace("\\t", "\t")
.replace("\\r", "\r")
.replace("\\\\", "\\")
.replace("\\/", "/")
.replace("\\b", "\b")
.replace("\\f", "\f");
}
@Override
public boolean canWrite(Object value) {
return value instanceof String || value instanceof Character || value instanceof URL || value instanceof URI || value instanceof Enum;
}
@Override
public void write(Object value, WriterContext context) {
context.write('"');
escapeUnicode(value.toString(), context);
context.write('"');
}
@Override
public boolean isPrimitiveType() {
return true;
}
private void escapeUnicode(String in, WriterContext context) {
for (int i = 0; i < in.length(); i++) {
int codePoint = in.codePointAt(i);
if (codePoint < specialCharacterEscapes.length && specialCharacterEscapes[codePoint] != null) {
context.write(specialCharacterEscapes[codePoint]);
} else {
context.write(in.charAt(i));
}
}
}
private StringValueReaderWriter() {}
@Override
public String toString() {
return "string";
}
}

View file

@ -0,0 +1,33 @@
package dev.plex.toml;
import java.util.Collection;
import static dev.plex.toml.ValueWriters.WRITERS;
class TableArrayValueWriter extends ArrayValueWriter
{
static final ValueWriter TABLE_ARRAY_VALUE_WRITER = new TableArrayValueWriter();
@Override
public boolean canWrite(Object value) {
return isArrayish(value) && !isArrayOfPrimitive(value);
}
@Override
public void write(Object from, WriterContext context) {
Collection<?> values = normalize(from);
WriterContext subContext = context.pushTableFromArray();
for (Object value : values) {
WRITERS.findWriterFor(value).write(value, subContext);
}
}
private TableArrayValueWriter() {}
@Override
public String toString() {
return "table-array";
}
}

View file

@ -0,0 +1,481 @@
package dev.plex.toml;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import lombok.Getter;
import java.io.*;
import java.util.*;
/**
* <p>Provides access to the keys and tables in a TOML data source.</p>
*
* <p>All getters can fall back to default values if they have been provided as a constructor argument.
* Getters for simple values (String, Date, etc.) will return null if no matching key exists.
* {@link #getList(String)}, {@link #getTable(String)} and {@link #getTables(String)} return empty values if there is no matching key.</p>
*
* <p>All read methods throw an {@link IllegalStateException} if the TOML is incorrect.</p>
*
* <p>Example usage:</p>
* <pre><code>
* Toml toml = new Toml().read(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);
* </code></pre>
*/
public class Toml
{
private static final Gson DEFAULT_GSON = new Gson();
@Getter
private Map<String, Object> values = new HashMap<String, Object>();
private final Toml defaults;
/**
* Creates Toml instance with no defaults.
*/
public Toml()
{
this(null);
}
/**
* @param defaults fallback values used when the requested key or table is not present in the TOML source that has been read.
*/
public Toml(Toml defaults)
{
this(defaults, new HashMap<>());
}
/**
* Populates the current Toml instance with values from file.
*
* @param file The File to be read. Expected to be encoded as UTF-8.
* @return this instance
* @throws IllegalStateException If file contains invalid TOML
*/
public Toml read(File file)
{
try
{
return read(new InputStreamReader(new FileInputStream(file), "UTF8"));
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
/**
* Populates the current Toml instance with values from inputStream.
*
* @param inputStream Closed after it has been read.
* @return this instance
* @throws IllegalStateException If file contains invalid TOML
*/
public Toml read(InputStream inputStream)
{
return read(new InputStreamReader(inputStream));
}
/**
* Populates the current Toml instance with values from reader.
*
* @param reader Closed after it has been read.
* @return this instance
* @throws IllegalStateException If file contains invalid TOML
*/
public Toml read(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();
}
read(w.toString());
} catch (IOException e)
{
throw new RuntimeException(e);
} finally
{
try
{
bufferedReader.close();
} catch (IOException e)
{
}
}
return this;
}
/**
* Populates the current Toml instance with values from otherToml.
*
* @param otherToml
* @return this instance
*/
public Toml read(Toml otherToml)
{
this.values = otherToml.values;
return this;
}
/**
* Populates the current Toml instance with values from tomlString.
*
* @param tomlString String to be read.
* @return this instance
* @throws IllegalStateException If tomlString is not valid TOML
*/
public Toml read(String tomlString) throws IllegalStateException
{
dev.plex.toml.Results results = dev.plex.toml.TomlParser.run(tomlString);
if (results.errors.hasErrors())
{
throw new IllegalStateException(results.errors.toString());
}
this.values = results.consume();
return this;
}
public String getString(String key)
{
return (String) get(key);
}
public String getString(String key, String defaultValue)
{
String val = getString(key);
return val == null ? defaultValue : val;
}
public Long getLong(String key)
{
return (Long) get(key);
}
public Long getLong(String key, Long defaultValue)
{
Long val = getLong(key);
return val == null ? defaultValue : val;
}
/**
* @param key a TOML key
* @param <T> type of list items
* @return <code>null</code> if the key is not found
*/
public <T> List<T> getList(String key)
{
@SuppressWarnings("unchecked")
List<T> list = (List<T>) get(key);
return list;
}
/**
* @param key a TOML key
* @param defaultValue a list of default values
* @param <T> type of list items
* @return <code>null</code> is the key is not found
*/
public <T> List<T> getList(String key, List<T> defaultValue)
{
List<T> list = getList(key);
return list != null ? list : defaultValue;
}
public Boolean getBoolean(String key)
{
return (Boolean) get(key);
}
public Boolean getBoolean(String key, Boolean defaultValue)
{
Boolean val = getBoolean(key);
return val == null ? defaultValue : val;
}
public Date getDate(String key)
{
return (Date) get(key);
}
public Date getDate(String key, Date defaultValue)
{
Date val = getDate(key);
return val == null ? defaultValue : val;
}
public Double getDouble(String key)
{
return (Double) get(key);
}
public Double getDouble(String key, Double defaultValue)
{
Double val = getDouble(key);
return val == null ? defaultValue : val;
}
/**
* @param key A table name, not including square brackets.
* @return A new Toml instance or <code>null</code> if no value is found for key.
*/
@SuppressWarnings("unchecked")
public Toml getTable(String key)
{
Map<String, Object> map = (Map<String, Object>) get(key);
return map != null ? new Toml(null, map) : null;
}
/**
* @param key Name of array of tables, not including square brackets.
* @return A {@link List} of Toml instances or <code>null</code> if no value is found for key.
*/
@SuppressWarnings("unchecked")
public List<Toml> getTables(String key)
{
List<Map<String, Object>> tableArray = (List<Map<String, Object>>) get(key);
if (tableArray == null)
{
return null;
}
ArrayList<Toml> tables = new ArrayList<Toml>();
for (Map<String, Object> table : tableArray)
{
tables.add(new Toml(null, table));
}
return tables;
}
/**
* @param key a key name, can be compound (eg. a.b.c)
* @return true if key is present
*/
public boolean contains(String key)
{
return get(key) != null;
}
/**
* @param key a key name, can be compound (eg. a.b.c)
* @return true if key is present and is a primitive
*/
public boolean containsPrimitive(String key)
{
Object object = get(key);
return object != null && !(object instanceof Map) && !(object instanceof List);
}
/**
* @param key a key name, can be compound (eg. a.b.c)
* @return true if key is present and is a table
*/
public boolean containsTable(String key)
{
Object object = get(key);
return object != null && (object instanceof Map);
}
/**
* @param key a key name, can be compound (eg. a.b.c)
* @return true if key is present and is a table array
*/
public boolean containsTableArray(String key)
{
Object object = get(key);
return object != null && (object instanceof List);
}
public boolean isEmpty()
{
return values.isEmpty();
}
/**
* <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>
*
* <p>Tables are recursively converted to custom classes or to {@link Map Map&lt;String, Object&gt;}.</p>
*
* <p>In addition to straight-forward conversion of TOML primitives, the following are also available:</p>
*
* <ul>
* <li>Integer -&gt; int, long (or wrapper), {@link java.math.BigInteger}</li>
* <li>Float -&gt; float, double (or wrapper), {@link java.math.BigDecimal}</li>
* <li>One-letter String -&gt; char, {@link Character}</li>
* <li>String -&gt; {@link String}, enum, {@link java.net.URI}, {@link java.net.URL}</li>
* <li>Multiline and Literal Strings -&gt; {@link String}</li>
* <li>Array -&gt; {@link List}, {@link Set}, array. The generic type can be anything that can be converted.</li>
* <li>Table -&gt; Custom class, {@link Map Map&lt;String, Object&gt;}</li>
* </ul>
*
* @param targetClass Class to deserialize TOML to.
* @param <T> type of targetClass.
* @return A new instance of targetClass.
*/
public <T> T to(Class<T> targetClass)
{
JsonElement json = DEFAULT_GSON.toJsonTree(toMap());
if (targetClass == JsonElement.class)
{
return targetClass.cast(json);
}
return DEFAULT_GSON.fromJson(json, targetClass);
}
public Map<String, Object> toMap()
{
HashMap<String, Object> valuesCopy = new HashMap<String, Object>(values);
if (defaults != null)
{
for (Map.Entry<String, Object> entry : defaults.values.entrySet())
{
if (!valuesCopy.containsKey(entry.getKey()))
{
valuesCopy.put(entry.getKey(), entry.getValue());
}
}
}
return valuesCopy;
}
/**
* @return a {@link Set} of Map.Entry instances. Modifications to the {@link Set} are not reflected in this Toml instance. Entries are immutable, so {@link Map.Entry#setValue(Object)} throws an UnsupportedOperationException.
*/
public Set<Map.Entry<String, Object>> entrySet()
{
Set<Map.Entry<String, Object>> entries = new LinkedHashSet<Map.Entry<String, Object>>();
for (Map.Entry<String, Object> entry : values.entrySet())
{
Class<? extends Object> entryClass = entry.getValue().getClass();
if (Map.class.isAssignableFrom(entryClass))
{
entries.add(new Entry(entry.getKey(), getTable(entry.getKey())));
} else if (List.class.isAssignableFrom(entryClass))
{
List<?> value = (List<?>) entry.getValue();
if (!value.isEmpty() && value.get(0) instanceof Map)
{
entries.add(new Entry(entry.getKey(), getTables(entry.getKey())));
} else
{
entries.add(new Entry(entry.getKey(), value));
}
} else
{
entries.add(new Entry(entry.getKey(), entry.getValue()));
}
}
return entries;
}
private class Entry implements Map.Entry<String, Object>
{
private final String key;
private final Object value;
@Override
public String getKey()
{
return key;
}
@Override
public Object getValue()
{
return value;
}
@Override
public Object setValue(Object value)
{
throw new UnsupportedOperationException("TOML entry values cannot be changed.");
}
private Entry(String key, Object value)
{
this.key = key;
this.value = value;
}
}
@SuppressWarnings("unchecked")
public Object get(String key)
{
if (values.containsKey(key))
{
return values.get(key);
}
Object current = new HashMap<>(values);
dev.plex.toml.Keys.Key[] keys = dev.plex.toml.Keys.split(key);
for (dev.plex.toml.Keys.Key k : keys)
{
if (k.index == -1 && current instanceof Map && ((Map<String, Object>) current).containsKey(k.path))
{
return ((Map<String, Object>) current).get(k.path);
}
current = ((Map<String, Object>) current).get(k.name);
if (k.index > -1 && current != null)
{
if (k.index >= ((List<?>) current).size())
{
return null;
}
current = ((List<?>) current).get(k.index);
}
if (current == null)
{
return defaults != null ? defaults.get(key) : null;
}
}
return current;
}
private Toml(Toml defaults, Map<String, Object> values)
{
this.values = values;
this.defaults = defaults;
}
}

View file

@ -0,0 +1,63 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
class TomlParser {
static dev.plex.toml.Results run(String tomlString) {
final dev.plex.toml.Results results = new dev.plex.toml.Results();
if (tomlString.isEmpty()) {
return results;
}
AtomicInteger index = new AtomicInteger();
boolean inComment = false;
AtomicInteger line = new AtomicInteger(1);
dev.plex.toml.Identifier identifier = null;
Object value = null;
for (int i = index.get(); i < tomlString.length(); i = index.incrementAndGet()) {
char c = tomlString.charAt(i);
if (results.errors.hasErrors()) {
break;
}
if (c == '#' && !inComment) {
inComment = true;
} else if (!Character.isWhitespace(c) && !inComment && identifier == null) {
dev.plex.toml.Identifier id = dev.plex.toml.IdentifierConverter.IDENTIFIER_CONVERTER.convert(tomlString, index, new dev.plex.toml.Context(null, line, results.errors));
if (id != dev.plex.toml.Identifier.INVALID) {
if (id.isKey()) {
identifier = id;
} else if (id.isTable()) {
results.startTables(id, line);
} else if (id.isTableArray()) {
results.startTableArray(id, line);
}
}
} else if (c == '\n') {
inComment = false;
identifier = null;
value = null;
line.incrementAndGet();
} else if (!inComment && identifier != null && identifier.isKey() && value == null && !Character.isWhitespace(c)) {
value = ValueReaders.VALUE_READERS.convert(tomlString, index, new dev.plex.toml.Context(identifier, line, results.errors));
if (value instanceof dev.plex.toml.Results.Errors) {
results.errors.add((dev.plex.toml.Results.Errors) value);
} else {
results.addValue(identifier.getName(), value, line);
}
} else if (value != null && !inComment && !Character.isWhitespace(c)) {
results.errors.invalidTextAfterIdentifier(identifier, c, line.get());
}
}
return results;
}
private TomlParser() {}
}

View file

@ -0,0 +1,178 @@
package dev.plex.toml;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import static dev.plex.toml.ValueWriters.WRITERS;
/**
* <p>Converts Objects to TOML</p>
*
* <p>An input Object can comprise arbitrarily nested combinations of Java primitive types,
* other {@link Object}s, {@link Map}s, {@link List}s, and Arrays. {@link Object}s and {@link Map}s
* are output to TOML tables, and {@link List}s and Array to TOML arrays.</p>
*
* <p>Example usage:</p>
* <pre><code>
* class AClass {
* int anInt = 1;
* int[] anArray = { 2, 3 };
* }
*
* String tomlString = new TomlWriter().write(new AClass());
* </code></pre>
*/
public class TomlWriter
{
public static class Builder
{
private int keyIndentation;
private int tableIndentation;
private int arrayDelimiterPadding = 0;
private TimeZone timeZone = TimeZone.getTimeZone("UTC");
private boolean showFractionalSeconds = false;
public Builder indentValuesBy(int spaces)
{
this.keyIndentation = spaces;
return this;
}
public Builder indentTablesBy(int spaces)
{
this.tableIndentation = spaces;
return this;
}
public Builder timeZone(TimeZone timeZone)
{
this.timeZone = timeZone;
return this;
}
/**
* @param spaces number of spaces to put between opening square bracket and first item and between closing square bracket and last item
* @return this TomlWriter.Builder instance
*/
public Builder padArrayDelimitersBy(int spaces)
{
this.arrayDelimiterPadding = spaces;
return this;
}
public TomlWriter build()
{
return new TomlWriter(keyIndentation, tableIndentation, arrayDelimiterPadding, timeZone, showFractionalSeconds);
}
public Builder showFractionalSeconds()
{
this.showFractionalSeconds = true;
return this;
}
}
private final IndentationPolicy indentationPolicy;
private final dev.plex.toml.DatePolicy datePolicy;
/**
* Creates a TomlWriter instance.
*/
public TomlWriter()
{
this(0, 0, 0, TimeZone.getTimeZone("UTC"), false);
}
private TomlWriter(int keyIndentation, int tableIndentation, int arrayDelimiterPadding, TimeZone timeZone, boolean showFractionalSeconds)
{
this.indentationPolicy = new IndentationPolicy(keyIndentation, tableIndentation, arrayDelimiterPadding);
this.datePolicy = new dev.plex.toml.DatePolicy(timeZone, showFractionalSeconds);
}
/**
* Write an Object into TOML String.
*
* @param from the object to be written
* @return a string containing the TOML representation of the given Object
*/
public String write(Object from)
{
try
{
StringWriter output = new StringWriter();
write(from, output, null);
return output.toString();
} catch (IOException e)
{
throw new RuntimeException(e);
}
}
/**
* Write an Object in TOML to a {@link File}. Output is encoded as UTF-8.
*
* @param from the object to be written
* @param target the File to which the TOML will be written
* @throws IOException if any file operations fail
*/
public void write(Object from, File target) throws IOException
{
OutputStream outputStream = new FileOutputStream(target, true);
try
{
write(from, outputStream, target);
} finally
{
outputStream.close();
}
}
/**
* Write an Object in TOML to a {@link OutputStream}. Output is encoded as UTF-8.
*
* @param from the object to be written
* @param target the OutputStream to which the TOML will be written. The stream is NOT closed after being written to.
* @throws IOException if target.write() fails
*/
public void write(Object from, OutputStream target, @Nullable File file) throws IOException
{
OutputStreamWriter writer = new OutputStreamWriter(target, "UTF-8");
write(from, writer, file);
writer.flush();
}
/**
* Write an Object in TOML to a {@link Writer}. You MUST ensure that the {@link Writer}s's encoding is set to UTF-8 for the TOML to be valid.
*
* @param from the object to be written. Can be a Map or a custom type. Must not be null.
* @param target the Writer to which TOML will be written. The Writer is not closed.
* @throws IOException if target.write() fails
* @throws IllegalArgumentException if from is of an invalid type
*/
public void write(Object from, Writer target, @Nullable File file) throws IOException
{
dev.plex.toml.ValueWriter valueWriter = WRITERS.findWriterFor(from);
if (valueWriter == MapValueWriter.MAP_VALUE_WRITER || valueWriter == dev.plex.toml.ObjectValueWriter.OBJECT_VALUE_WRITER)
{
WriterContext context = new WriterContext(indentationPolicy, datePolicy, target);
if (file != null && file.exists())
{
context.file = file;
}
valueWriter.write(from, context);
} else
{
throw new IllegalArgumentException("An object of class " + from.getClass().getSimpleName() + " cannot produce valid TOML. Please pass in a Map or a custom type.");
}
}
}

View file

@ -0,0 +1,21 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
interface ValueReader {
/**
* @param s must already have been trimmed
*/
boolean canRead(String s);
/**
* Partial validation. Stops after type terminator, rather than at EOI.
*
* @param s must already have been validated by {@link #canRead(String)}
* @param index where to start in s
* @param line current line number, used for error reporting
* @return a value or a {@link dev.plex.toml.Results.Errors}
*/
Object read(String s, AtomicInteger index, dev.plex.toml.Context context);
}

View file

@ -0,0 +1,35 @@
package dev.plex.toml;
import java.util.concurrent.atomic.AtomicInteger;
import static dev.plex.toml.ArrayValueReader.ARRAY_VALUE_READER;
import static dev.plex.toml.BooleanValueReaderWriter.BOOLEAN_VALUE_READER_WRITER;
import static dev.plex.toml.DateValueReaderWriter.DATE_VALUE_READER_WRITER;
import static dev.plex.toml.LiteralStringValueReader.LITERAL_STRING_VALUE_READER;
import static dev.plex.toml.MultilineLiteralStringValueReader.MULTILINE_LITERAL_STRING_VALUE_READER;
import static dev.plex.toml.MultilineStringValueReader.MULTILINE_STRING_VALUE_READER;
import static dev.plex.toml.StringValueReaderWriter.STRING_VALUE_READER_WRITER;
class ValueReaders {
static final ValueReaders VALUE_READERS = new ValueReaders();
Object convert(String value, AtomicInteger index, dev.plex.toml.Context context) {
String substring = value.substring(index.get());
for (dev.plex.toml.ValueReader valueParser : READERS) {
if (valueParser.canRead(substring)) {
return valueParser.read(value, index, context);
}
}
dev.plex.toml.Results.Errors errors = new dev.plex.toml.Results.Errors();
errors.invalidValue(context.identifier.getName(), substring, context.line.get());
return errors;
}
private ValueReaders() {}
private static final dev.plex.toml.ValueReader[] READERS = {
MULTILINE_STRING_VALUE_READER, MULTILINE_LITERAL_STRING_VALUE_READER, LITERAL_STRING_VALUE_READER, STRING_VALUE_READER_WRITER, DATE_VALUE_READER_WRITER, NumberValueReaderWriter.NUMBER_VALUE_READER_WRITER, BOOLEAN_VALUE_READER_WRITER, ARRAY_VALUE_READER, InlineTableValueReader.INLINE_TABLE_VALUE_READER
};
}

View file

@ -0,0 +1,9 @@
package dev.plex.toml;
interface ValueWriter {
boolean canWrite(Object value);
void write(Object value, WriterContext context);
boolean isPrimitiveType();
}

View file

@ -0,0 +1,28 @@
package dev.plex.toml;
class ValueWriters {
static final ValueWriters WRITERS = new ValueWriters();
ValueWriter findWriterFor(Object value) {
for (ValueWriter valueWriter : VALUE_WRITERS) {
if (valueWriter.canWrite(value)) {
return valueWriter;
}
}
return ObjectValueWriter.OBJECT_VALUE_WRITER;
}
private ValueWriters() {}
private static dev.plex.toml.DateValueReaderWriter getPlatformSpecificDateConverter() {
String specificationVersion = Runtime.class.getPackage().getSpecificationVersion();
return specificationVersion != null && specificationVersion.startsWith("1.6") ? dev.plex.toml.DateValueReaderWriter.DATE_PARSER_JDK_6 : dev.plex.toml.DateValueReaderWriter.DATE_VALUE_READER_WRITER;
}
private static final ValueWriter[] VALUE_WRITERS = {
StringValueReaderWriter.STRING_VALUE_READER_WRITER, NumberValueReaderWriter.NUMBER_VALUE_READER_WRITER, dev.plex.toml.BooleanValueReaderWriter.BOOLEAN_VALUE_READER_WRITER, getPlatformSpecificDateConverter(),
MapValueWriter.MAP_VALUE_WRITER, dev.plex.toml.PrimitiveArrayValueWriter.PRIMITIVE_ARRAY_VALUE_WRITER, TableArrayValueWriter.TABLE_ARRAY_VALUE_WRITER
};
}

View file

@ -0,0 +1,182 @@
package dev.plex.toml;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
class WriterContext
{
private String arrayKey = null;
private boolean isArrayOfTable = false;
private boolean empty = true;
public final String key;
private final String currentTableIndent;
private final String currentFieldIndent;
private final Writer output;
private final dev.plex.toml.IndentationPolicy indentationPolicy;
private final dev.plex.toml.DatePolicy datePolicy;
public File file;
public String parentName;
public boolean hasRun = false;
WriterContext(dev.plex.toml.IndentationPolicy indentationPolicy, dev.plex.toml.DatePolicy datePolicy, Writer output)
{
this("", "", output, indentationPolicy, datePolicy);
}
WriterContext pushTable(String newKey)
{
String newIndent = "";
if (!key.isEmpty())
{
newIndent = growIndent(indentationPolicy);
}
String fullKey = key.isEmpty() ? newKey : key + "." + newKey;
WriterContext subContext = new WriterContext(fullKey, newIndent, output, indentationPolicy, datePolicy);
if (!empty)
{
subContext.empty = false;
}
return subContext;
}
WriterContext pushTableFromArray()
{
WriterContext subContext = new WriterContext(key, currentTableIndent, output, indentationPolicy, datePolicy);
if (!empty)
{
subContext.empty = false;
}
subContext.setIsArrayOfTable(true);
return subContext;
}
WriterContext write(String s)
{
try
{
output.write(s);
if (empty && !s.isEmpty())
{
empty = false;
}
return this;
} catch (IOException e)
{
throw new RuntimeException(e);
}
}
void write(char[] chars)
{
for (char c : chars)
{
write(c);
}
}
WriterContext write(char c)
{
try
{
output.write(c);
empty = false;
return this;
} catch (IOException e)
{
throw new RuntimeException(e);
}
}
void writeKey()
{
if (key.isEmpty())
{
return;
}
if (!empty)
{
write('\n');
}
write(currentTableIndent);
if (isArrayOfTable)
{
write("[[").write(key).write("]]\n");
} else
{
write('[').write(key).write("]\n");
}
}
void writeArrayDelimiterPadding()
{
for (int i = 0; i < indentationPolicy.getArrayDelimiterPadding(); i++)
{
write(' ');
}
}
void indent()
{
if (!key.isEmpty())
{
write(currentFieldIndent);
}
}
dev.plex.toml.DatePolicy getDatePolicy()
{
return datePolicy;
}
WriterContext setIsArrayOfTable(boolean isArrayOfTable)
{
this.isArrayOfTable = isArrayOfTable;
return this;
}
WriterContext setArrayKey(String arrayKey)
{
this.arrayKey = arrayKey;
return this;
}
String getContextPath()
{
return key.isEmpty() ? arrayKey : key + "." + arrayKey;
}
private String growIndent(dev.plex.toml.IndentationPolicy indentationPolicy)
{
return currentTableIndent + fillStringWithSpaces(indentationPolicy.getTableIndent());
}
private String fillStringWithSpaces(int count)
{
char[] chars = new char[count];
Arrays.fill(chars, ' ');
return new String(chars);
}
private WriterContext(String key, String tableIndent, Writer output, dev.plex.toml.IndentationPolicy indentationPolicy, dev.plex.toml.DatePolicy datePolicy)
{
this.key = key;
this.output = output;
this.indentationPolicy = indentationPolicy;
this.currentTableIndent = tableIndent;
this.datePolicy = datePolicy;
this.currentFieldIndent = tableIndent + fillStringWithSpaces(this.indentationPolicy.getKeyValueIndent());
}
}

View file

@ -1,91 +0,0 @@
package com.moandjiezana.toml;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Map;
import java.util.TimeZone;
import org.junit.Test;
public class RealWorldTest {
@SuppressWarnings("unchecked")
@Test
public void should_parse_example() throws Exception {
Toml toml = new Toml().parse(new File(getClass().getResource("example.toml").getFile()));
// printMap(root);
assertEquals("TOML Example", toml.getString("title"));
Toml owner = toml.getKeyGroup("owner");
assertEquals("Tom Preston-Werner", owner.getString("name"));
assertEquals("GitHub", owner.getString("organization"));
assertEquals("GitHub Cofounder & CEO\nLikes tater tots and beer.", owner.getString("bio"));
Calendar dob = Calendar.getInstance();
dob.set(1979, Calendar.MAY, 27, 7, 32, 0);
dob.set(Calendar.MILLISECOND, 0);
dob.setTimeZone(TimeZone.getTimeZone("UTC"));
assertEquals(dob.getTime(), owner.getDate("dob"));
Toml database = toml.getKeyGroup("database");
assertEquals("192.168.1.1", database.getString("server"));
assertEquals(5000L, database.getLong("connection_max").longValue());
assertTrue(database.getBoolean("enabled"));
assertEquals(Arrays.asList(8001L, 8001L, 8002L), database.getList("ports", Long.class));
Toml servers = toml.getKeyGroup("servers");
Toml alphaServers = servers.getKeyGroup("alpha");
assertEquals("10.0.0.1", alphaServers.getString("ip"));
assertEquals("eqdc10", alphaServers.getString("dc"));
Toml betaServers = servers.getKeyGroup("beta");
assertEquals("10.0.0.2", betaServers.getString("ip"));
assertEquals("eqdc10", betaServers.getString("dc"));
Toml clients = toml.getKeyGroup("clients");
assertEquals(asList(asList("gamma", "delta"), asList(1L, 2L)), clients.getList("data", String.class));
assertEquals(asList("alpha", "omega"), clients.getList("hosts", String.class));
}
@Test
public void should_parse_hard_example() throws Exception {
Toml toml = new Toml().parse(new File(getClass().getResource("hard_example.toml").getFile()));
assertEquals("You'll hate me after this - #", toml.getString("the.test_string"));
assertEquals(asList("] ", " # "), toml.getList("the.hard.test_array", String.class));
assertEquals(asList("Test #11 ]proved that", "Experiment #9 was a success"), toml.getList("the.hard.test_array2", String.class));
assertEquals(" Same thing, but with a string #", toml.getString("the.hard.another_test_string"));
assertEquals(" And when \"'s are in the string, along with # \"", toml.getString("the.hard.harder_test_string"));
Toml theHardBit = toml.getKeyGroup("the.hard.bit#");
assertEquals("You don't think some user won't do that?", theHardBit.getString("what?"));
assertEquals(asList("]"), theHardBit.getList("multi_line_array", String.class));
}
@Test
public void should_allow_keys_with_same_name_in_different_groups() throws Exception {
Toml toml = new Toml().parse(new File(getClass().getResource("should_allow_keys_with_same_name_in_different_groups.toml").getFile()));
assertTrue(toml.getKeyGroup("siteInfo.local.sh").getBoolean("enable"));
assertFalse(toml.getKeyGroup("siteInfo.localMobile.sh").getBoolean("enable"));
}
@SuppressWarnings("unchecked")
private void printMap(Map<String, Object> map) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() instanceof Map) {
System.out.println("[" + entry.getKey() + "]");
printMap((Map<String, Object>) entry.getValue());
System.out.println("[/" + entry.getKey() + "]");
} else {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
}

View file

@ -1,61 +0,0 @@
package com.moandjiezana.toml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Before;
import org.junit.Test;
public class TomlDefaultsTest {
private Toml defaultToml;
@Before
public void before() {
defaultToml = new Toml().parse("a = \"a\"\n[group]\na=\"a\"");
}
@Test
public void should_fall_back_to_default_value() {
Toml toml = new Toml(defaultToml);
assertEquals("a", toml.getString("a"));
}
@Test
public void should_use_value_when_present_in_values_and_defaults() {
Toml toml = new Toml(defaultToml).parse("a = \"b\"");
assertEquals("b", toml.getString("a"));
}
@Test
public void should_return_null_when_no_defaults_for_key() throws Exception {
Toml toml = new Toml(defaultToml).parse("");
assertNull(toml.getString("b"));
}
@Test
public void should_fall_back_to_default_with_multi_key() throws Exception {
Toml toml = new Toml(defaultToml).parse("");
assertEquals("a", toml.getString("group.a"));
}
@Test
public void should_fall_back_to_key_group() throws Exception {
Toml toml = new Toml(defaultToml).parse("");
assertEquals("a", toml.getKeyGroup("group").getString("a"));
}
@Test
public void should_fall_back_to_key_within_key_group() throws Exception {
Toml toml = new Toml(defaultToml).parse("[group]\nb=1");
assertEquals(1, toml.getKeyGroup("group").getLong("b").intValue());
assertEquals("a", toml.getKeyGroup("group").getString("a"));
}
}

View file

@ -1,251 +0,0 @@
package com.moandjiezana.toml;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.util.Calendar;
import java.util.TimeZone;
import org.junit.Ignore;
import org.junit.Test;
public class TomlTest {
@Test
public void should_get_string() throws Exception {
Toml toml = new Toml().parse("a = \"a\"");
assertEquals("a", toml.getString("a"));
}
@Test
public void should_get_number() throws Exception {
Toml toml = new Toml().parse("b = 1001");
assertEquals(1001, toml.getLong("b").intValue());
}
@Test
public void should_get_negative_number() throws Exception {
Toml toml = new Toml().parse("b = -1001");
assertEquals(-1001, toml.getLong("b").intValue());
}
@Test
public void should_get_list() throws Exception {
Toml toml = new Toml().parse("list = [\"a\", \"b\", \"c\"]");
assertEquals(asList("a", "b", "c"), toml.getList("list", String.class));
}
@Test
public void should_get_boolean() throws Exception {
Toml toml = new Toml().parse("bool_false = false\nbool_true = true");
assertFalse(toml.getBoolean("bool_false"));
assertTrue(toml.getBoolean("bool_true"));
}
@Test
public void should_get_date() throws Exception {
Toml toml = new Toml().parse("a_date = 2011-11-10T13:12:00Z");
Calendar calendar = Calendar.getInstance();
calendar.set(2011, Calendar.NOVEMBER, 10, 13, 12, 00);
calendar.set(Calendar.MILLISECOND, 0);
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
assertEquals(calendar.getTime(), toml.getDate("a_date"));
}
@Test
public void should_get_double() throws Exception {
Toml toml = new Toml().parse("double = 5.25");
assertEquals(5.25D, toml.getDouble("double").doubleValue(), 0.0);
}
@Test
public void should_get_negative_double() throws Exception {
Toml toml = new Toml().parse("double = -5.25");
assertEquals(-5.25D, toml.getDouble("double").doubleValue(), 0.0);
}
@Test
public void should_get_key_group() throws Exception {
Toml toml = new Toml().parse("[group]\nkey = \"value\"");
Toml group = toml.getKeyGroup("group");
assertEquals("value", group.getString("key"));
}
@Test
public void should_get_value_for_multi_key() throws Exception {
Toml toml = new Toml().parse("[group]\nkey = \"value\"");
assertEquals("value", toml.getString("group.key"));
}
@Test
public void should_get_value_for_multi_key_with_no_parent_key_group() throws Exception {
Toml toml = new Toml().parse("[group.sub]\nkey = \"value\"");
assertEquals("value", toml.getString("group.sub.key"));
}
@Test
public void should_get_key_group_for_multi_key() throws Exception {
Toml toml = new Toml().parse("[group]\nother=1\n[group.sub]\nkey = \"value\"");
assertEquals("value", toml.getKeyGroup("group.sub").getString("key"));
}
@Test
public void should_get_key_group_for_multi_key_with_no_parent_key_group() throws Exception {
Toml toml = new Toml().parse("[group.sub]\nkey = \"value\"");
assertEquals("value", toml.getKeyGroup("group.sub").getString("key"));
}
@Test
public void should_get_value_from_key_group_with_sub_key_group() throws Exception {
Toml toml = new Toml().parse("[a.b]\nc=1\n[a]\nd=2");
assertEquals(2, toml.getLong("a.d").intValue());
assertEquals(1, toml.getKeyGroup("a.b").getLong("c").intValue());
}
@Test
public void should_return_null_if_no_value_for_key() throws Exception {
Toml toml = new Toml().parse("");
assertNull(toml.getString("a"));
}
@Test
public void should_return_null_when_no_value_for_multi_key() throws Exception {
Toml toml = new Toml().parse("");
assertNull(toml.getString("group.key"));
}
@Test
public void should_load_from_file() throws Exception {
Toml toml = new Toml().parse(new File(getClass().getResource("should_load_from_file.toml").getFile()));
assertEquals("value", toml.getString("key"));
}
@Test
public void should_support_numbers_in_key_names() throws Exception {
Toml toml = new Toml().parse("a1 = 1");
assertEquals(1, toml.getLong("a1").intValue());
}
@Test
public void should_support_numbers_in_key_group_names() throws Exception {
Toml toml = new Toml().parse("[group1]\na = 1");
assertEquals(1, toml.getLong("group1.a").intValue());
}
@Test
public void should_support_underscores_in_key_names() throws Exception {
Toml toml = new Toml().parse("a_a = 1");
assertEquals(1, toml.getLong("a_a").intValue());
}
@Test
public void should_support_question_marks_in_key_names() throws Exception {
Toml toml = new Toml().parse("key?=true");
assertTrue(toml.getBoolean("key?"));
}
@Test
public void should_support_underscores_in_key_group_names() throws Exception {
Toml toml = new Toml().parse("[group_a]\na = 1");
assertEquals(1, toml.getLong("group_a.a").intValue());
}
@Test
public void should_support_sharp_sign_in_key_group_names() throws Exception {
Toml toml = new Toml().parse("[group#]\nkey=1");
assertEquals(1, toml.getLong("group#.key").intValue());
}
@Test
public void should_support_blank_lines() throws Exception {
Toml toml = new Toml().parse(new File(getClass().getResource("should_support_blank_line.toml").getFile()));
assertEquals(1, toml.getLong("group.key").intValue());
}
@Test
public void should_support_special_characters_in_strings() {
Toml toml = new Toml().parse(new File(getClass().getResource("should_support_special_characters_in_strings.toml").getFile()));
assertEquals("\" \t \n \r \\ / \b \f", toml.getString("key"));
}
@Test
public void should_support_unicode_characters_in_strings() throws Exception {
Toml toml = new Toml().parse("key=\"\\u00B1\"");
assertEquals("±", toml.getString("key"));
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_reserved_special_character_in_strings() throws Exception {
new Toml().parse("key=\"\\m\"");
}
@Test
public void should_ignore_comma_at_end_of_array() throws Exception {
Toml toml = new Toml().parse("key=[1,2,3,]");
assertEquals(asList(1L, 2L, 3L), toml.getList("key", Long.class));
}
@Test(expected = IllegalStateException.class)
public void should_fail_when_dot_in_key_name() throws Exception {
new Toml().parse("a.a = 1");
}
@Test(expected = IllegalStateException.class)
public void should_fail_on_invalid_date() throws Exception {
new Toml().parse("d = 2012-13-01T15:00:00Z");
}
@Test(expected = IllegalStateException.class)
public void should_fail_when_key_is_overwritten_by_key_group() {
new Toml().parse("[a]\nb=1\n[a.b]\nc=2");
}
@Test(expected = IllegalStateException.class)
public void should_fail_when_key_is_overwritten_by_another_key() {
new Toml().parse("[fruit]\ntype=\"apple\"\ntype=\"orange\"");
}
@Test(expected = IllegalStateException.class)
public void should_fail_when_key_group_defined_twice() throws Exception {
new Toml().parse("[a]\nb=1\n[a]\nc=2");
}
@Ignore
@Test(expected = IllegalStateException.class)
public void should_fail_when_illegal_characters_after_key_group() throws Exception {
new Toml().parse("[error] if you didn't catch this, your parser is broken");
}
}

View file

@ -1,75 +0,0 @@
package com.moandjiezana.toml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.moandjiezana.toml.testutils.KeyGroupAsMap;
import com.moandjiezana.toml.testutils.TomlKeyGroups;
import com.moandjiezana.toml.testutils.TomlPrimitives;
import java.io.File;
import java.util.Calendar;
import java.util.TimeZone;
import org.junit.Test;
public class TomlToClassTest {
@Test
public void should_convert_primitive_values() throws Exception {
Toml toml = new Toml().parse(file("should_convert_primitive_values.toml"));
TomlPrimitives values = toml.to(TomlPrimitives.class);
Calendar calendar = Calendar.getInstance();
calendar.set(1979, Calendar.MAY, 27, 7, 32, 00);
calendar.set(Calendar.MILLISECOND, 0);
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
assertEquals("string", values.string);
assertEquals((Long) 123L, values.number);
assertEquals(2.1, values.decimal, 0);
assertTrue(values.bool);
assertEquals(calendar.getTime(), values.date);
}
@Test
public void should_convert_key_groups() throws Exception {
String fileName = "should_convert_key_groups.toml";
Toml toml = new Toml().parse(file(fileName));
TomlKeyGroups tomlKeyGroups = toml.to(TomlKeyGroups.class);
assertEquals("value1", tomlKeyGroups.group1.string);
assertEquals("value2", tomlKeyGroups.group2.string);
}
@Test
public void should_use_defaults() throws Exception {
Toml defaults = new Toml().parse(file("should_convert_key_groups.toml"));
Toml toml = new Toml(defaults).parse("");
TomlKeyGroups tomlKeyGroups = toml.to(TomlKeyGroups.class);
assertEquals("value1", tomlKeyGroups.group1.string);
assertEquals("value2", tomlKeyGroups.group2.string);
}
@Test
public void should_ignore_keys_not_in_class() throws Exception {
TomlPrimitives tomlPrimitives = new Toml().parse("a=1\nstring=\"s\"").to(TomlPrimitives.class);
assertEquals("s", tomlPrimitives.string);
}
@Test
public void should_convert_key_group_as_map() throws Exception {
KeyGroupAsMap keyGroupAsMap = new Toml().parse("[group]\nkey=\"value\"").to(KeyGroupAsMap.class);
assertEquals("value", keyGroupAsMap.group.get("key"));
}
private File file(String fileName) {
return new File(getClass().getResource(fileName).getFile());
}
}

View file

@ -1,8 +0,0 @@
package com.moandjiezana.toml.testutils;
import java.util.Map;
public class KeyGroupAsMap {
public Map<String, Object> group;
}

View file

@ -1,8 +0,0 @@
package com.moandjiezana.toml.testutils;
public class TomlKeyGroups {
public TomlPrimitives group1;
public TomlPrimitives group2;
}

View file

@ -1,13 +0,0 @@
package com.moandjiezana.toml.testutils;
import java.util.Date;
public class TomlPrimitives {
public String string;
public Long number;
public Double decimal;
public Boolean bool;
public Date date;
}

View file

@ -1,35 +0,0 @@
# This is a TOML document. Boom.
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
organization = "GitHub"
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
[servers]
# You can indent as you please. Tabs or spaces. TOML don't care.
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
[clients]
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]

View file

@ -1,33 +0,0 @@
# Test file for TOML
# Only this one tries to emulate a TOML file written by a user of the kind of parser writers probably hate
# This part you'll really hate
[the]
test_string = "You'll hate me after this - #" # " Annoying, isn't it?
[the.hard]
test_array = [ "] ", " # "] # ] There you go, parse this!
test_array2 = [ "Test #11 ]proved that", "Experiment #9 was a success" ]
# You didn't think it'd as easy as chucking out the last #, did you?
another_test_string = " Same thing, but with a string #"
harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too"
# Things will get harder
[the.hard.bit#]
what? = "You don't think some user won't do that?"
multi_line_array = [
"]",
# ] Oh yes I did
]
# Each of the following keygroups/key value pairs should produce an error. Uncomment to them to test
#[error] if you didn't catch this, your parser is broken
#string = "Anything other than tabs, spaces and newline after a keygroup or key value pair has ended should produce an error unless it is a comment" like this
#array = [
# "This might most likely happen in multiline arrays",
# Like here,
# "or here,
# and here"
# ] End of array comment, forgot the #
#number = 3.14 pi <--again forgot the #

View file

@ -1,11 +0,0 @@
# Each of the following keygroups/key value pairs should produce an error. Uncomment to them to test
[error] if you didn't catch this, your parser is broken
#string = "Anything other than tabs, spaces and newline after a keygroup or key value pair has ended should produce an error unless it is a comment" like this
#array = [
# "This might most likely happen in multiline arrays",
# Like here,
# "or here,
# and here"
# ] End of array comment, forgot the #
#number = 3.14 pi <--again forgot the #

View file

@ -1,4 +0,0 @@
[siteInfo.local.sh]
enable=true
[siteInfo.localMobile.sh]
enable=false

View file

@ -1,5 +0,0 @@
[group1]
string = "value1"
[group2]
string = "value2"

View file

@ -1,5 +0,0 @@
string = "string"
number = 123
decimal = 2.1
bool = true
date = 1979-05-27T07:32:00Z

View file

@ -1,5 +0,0 @@
# Comment
[group]
key = 1

View file

@ -1 +0,0 @@
key = "\" \t \n \r \\ \/ \b \f"