Merge: Apply serialization in calculator, a_star, more_collections, Couple and Container
authorJean Privat <jean@pryen.org>
Tue, 26 May 2015 10:09:51 +0000 (06:09 -0400)
committerJean Privat <jean@pryen.org>
Tue, 26 May 2015 10:09:51 +0000 (06:09 -0400)
Use `auto_serialize` in the calculator example, and remove the old custom JSON serialization.

The `a_star` module needed special care and could not rely only on `auto_serialize`. This was to avoid serializing graphs as a deep tree, instead we serialize the nodes first, then the links and we rebuild the graph at deserialization.

Besides that, more collections support the serialization.

Pull-Request: #1364
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

examples/calculator/src/calculator.nit
examples/calculator/src/calculator_logic.nit
lib/a_star.nit
lib/more_collections.nit
lib/serialization/serialization.nit
tests/sav/nitserial_args1.res

index 2e2f437..6ed8be7 100644 (file)
@@ -96,7 +96,7 @@ class CalculatorWindow
 
        redef fun on_save_state
        do
-               app.data_store["context"] = context.to_json
+               app.data_store["context"] = context
                super
        end
 
@@ -104,11 +104,10 @@ class CalculatorWindow
        do
                super
 
-               var save = app.data_store["context"]
-               if save == null then return
-               assert save isa String
+               var context = app.data_store["context"]
+               if not context isa CalculatorContext then return
 
-               self.context = new CalculatorContext.from_json(save)
+               self.context = context
                display.text = context.display_text
        end
 end
index be55201..ea45a99 100644 (file)
 # Business logic of a calculator
 module calculator_logic
 
-import json::dynamic
+import serialization
 
 # Hold the state of the calculator and its services
 class CalculatorContext
+       auto_serializable
+
        # Result of the last operation
        var result: nullable Numeric = null
 
@@ -120,52 +122,6 @@ class CalculatorContext
                self.result = result
                self.current = null
        end
-
-       # Serialize calculator state to Json
-       fun to_json: String
-       do
-               # Do not save NaN nor inf
-               var result = self.result
-               if result != null and (result.to_f.is_nan or result.to_f.is_inf != 0) then result = null
-
-               var self_last_op = self.last_op
-               var last_op
-               if self_last_op == null then
-                       last_op = "null"
-               else last_op = "\"{self_last_op}\""
-
-               var self_current = self.current
-               var current
-               if self_current == null then
-                       current = "null"
-               else current = "\"{self_current}\""
-
-               return """
-{
-       "result": {{{result or else "null"}}},
-       "last_op": {{{last_op}}},
-       "current": {{{current}}}
-}"""
-       end
-
-       # Load calculator state from Json
-       init from_json(json_string: String)
-       do
-               var json = json_string.to_json_value
-               if json.is_error then
-                       print "Loading state failed: {json.to_error}"
-                       return
-               end
-
-               var result = json["result"]
-               if result.is_numeric then self.result = result.to_numeric
-
-               var last_op = json["last_op"]
-               if last_op.is_string then self.last_op = last_op.to_s.chars.first
-
-               var current = json["current"]
-               if current.is_string then self.current = new FlatBuffer.from(current.to_s)
-       end
 end
 
 redef universal Float
index 9735d90..a0b1b48 100644 (file)
 # ~~~
 module a_star
 
+import serialization
+
 # General graph node
 class Node
+       super Serializable
+
        # Type of the others nodes in the `graph`
        type N: Node
 
@@ -183,10 +187,32 @@ class Node
                        end
                end
        end
+
+       # We customize the serialization process to avoid problems with recursive
+       # serialization engines. These engines, such as `JsonSerializer`,
+       # are at danger to serialize the graph as a very deep tree.
+       # With a large graph it can cause a stack overflow.
+       #
+       # Instead, we serialize the nodes first and then the links.
+       redef fun core_serialize_to(serializer: Serializer)
+       do
+               serializer.serialize_attribute("graph", graph)
+       end
+
+       redef init from_deserializer(deserializer)
+       do
+               deserializer.notify_of_creation self
+
+               var graph = deserializer.deserialize_attribute("graph")
+               assert graph isa Graph[N, Link]
+               self.graph = graph
+       end
 end
 
 # Link between two nodes and associated to a graph
 class Link
+       auto_serializable
+
        # Type of the nodes in `graph`
        type N: Node
 
@@ -210,6 +236,8 @@ end
 
 # General graph
 class Graph[N: Node, L: Link]
+       super Serializable
+
        # Nodes in this graph
        var nodes: Set[N] = new HashSet[N]
 
@@ -236,15 +264,35 @@ class Graph[N: Node, L: Link]
 
        # Used to check if nodes have been searched in one pathfinding
        private var pathfinding_current_evocation: Int = 0
