init
This commit is contained in:
22
src/main/java/net/sbyrne/fig/FigModel.kt
Normal file
22
src/main/java/net/sbyrne/fig/FigModel.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package net.sbyrne.fig
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
sealed class FigValue {}
|
||||
|
||||
data class FigList( val list:MutableList<FigValue?> = mutableListOf() ): FigValue()
|
||||
|
||||
data class FigMap( val name:String? = null, val map:MutableMap<String?,FigValue?> = mutableMapOf() ): FigValue()
|
||||
|
||||
data class FigBoolean(val value:Boolean): FigValue() {
|
||||
companion object {
|
||||
val TRUE = FigBoolean(true)
|
||||
val FALSE = FigBoolean(false)
|
||||
}
|
||||
}
|
||||
|
||||
data class FigString( val value:String ): FigValue()
|
||||
|
||||
data class FigNumber(val value:BigDecimal): FigValue() {
|
||||
constructor(value:String) : this(BigDecimal(value))
|
||||
}
|
||||
424
src/main/java/net/sbyrne/fig/FigParser.kt
Normal file
424
src/main/java/net/sbyrne/fig/FigParser.kt
Normal file
@@ -0,0 +1,424 @@
|
||||
package net.sbyrne.fig
|
||||
|
||||
import java.io.File
|
||||
|
||||
import org.parboiled.BaseParser
|
||||
import org.parboiled.Parboiled
|
||||
import org.parboiled.Rule
|
||||
import org.parboiled.annotations.BuildParseTree
|
||||
import org.parboiled.errors.ErrorUtils
|
||||
import org.parboiled.matchers.Matcher
|
||||
import org.parboiled.matchers.TestMatcher
|
||||
import org.parboiled.parserunners.TracingParseRunner
|
||||
import org.parboiled.support.ParseTreeUtils
|
||||
|
||||
object FigParser {
|
||||
|
||||
fun parse(file: File): FigValue = parse(file.readText())
|
||||
|
||||
fun parse(text: String): FigValue {
|
||||
println("====== parse <<<${text}>>>")
|
||||
val parser = Parboiled.createParser(FigPegParser::class.java)
|
||||
//val runner = ReportingParseRunner<FigValue>(parser.FigRule())
|
||||
val runner = TracingParseRunner<Any?>(parser.FigRule())
|
||||
val result = runner.run(text)
|
||||
if (result.hasErrors()) {
|
||||
throw Exception(ErrorUtils.printParseError(result.parseErrors.first()))
|
||||
}
|
||||
println("-----[ ${text} ]-----")
|
||||
println(ParseTreeUtils.printNodeTree(result))
|
||||
println("--------")
|
||||
val value = result.valueStack.pop() as FigValue
|
||||
if ( ! result.valueStack.isEmpty ) throw Exception( "Unexpected stuff on stack: ${result.valueStack.joinToString(",")}" )
|
||||
return value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@BuildParseTree
|
||||
open class FigPegParser: BaseParser<Any>() {
|
||||
|
||||
companion object {
|
||||
const val WHITESPACE =
|
||||
"\u0009" + // CHARACTER TABULATION (\t)
|
||||
"\u000A" + // LINE FEED (\n)
|
||||
"\u000B" + // LINE TABULATION
|
||||
"\u000C" + // FORM FEED
|
||||
"\u000D" + // CARRIAGE RETURN (\r)
|
||||
"\u001C" + // INFORMATION SEPARATOR FOUR
|
||||
"\u001D" + // INFORMATION SEPARATOR THREE
|
||||
"\u001E" + // INFORMATION SEPARATOR TWO
|
||||
"\u001F" + // INFORMATION SEPARATOR ONE
|
||||
"\u0020" + // SPACE
|
||||
"\u00A0" + // NO-BREAK SPACE
|
||||
"\u1680" + // OGHAM SPACE MARK
|
||||
"\u2000" + // EN QUAD
|
||||
"\u2001" + // EM QUAD
|
||||
"\u2002" + // EN SPACE
|
||||
"\u2003" + // EM SPACE
|
||||
"\u2004" + // THREE-PER-EM SPACE
|
||||
"\u2005" + // FOUR-PER-EM SPACE
|
||||
"\u2006" + // SIX-PER-EM SPACE
|
||||
"\u2007" + // FIGURE SPACE
|
||||
"\u2008" + // PUNCTUATION SPACE
|
||||
"\u2009" + // THIN SPACE
|
||||
"\u200A" + // HAIR SPACE
|
||||
"\u202F" + // NARROW NO-BREAK SPACE
|
||||
"\u205F" + // MEDIUM MATHEMATICAL SPACE
|
||||
"\u3000" + // IDEOGRAPHIC SPACE
|
||||
"\u2028" + // LINE SEPARATOR
|
||||
"\u2029" // PARAGRAPH SEPARATOR
|
||||
}
|
||||
|
||||
// pushes FigList or FigMap to stack
|
||||
open fun FigRule(): Rule {
|
||||
return FirstOf(
|
||||
Sequence(
|
||||
WhitespaceRule(),
|
||||
FigListRule(), // pushes FigList
|
||||
WhitespaceRule(),
|
||||
EOI.skipNode()
|
||||
),
|
||||
Sequence(
|
||||
WhitespaceRule(),
|
||||
FigMapRule(), // pushes FigMap
|
||||
WhitespaceRule(),
|
||||
EOI.skipNode()
|
||||
),
|
||||
Sequence(
|
||||
WhitespaceRule(),
|
||||
ImpliedFigListRule(), // pushes FigList
|
||||
WhitespaceRule(),
|
||||
EOI.skipNode()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
open fun WhitespaceTerminatorOrEndRule(terminators:String?): Rule {
|
||||
return FirstOf(
|
||||
AnyOf("${terminators?:""}${WHITESPACE}"),
|
||||
CommentRule(),
|
||||
EOI
|
||||
)
|
||||
}
|
||||
|
||||
open fun CommentRule(): Rule {
|
||||
return Sequence(
|
||||
"<",
|
||||
ZeroOrMore(
|
||||
Sequence(
|
||||
TestNot( ">" ),
|
||||
ANY
|
||||
)
|
||||
),
|
||||
">"
|
||||
)
|
||||
}
|
||||
|
||||
open fun TestWhitespaceTerminatorOrEnd(terminators:String?): Matcher {
|
||||
return TestMatcher(
|
||||
WhitespaceTerminatorOrEndRule(terminators)
|
||||
)
|
||||
}
|
||||
|
||||
open fun WhitespaceRule(): Rule {
|
||||
return ZeroOrMore(
|
||||
FirstOf(
|
||||
AnyOf(WHITESPACE),
|
||||
CommentRule()
|
||||
)
|
||||
).suppressNode()
|
||||
}
|
||||
|
||||
// pushes FigList
|
||||
open fun FigListRule(): Rule {
|
||||
return Sequence(
|
||||
AnyOf("[").skipNode(),
|
||||
FigListContentRule("]"), // pushes FigList
|
||||
FirstOf(
|
||||
AnyOf("]"),
|
||||
EOI
|
||||
).suppressNode()
|
||||
).skipNode()
|
||||
}
|
||||
|
||||
// pushes FigList
|
||||
open fun ImpliedFigListRule(): Rule {
|
||||
return FigListContentRule(null) // pushes FigList
|
||||
}
|
||||
|
||||
// pushes FigList
|
||||
open fun FigListContentRule(terminators:String?): Rule {
|
||||
return Sequence(
|
||||
push(FigList()),
|
||||
ZeroOrMore(
|
||||
Sequence(
|
||||
WhitespaceRule(),
|
||||
ValueRule(terminators), // pushes FigValue?
|
||||
addFigListValue(), // adds FigValue? on top of stack to FigList at next position on stack
|
||||
WhitespaceRule()
|
||||
).skipNode()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
open fun addFigListValue(): Boolean {
|
||||
( peek(1) as FigList ).list.add( pop() as FigValue? )
|
||||
return true
|
||||
}
|
||||
|
||||
// pushes FigMap
|
||||
open fun FigMapRule(): Rule {
|
||||
return Sequence(
|
||||
String("{").skipNode(),
|
||||
FirstOf(
|
||||
Sequence(
|
||||
FigMapIdentifierSegmentRule(), // pushes String
|
||||
push(FigMap(name=pop() as String))
|
||||
),
|
||||
push(FigMap())
|
||||
).skipNode(),
|
||||
FigMapContentRule(), // adds k/v to map on stack
|
||||
FirstOf(
|
||||
String("}"),
|
||||
EOI
|
||||
).suppressNode()
|
||||
)
|
||||
}
|
||||
|
||||
// pushes String
|
||||
open fun FigMapIdentifierSegmentRule(): Rule {
|
||||
return Sequence(
|
||||
String("%").skipNode(),
|
||||
FigMapIdentifierRule(), // pushes String
|
||||
TestWhitespaceTerminatorOrEnd("}")
|
||||
).skipNode()
|
||||
}
|
||||
|
||||
// pushes String
|
||||
open fun FigMapIdentifierRule(): Rule {
|
||||
return Sequence(
|
||||
ZeroOrMore(
|
||||
Sequence(
|
||||
TestNot(
|
||||
WhitespaceTerminatorOrEndRule("}")
|
||||
),
|
||||
ANY
|
||||
)
|
||||
),
|
||||
push(match())
|
||||
).suppressSubnodes()
|
||||
}
|
||||
|
||||
// adds k/v's to FigMap on stack
|
||||
open fun FigMapContentRule(): Rule {
|
||||
return ZeroOrMore(
|
||||
Sequence(
|
||||
WhitespaceRule(),
|
||||
FigMapKeyValueRule(), // adds k/v to FigMap on stack
|
||||
TestWhitespaceTerminatorOrEnd("}"),
|
||||
WhitespaceRule()
|
||||
).skipNode()
|
||||
).skipNode()
|
||||
}
|
||||
|
||||
// adds k/v to FigMap on stack
|
||||
open fun FigMapKeyValueRule(): Rule {
|
||||
return Sequence(
|
||||
FirstOf(
|
||||
FigMapKeyWithValueRule(),
|
||||
FigMapValueWithoutKeyRule(),
|
||||
FigMapKeyWithoutValueRule()
|
||||
),
|
||||
addEntryToMap()
|
||||
).skipNode()
|
||||
}
|
||||
|
||||
data class FigMapKey(val key:FigString?)
|
||||
data class FigMapValue(val value:FigValue?)
|
||||
|
||||
open fun addEntryToMap(): Boolean {
|
||||
var key:FigMapKey? = null
|
||||
var value:FigMapValue? = null
|
||||
while ( peek() !is FigMap ) {
|
||||
val obj = pop()
|
||||
when ( obj ) {
|
||||
is FigMapKey -> key = obj
|
||||
is FigMapValue -> value = obj
|
||||
else -> throw Exception("Unexpected ${obj}" )
|
||||
}
|
||||
}
|
||||
( peek() as FigMap ).map[key?.key?.value] = value?.value
|
||||
return true
|
||||
}
|
||||
|
||||
open fun FigMapKeyWithValueRule(): Rule {
|
||||
return Sequence(
|
||||
StringLiteralRule(":}"),
|
||||
push( FigMapKey( pop() as FigString ) ),
|
||||
WhitespaceRule(),
|
||||
String(":").suppressNode(),
|
||||
WhitespaceRule(),
|
||||
Optional(
|
||||
Sequence(
|
||||
ValueRule("}"),
|
||||
push( FigMapValue( pop() as FigValue? ) )
|
||||
)
|
||||
).skipNode()
|
||||
)
|
||||
}
|
||||
|
||||
open fun FigMapValueWithoutKeyRule(): Rule {
|
||||
return Sequence(
|
||||
WhitespaceRule(),
|
||||
String(":").suppressNode(),
|
||||
WhitespaceRule(),
|
||||
ValueRule("}"),
|
||||
push( FigMapValue( pop() as FigValue? ) )
|
||||
)
|
||||
}
|
||||
|
||||
open fun FigMapKeyWithoutValueRule(): Rule {
|
||||
return Sequence(
|
||||
StringLiteralRule(":}"),
|
||||
push( FigMapKey( pop() as FigString ) )
|
||||
)
|
||||
}
|
||||
|
||||
// pushes FigValue? to stack
|
||||
open fun ValueRule(terminators:String?): Rule {
|
||||
return FirstOf(
|
||||
NullLiteralRule(terminators), // pushes null to stack
|
||||
BooleanLiteralRule(terminators), // pushes FigBoolean to stack
|
||||
NumericLiteralRule(terminators), // pushes FigNumber to stack
|
||||
FigListRule(), // pushes FigList to stack
|
||||
FigMapRule(), // pushes FigMap to stack
|
||||
StringLiteralRule(terminators) // pushes FigString to map
|
||||
).skipNode()
|
||||
}
|
||||
|
||||
// pushes null
|
||||
open fun NullLiteralRule(terminators:String?): Rule {
|
||||
return Sequence(
|
||||
String( "null" ),
|
||||
TestWhitespaceTerminatorOrEnd(terminators),
|
||||
push(null)
|
||||
).suppressSubnodes()
|
||||
}
|
||||
|
||||
// pushes FigBoolean
|
||||
open fun BooleanLiteralRule(terminators:String?): Rule {
|
||||
return Sequence(
|
||||
FirstOf(
|
||||
Sequence(
|
||||
"true",
|
||||
push( FigBoolean.TRUE )
|
||||
),
|
||||
Sequence(
|
||||
"false",
|
||||
push( FigBoolean.FALSE )
|
||||
)
|
||||
),
|
||||
TestWhitespaceTerminatorOrEnd(terminators)
|
||||
).suppressSubnodes()
|
||||
}
|
||||
|
||||
// pushes FigNumber
|
||||
open fun NumericLiteralRule(terminators:String?): Rule {
|
||||
return Sequence(
|
||||
Sequence(
|
||||
Optional(
|
||||
AnyOf( "+-" )
|
||||
),
|
||||
OneOrMore(DigitRule()),
|
||||
Optional(
|
||||
Sequence(
|
||||
".",
|
||||
OneOrMore(DigitRule())
|
||||
)
|
||||
),
|
||||
Optional(
|
||||
"E",
|
||||
Optional(
|
||||
AnyOf("+-")
|
||||
),
|
||||
OneOrMore(
|
||||
DigitRule()
|
||||
)
|
||||
),
|
||||
TestWhitespaceTerminatorOrEnd(terminators)
|
||||
),
|
||||
push( FigNumber(match() ) )
|
||||
).suppressSubnodes()
|
||||
}
|
||||
|
||||
open fun DigitRule(): Rule {
|
||||
return AnyOf( "0123456789" )
|
||||
}
|
||||
|
||||
// pushes FigString
|
||||
open fun StringLiteralRule( terminators:String? ): Rule {
|
||||
return FirstOf(
|
||||
Sequence(
|
||||
"\"",
|
||||
TextOrEmptyRule(charsRequiringEscape = "\""),
|
||||
push( FigString( match() ) ),
|
||||
"\""
|
||||
),
|
||||
Sequence(
|
||||
TextRule(charsRequiringEscape = "${WHITESPACE}${terminators?:""}"),
|
||||
push( FigString( match() ) ),
|
||||
TestWhitespaceTerminatorOrEnd(terminators)
|
||||
)
|
||||
).suppressSubnodes()
|
||||
}
|
||||
|
||||
/**
|
||||
* A text string where the specified characters and backslash must be escaped with a backslash.
|
||||
* The text can be empty.
|
||||
*
|
||||
* @param charsRequiringEscape
|
||||
*/
|
||||
open fun TextOrEmptyRule(charsRequiringEscape: String): Rule {
|
||||
return ZeroOrMore(
|
||||
FirstOf(
|
||||
Sequence(
|
||||
'\\',
|
||||
AnyOf(charsRequiringEscape + '\\')
|
||||
),
|
||||
OneOrMore(
|
||||
Sequence(
|
||||
TestNot(
|
||||
AnyOf(charsRequiringEscape + '\\')
|
||||
),
|
||||
ANY
|
||||
)
|
||||
).suppressSubnodes()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A text string where the specified characters and backslash must be escaped with a backslash.
|
||||
* The text can be empty.
|
||||
*
|
||||
* @param charsRequiringEscape
|
||||
*/
|
||||
open fun TextRule(charsRequiringEscape: String): Rule {
|
||||
return OneOrMore(
|
||||
FirstOf(
|
||||
Sequence(
|
||||
'\\',
|
||||
AnyOf(charsRequiringEscape + '\\')
|
||||
),
|
||||
OneOrMore(
|
||||
Sequence(
|
||||
TestNot(AnyOf(charsRequiringEscape + '\\')),
|
||||
ANY
|
||||
)
|
||||
).suppressSubnodes()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
31
src/main/java/net/sbyrne/fig/Jackson.kt
Normal file
31
src/main/java/net/sbyrne/fig/Jackson.kt
Normal file
@@ -0,0 +1,31 @@
|
||||
package net.sbyrne.fig
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.databind.node.*
|
||||
|
||||
fun FigValue?.toJsonNode(): JsonNode =
|
||||
when(this) {
|
||||
null -> NullNode.instance
|
||||
is FigMap -> ObjectNode(
|
||||
JsonNodeFactory.instance,
|
||||
map
|
||||
.asSequence()
|
||||
.map { it.key to it.value.toJsonNode() }
|
||||
.let { m ->
|
||||
if ( name != null ) {
|
||||
// TODO a way to configure the attribute name. Or get rid of name?
|
||||
m.plus( "@type" to TextNode(name) )
|
||||
} else {
|
||||
m
|
||||
}
|
||||
}
|
||||
.toMap()
|
||||
)
|
||||
is FigList -> ArrayNode(
|
||||
JsonNodeFactory.instance,
|
||||
list.map { it.toJsonNode() }
|
||||
)
|
||||
is FigNumber -> DecimalNode(value)
|
||||
is FigString -> TextNode(value)
|
||||
is FigBoolean -> if ( value ) BooleanNode.TRUE else BooleanNode.FALSE
|
||||
}
|
||||
242
src/test/java/net/sbyrne/fig/FigParserTest.kt
Normal file
242
src/test/java/net/sbyrne/fig/FigParserTest.kt
Normal file
@@ -0,0 +1,242 @@
|
||||
package net.sbyrne.fig
|
||||
|
||||
import java.math.BigDecimal
|
||||
import kotlin.test.*
|
||||
|
||||
class FigParserTest {
|
||||
|
||||
fun test(input:String,ref:FigValue?=null) {
|
||||
val result = FigParser.parse(input)
|
||||
println( result )
|
||||
if ( ref != null ) {
|
||||
assertEquals(ref,result)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listOfNull() = test(
|
||||
"[null]",
|
||||
FigList(mutableListOf(null))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun listOfNullSpaced() = test(
|
||||
"[ null ]",
|
||||
FigList(mutableListOf(null))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun listOfNullAndBooleans() = test(
|
||||
" [null true false null] ",
|
||||
FigList(mutableListOf(
|
||||
null,
|
||||
FigBoolean(true),
|
||||
FigBoolean(false),
|
||||
null
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun implicitListOfNull() = test(
|
||||
"null",
|
||||
FigList(mutableListOf(null))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun implicitListofNullSpaced() = test(
|
||||
" null ",
|
||||
FigList(mutableListOf(null))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun implicitListOfNullAndBooleans() = test(
|
||||
" null true false null ",
|
||||
FigList(mutableListOf(
|
||||
null,
|
||||
FigBoolean(true),
|
||||
FigBoolean(false),
|
||||
null
|
||||
) )
|
||||
)
|
||||
|
||||
@Test
|
||||
fun stringStartingWithNumber() = test(
|
||||
"5a",
|
||||
FigList(mutableListOf(
|
||||
FigString("5a")
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testNumber() = test(
|
||||
"1",
|
||||
FigList(mutableListOf(
|
||||
FigNumber(BigDecimal("1"))
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun listOfNumbers() = test(
|
||||
"[ 1 2.5 -3.5 -4 1E2 -1E2 -1.0E4 -2.3E-23 5E-2 5 ]",
|
||||
FigList(mutableListOf(
|
||||
FigNumber("1"),
|
||||
FigNumber("2.5"),
|
||||
FigNumber("-3.5"),
|
||||
FigNumber("-4"),
|
||||
FigNumber("1E2"),
|
||||
FigNumber("-1E2"),
|
||||
FigNumber("-1.0E4"),
|
||||
FigNumber("-2.3E-23"),
|
||||
FigNumber("5E-2"),
|
||||
FigNumber("5")
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun escapeQuotedString() = test(
|
||||
""""a\ b""",
|
||||
FigList(mutableListOf(
|
||||
FigString("a b")
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun escapeUnquotedString() = test(
|
||||
"""a\ b""",
|
||||
FigList(mutableListOf(
|
||||
FigString("a b")
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun listOfLiteralValues() = test(
|
||||
"""[ true null abc "A B" x ]""",
|
||||
FigList(mutableListOf(
|
||||
FigBoolean(true),
|
||||
null,
|
||||
FigString("abc"),
|
||||
FigString("A B"),
|
||||
FigString("x"),
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun listWithSublists() = test(
|
||||
""" [ [ null ] abc [ "hello world" whatever ] xx] """,
|
||||
FigList(mutableListOf(
|
||||
FigList(mutableListOf(null)),
|
||||
FigString("abc"),
|
||||
FigList(mutableListOf(
|
||||
FigString("hello world"),
|
||||
FigString("whatever")
|
||||
)),
|
||||
FigString("xx")
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun mapWithNullKeysAndMissingValues() = test(
|
||||
"""{ a:b c:d :e f }""",
|
||||
FigMap(map=mutableMapOf(
|
||||
"a" to FigString("b"),
|
||||
"c" to FigString("d"),
|
||||
null to FigString("e"),
|
||||
"f" to null
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun mapWithNullKeysAndNoValues() = test(
|
||||
"""{ a:b c:d :e f: }""",
|
||||
FigMap(map= mutableMapOf(
|
||||
"a" to FigString("b"),
|
||||
"c" to FigString("d"),
|
||||
null to FigString("e"),
|
||||
"f" to null
|
||||
) )
|
||||
)
|
||||
|
||||
@Test
|
||||
fun namedMap() = test(
|
||||
"""{%abc a:b c:d :e f: }""",
|
||||
FigMap("abc", mutableMapOf(
|
||||
"a" to FigString("b"),
|
||||
"c" to FigString("d"),
|
||||
null to FigString("e"),
|
||||
"f" to null
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun mapSpaces() = test(
|
||||
"""{%abc a : b c : d : e f : }""",
|
||||
FigMap("abc", mutableMapOf(
|
||||
"a" to FigString("b"),
|
||||
"c" to FigString("d"),
|
||||
null to FigString("e"),
|
||||
"f" to null
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun namedMapWithSubObjects() = test(
|
||||
"""{%abc a:[b c] c:{%bar x:y} :e f: }""",
|
||||
FigMap("abc", mutableMapOf(
|
||||
"a" to FigList(mutableListOf(FigString("b"),FigString("c"))),
|
||||
"c" to FigMap("bar", mutableMapOf("x" to FigString("y"))),
|
||||
null to FigString("e"),
|
||||
"f" to null
|
||||
) )
|
||||
)
|
||||
|
||||
@Test
|
||||
fun trailingEnds() = test(
|
||||
"""{%abc a:{ A:one B:two C:[ a ] } }""",
|
||||
FigMap("abc",mutableMapOf(
|
||||
"a" to FigMap(map= mutableMapOf(
|
||||
"A" to FigString("one"),
|
||||
"B" to FigString("two"),
|
||||
"C" to FigList(mutableListOf(
|
||||
FigString("a")
|
||||
))
|
||||
))
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun missingTrailingEnds() = test(
|
||||
"""{%abc a:{ A:one B:two C:[ a""",
|
||||
FigMap("abc",mutableMapOf(
|
||||
"a" to FigMap(map= mutableMapOf(
|
||||
"A" to FigString("one"),
|
||||
"B" to FigString("two"),
|
||||
"C" to FigList(mutableListOf(
|
||||
FigString("a")
|
||||
))
|
||||
))
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun implicitListStartMap() = test(
|
||||
"""{a:b} c""",
|
||||
FigList(mutableListOf(
|
||||
FigMap(map= mutableMapOf("a" to FigString("b"))),
|
||||
FigString("c")
|
||||
) )
|
||||
)
|
||||
|
||||
@Test
|
||||
fun implicitListStartList() = test(
|
||||
"""[a b] c""",
|
||||
FigList(mutableListOf(
|
||||
FigList(mutableListOf(FigString("a"),FigString("b"))),
|
||||
FigString("c")
|
||||
))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun comments() = test("""<comm ment>{%abc<comment a>a:{<second comment>A:1 B:2 C:[ a ] } }<comment> <another comment>""")
|
||||
|
||||
}
|
||||
|
||||
92
src/test/java/net/sbyrne/fig/JacksonTest.kt
Normal file
92
src/test/java/net/sbyrne/fig/JacksonTest.kt
Normal file
@@ -0,0 +1,92 @@
|
||||
package net.sbyrne.fig
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
||||
import com.fasterxml.jackson.core.Version
|
||||
import com.fasterxml.jackson.databind.Module
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
|
||||
import org.junit.Test
|
||||
|
||||
class JacksonTest {
|
||||
|
||||
val objectMapper = ObjectMapper()
|
||||
.registerModule(ModelModule)
|
||||
.registerKotlinModule()
|
||||
|
||||
@Test
|
||||
fun test() {
|
||||
val jsonNode = FigParser
|
||||
.parse("""{ title:"my group" description:"blah blah blah" attendees:[ { @type:Robot id:1 } { @type:Person name:"John Doe" email:jdoe@example.com department:TECH } ]""")
|
||||
.toJsonNode()
|
||||
println( jsonNode )
|
||||
val group = objectMapper.reader().treeToValue(jsonNode,Group::class.java)
|
||||
println( group )
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testName() {
|
||||
val jsonNode = FigParser
|
||||
.parse("""{ title:"my group" description:"blah blah blah" attendees:[ {%Robot id:1} {%Person name:"John Doe" email:jdoe@example.com department:TECH} ]""")
|
||||
.toJsonNode()
|
||||
println( jsonNode )
|
||||
val group = objectMapper.reader().treeToValue(jsonNode,Group::class.java)
|
||||
println( group )
|
||||
}
|
||||
}
|
||||
|
||||
data class Group (
|
||||
val title:String,
|
||||
val description:String,
|
||||
val attendees:List<Attendee>
|
||||
)
|
||||
|
||||
interface Attendee
|
||||
|
||||
data class Robot(
|
||||
val id:Long
|
||||
) : Attendee
|
||||
|
||||
data class Person(
|
||||
val name:String,
|
||||
val department:Department,
|
||||
val email:String
|
||||
) : Attendee
|
||||
|
||||
enum class Department {
|
||||
TECH,
|
||||
RESEARCH
|
||||
}
|
||||
|
||||
object ModelModule: Module() {
|
||||
override fun getModuleName():String = "Kotlunch Model Module"
|
||||
override fun version(): Version = Version.unknownVersion()
|
||||
|
||||
override fun setupModule(context:SetupContext) {
|
||||
context.setMixInAnnotations( Attendee::class.java, AttendeeMixin::class.java )
|
||||
context.setMixInAnnotations( Robot::class.java, RobotMixin::class.java )
|
||||
context.setMixInAnnotations( Person::class.java, PersonMixin::class.java )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JsonTypeInfo(
|
||||
use = JsonTypeInfo.Id.NAME,
|
||||
include = JsonTypeInfo.As.PROPERTY,
|
||||
property = "@type"
|
||||
)
|
||||
@JsonSubTypes(
|
||||
JsonSubTypes.Type(value=Person::class,name="Person"),
|
||||
JsonSubTypes.Type(value=Robot::class,name="Robot")
|
||||
)
|
||||
abstract class AttendeeMixin
|
||||
|
||||
class RobotMixin {
|
||||
val id:Long? = null
|
||||
}
|
||||
|
||||
class PersonMixin {
|
||||
val name:String? = null
|
||||
val department:Department? = null
|
||||
val email:String? = null
|
||||
}
|
||||
Reference in New Issue
Block a user