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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package it.gbresciani.jsonnode


/**
* Return an object of type [Node] for [this]
*
* @return [Node] object from a generic [Any]
*/
fun Any?.asNode(): Node = when (this) {
null -> Node.Null
is Node -> this
Expand Down
91 changes: 91 additions & 0 deletions src/main/kotlin/it/gbresciani/jsonnode/Node.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
package it.gbresciani.jsonnode

/** Representation of possible object in Json structure
*
* Possible values:
* - Null : is a node with null as value
* - Text : is a node with text type as value
* - Bool : is a node with a boolean type as value
* - Number : is a node with a number type as value
* - Array : is a node with collection of type Node as value
* - ObjectNode : is a node with a map<key: String, value: Node> as value where key
* represents the name of the field and value represents its value
*/
sealed class Node() : PrettyPrintable {
object Null : Node() {
override fun toString() = "null"
Expand Down Expand Up @@ -89,14 +100,75 @@ sealed class Node() : PrettyPrintable {

class ObjectNode(val map: Map<String, Node> = emptyMap()) : Node(), Map<String, Node> by map {

/**
* Join an arbitrary value at a specified path with [this]
*
* Note:
* + if path doesn't exists it will create new path with passed value
* + if path already exists, it will override the old value in path with new value passed
* + if path partially exists it will add just the new part of path
*
* @param value the arbitrary object that must be added
* @param at [NodePath] that indicate the position where to place the Node
* @return an [ObjectNode] containing the resultant structure of old ObjectNode with new Node added
*/
fun with(value: Any?, at: NodePath): ObjectNode = with(value.asNode(), at)

/**
* Join an arbitrary value at a specified path with [this]
*
* Note:
* + if path doesn't exists it will create new path with passed value
* + if path already exists, it will override the old value in path with new value passed
*
* @param value the arbitrary object that must be added
* @param at the single level position where to place the Node
* @return an [ObjectNode] containing the resultant structure of old ObjectNode with new Node added
*/
fun with(value: Any?, at: String) = with(value.asNode(), at = NodePath(at))


/**
* Join a [Node] at a specified path with [this]
*
* Note:
* + if path doesn't exists it will create new path with passed value
* + if path already exists, it will override the old value in path with new value passed
* + if path partially exists it will add just the new part of path
*
* @param value the new Node that must be added
* @param at [NodePath] that indicate the position where to place the Node
* @return an [ObjectNode] containing the resultant structure of old ObjectNode with new Node added
*/
fun with(value: Node, at: String) = with(value, at = NodePath(at))

/**
* Let merge a new [ObjectNode] with [this]
*
* Note:
* + if value is [null] the method will return [this]
*
* @param value the [ObjectNode] that must be merged with [this]
* @return an [ObjectNode] containing the resultant structure
*/
fun merge(value: ObjectNode?) = if (value == null)
this
else
with(value, NodePath())


/**
* Join a Node value at a specified path with [this]
*
* Note:
* + if path doesn't exists it will create new path with passed value
* + if path already exists, it will override the old value in path with new value passed
* + if path partially exists it will add just the new part of path
*
* @param value the new Node that must be added
* @param at [NodePath] that indicate the position where to place the Node
* @return an [ObjectNode] containing the resultant structure of old ObjectNode with new Node added
*/
fun with(value: Node, at: NodePath): ObjectNode {

val temp = mutableMapOf(*map.toList().toTypedArray())
Expand Down Expand Up @@ -127,8 +199,20 @@ sealed class Node() : PrettyPrintable {
return ObjectNode(temp)
}

/**
* Return, if exists, the [Node] at specified path otherwise return null
*
* @param path collection of Strings that compose the path to find
* @return [Node] at specified path
*/
fun getNode(vararg path: String) = getNode(NodePath(*path))

/**
* Return, if exists, the [Node] at specified path otherwise return null
*
* @param path [NodePath] to find
* @return [Node] at specified path
*/
fun getNode(path: NodePath): Node? {
val pathSize = path.size()
if (pathSize == 0) return null
Expand All @@ -143,6 +227,13 @@ sealed class Node() : PrettyPrintable {
else return node.getNode(tailPath)
}

/**
* Return a new [ObjectNode] created from values present in [this] at specified
* paths
*
* @param paths arbitrary number of [NodePath] to find and retrieve
* @return new [ObjectNode] created
*/
fun extract(vararg paths: NodePath) = paths.fold(Node.ObjectNode()) { acc, path ->
acc.with(getNode(path), at = path)
}
Expand Down
21 changes: 21 additions & 0 deletions src/main/kotlin/it/gbresciani/jsonnode/NodePath.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ package it.gbresciani.jsonnode

import java.util.*

/**
* Representation of a path in json structure
*
* @param path an arbitrary sequence of string that indicate several levels in json structure
*/
class NodePath(private vararg val path: String) {
constructor(path: List<String>) : this(*path.toTypedArray())

Expand All @@ -14,8 +19,24 @@ class NodePath(private vararg val path: String) {
}

override fun hashCode() = Arrays.hashCode(path)

/**
* Return first element of the path
*/
fun head() = path.first()

/**
* Return all the path except the first element
*/
fun tailPath() = NodePath(path.drop(1))

/**
* Return number of single elements in path
*/
fun size() = path.size

/**
* Return all the path as List
*/
fun asList() = path.toList()
}