+
+       redef fun core_serialize_to(serializer: Serializer)
+       do
+               serializer.serialize_attribute("nodes", nodes)
+               serializer.serialize_attribute("links", links)
+       end
+
+       redef init from_deserializer(deserializer)
+       do
+               deserializer.notify_of_creation self
+
+               var nodes = deserializer.deserialize_attribute("nodes")
+               assert nodes isa HashSet[N]
+               self.nodes = nodes
+
+               var links = deserializer.deserialize_attribute("links")
+               assert links isa HashSet[L]
+               for link in links do add_link link
+       end
 end
 
 # Result from path finding and a walkable path
 class AStarPath[N]
+       auto_serializable
 
-       # The total cost of this path
+       # Total cost of this path
        var total_cost: Int
 
-       # The list of nodes composing this path
+       # Nodes composing this path
        var nodes = new List[N]
 
        private var at: Int = 0
@@ -269,6 +317,8 @@ end
 
 # Context related to an evocation of pathfinding
 class PathContext
+       auto_serializable
+
        # Type of the nodes in `graph`
        type N: Node
 
@@ -302,6 +352,7 @@ end
 # Warning: A* is not optimize for such a case
 class ConstantPathContext
        super PathContext
+       auto_serializable
 
        redef fun worst_cost do return 1
        redef fun cost(l) do return 1
@@ -313,6 +364,7 @@ end
 # A `PathContext` for graphs with `WeightedLink`
 class WeightedPathContext
        super PathContext
+       auto_serializable
 
        redef type L: WeightedLink
 
@@ -341,6 +393,7 @@ end
 # A `Link` with a `weight`
 class WeightedLink
        super Link
+       auto_serializable
 
        # The `weight`, or cost, of this link
        var weight: Int
@@ -348,6 +401,8 @@ end
 
 # Advanced path conditions with customizable accept states
 class TargetCondition[N: Node]
+       auto_serializable
+
        # Should the pathfinding accept `node` as a goal?
        fun accept(node: N): Bool is abstract
 
index f9b55af..4cafb1d 100644 (file)
@@ -15,6 +15,8 @@
 # Highly specific, but useful, collections-related classes.
 module more_collections
 
+import serialization
+
 # Simple way to store an `HashMap[K, Array[V]]`
 #
 # Unlike standard HashMap, MultiHashMap provides a new
@@ -30,6 +32,7 @@ module more_collections
 #     assert m["four"] == ['i', 'i', 'i', 'i']
 #     assert m["zzz"] == new Array[Char]
 class MultiHashMap[K, V]
+       auto_serializable
        super HashMap[K, Array[V]]
 
        # Add `v` to the array associated with `k`.
@@ -61,6 +64,8 @@ end
 # assert hm2[2, "not-two"] == null
 # ~~~~
 class HashMap2[K1, K2, V]
+       auto_serializable
+
        private var level1 = new HashMap[K1, HashMap[K2, V]]
 
        # Return the value associated to the keys `k1` and `k2`.
@@ -108,6 +113,8 @@ end
 # assert hm3[2, "not-two", 22] == null
 # ~~~~
 class HashMap3[K1, K2, K3, V]
+       auto_serializable
+
        private var level1 = new HashMap[K1, HashMap2[K2, K3, V]]
 
        # Return the value associated to the keys `k1`, `k2`, and `k3`.
@@ -186,6 +193,7 @@ end
 # assert dma.default == [65]
 # ~~~~
 class DefaultMap[K, V]
+       auto_serializable
        super HashMap[K, V]
 
        # The default value.
index 6e5ce12..3ba9968 100644 (file)
@@ -164,3 +164,37 @@ redef class NativeString super DirectSerializable end
 redef class String super DirectSerializable end
 redef class SimpleCollection[E] super Serializable end
 redef class Map[K, V] super Serializable end
+
+redef class Couple[F, S]
+       super Serializable
+
+       redef init from_deserializer(v)
+       do
+               v.notify_of_creation self
+               var first = v.deserialize_attribute("first")
+               var second = v.deserialize_attribute("second")
+               init(first, second)
+       end
+
+       redef fun core_serialize_to(v)
+       do
+               v.serialize_attribute("first", first)
+               v.serialize_attribute("second", second)
+       end
+end
+
+redef class Container[E]
+       super Serializable
+
+       redef init from_deserializer(v)
+       do
+               v.notify_of_creation self
+               var item = v.deserialize_attribute("item")
+               init item
+       end
+
+       redef fun core_serialize_to(v)
+       do
+               v.serialize_attribute("item", first)
+       end
+end
index fc2e797..7a38c28 100644 (file)
@@ -15,6 +15,7 @@ redef class Deserializer
                if name == "Array[String]" then return new Array[String].from_deserializer(self)
                if name == "HashMap[Serializable, Array[Couple[Serializable, Int]]]" then return new HashMap[Serializable, Array[Couple[Serializable, Int]]].from_deserializer(self)
                if name == "Array[Couple[Serializable, Int]]" then return new Array[Couple[Serializable, Int]].from_deserializer(self)
+               if name == "Couple[Serializable, Int]" then return new Couple[Serializable, Int].from_deserializer(self)
                return super
        end
 end