# Pre order sets and partial order set (ie hierarchies)
module poset
+import serialization::serialization_core
+
# Pre-order set graph.
# This class models an incremental pre-order graph where new nodes and edges can be added (but not removed).
# Pre-order graph has two characteristics:
super Collection[E]
super Comparator
super Cloneable
+ super Serializable
redef type COMPARED: E is fixed
ids[x] = ids.length
end
for x in elements.keys do
- var xstr = x.to_s.escape_to_dot
+ var xstr = (x or else "null").to_s.escape_to_dot
var nx = "n{ids[x]}"
f.write "{nx}[label=\"{xstr}\"];\n"
var xe = self.elements[x]
# ~~~~
#
# Note that the linear extension is stable, unless a new node or a new edge is added.
- redef fun compare(a, b: E): Int
+ redef fun compare(a, b)
do
var ae = self.elements[a]
var be = self.elements[b]
end
return res
end
+
+ redef fun core_serialize_to(serializer)
+ do
+ # Optimize written data because this structure has duplicated data
+ # For example, serializing the class hierarchy of a simple program where E is String
+ # result is before: 200k, after: 56k.
+ serializer.serialize_attribute("elements", elements)
+ end
+
+ redef init from_deserializer(deserializer)
+ do
+ deserializer.notify_of_creation self
+ var elements = deserializer.deserialize_attribute("elements")
+ if elements isa HashMap[E, POSetElement[E]] then
+ self.elements = elements
+ end
+ end
end
-# View of an objet in a poset
+# View of an object in a poset
# This class is a helper to handle specific queries on a same object
#
# For instance, one common usage is to add a specific attribute for each poset a class belong.
# t.in_some_relation.greaters
# ~~~
class POSetElement[E]
+ super Serializable
+
# The poset self belong to
var poset: POSet[E]
end
end
return min
+ end
+ redef fun core_serialize_to(serializer)
+ do
+ serializer.serialize_attribute("poset", poset)
+ serializer.serialize_attribute("element", element)
+ serializer.serialize_attribute("tos", tos)
+ serializer.serialize_attribute("froms", froms)
+ serializer.serialize_attribute("dtos", dtos)
+ serializer.serialize_attribute("dfroms", dfroms)
+ serializer.serialize_attribute("count", count)
+
+ # Don't serialize `froms`, `dtos` and `tos` as they duplicate information.
+ # TODO serialize them if a flag for extra info is set on `serializer`.
+ end
+
+ redef init from_deserializer(v)
+ do
+ # Code generated by the serialization_phase from the compiler frontend,
+ # copied here for compatibility with nith.
+
+ super
+ v.notify_of_creation self
+
+ var poset = v.deserialize_attribute("poset", "POSet[nullable Object]")
+ if v.deserialize_attribute_missing then
+ v.errors.add new Error("Deserialization Error: attribute `{class_name}::poset` missing from JSON object")
+ else if not poset isa POSet[E] then
+ v.errors.add new AttributeTypeError(self, "poset", poset, "POSet[nullable Object]")
+ if v.keep_going == false then return
+ else
+ self.poset = poset
+ end
+
+ var element = v.deserialize_attribute("element", "nullable Object")
+ if v.deserialize_attribute_missing then
+ v.errors.add new Error("Deserialization Error: attribute `{class_name}::element` missing from JSON object")
+ else if not element isa E then
+ v.errors.add new AttributeTypeError(self, "element", element, "nullable Object")
+ if v.keep_going == false then return
+ else
+ self.element = element
+ end
+
+ var tos = v.deserialize_attribute("tos", "HashSet[nullable Object]")
+ if v.deserialize_attribute_missing then
+ else if not tos isa HashSet[E] then
+ v.errors.add new AttributeTypeError(self, "tos", tos, "HashSet[nullable Object]")
+ if v.keep_going == false then return
+ else
+ self.tos = tos
+ end
+
+ var froms = v.deserialize_attribute("froms", "HashSet[nullable Object]")
+ if v.deserialize_attribute_missing then
+ else if not froms isa HashSet[E] then
+ v.errors.add new AttributeTypeError(self, "froms", froms, "HashSet[nullable Object]")
+ if v.keep_going == false then return
+ else
+ self.froms = froms
+ end
+
+ var dtos = v.deserialize_attribute("dtos", "HashSet[nullable Object]")
+ if v.deserialize_attribute_missing then
+ else if not dtos isa HashSet[E] then
+ v.errors.add new AttributeTypeError(self, "dtos", dtos, "HashSet[nullable Object]")
+ if v.keep_going == false then return
+ else
+ self.dtos = dtos
+ end
+
+ var dfroms = v.deserialize_attribute("dfroms", "HashSet[nullable Object]")
+ if v.deserialize_attribute_missing then
+ else if not dfroms isa HashSet[E] then
+ v.errors.add new AttributeTypeError(self, "dfroms", dfroms, "HashSet[nullable Object]")
+ if v.keep_going == false then return
+ else
+ self.dfroms = dfroms
+ end
+
+ var count = v.deserialize_attribute("count", "Int")
+ if v.deserialize_attribute_missing then
+ v.errors.add new Error("Deserialization Error: attribute `{class_name}::count` missing from JSON object")
+ else if not count isa Int then
+ v.errors.add new AttributeTypeError(self, "count", count, "Int")
+ if v.keep_going == false then return
+ else
+ self.count = count
+ end
+ end
+end
+
+redef class MapRead[K, V]
+ # Return all elements of `keys` that have a value.
+ #
+ # ~~~
+ # var map = new Map[String, String]
+ # map["A"] = "a"
+ # map["B"] = "b"
+ # map["C"] = "c"
+ #
+ # assert map.filter_keys(["B"]) == ["B"]
+ # assert map.filter_keys(["A", "Z", "C"]) == ["A", "C"]
+ # assert map.filter_keys(["X", "Y", "Z"]).is_empty
+ # ~~~
+ #
+ # `has_key` is used to filter.
+ fun filter_keys(keys: Collection[nullable Object]): Array[K]
+ do
+ var res = new Array[K]
+ for e in keys do
+ if has_key(e) then res.add e
+ end
+ return res
+ end
+
+ # Search all the values in `pe.greaters`.
+ #
+ # Elements without values are ignored.
+ #
+ # Basically, values defined in all greater elements of `pe` are inherited.
+ #
+ # ~~~
+ # var pos = new POSet[String]
+ # pos.add_chain(["E", "D", "C", "B", "A"])
+ # pos.add_chain(["D", "X", "B"])
+ #
+ # var map = new HashMap[String, String]
+ # map["A"] = "a"
+ # map["C"] = "c"
+ # map["X"] = "x"
+ # map["E"] = "e"
+ #
+ # assert map.lookup_all_values(pos["B"]).has_exactly(["a"])
+ # assert map.lookup_all_values(pos["C"]).has_exactly(["a", "c"])
+ # assert map.lookup_all_values(pos["D"]).has_exactly(["a", "c", "x"])
+ # ~~~
+ fun lookup_all_values(pe: POSetElement[K]): Set[V]
+ do
+ var res = new Set[V]
+ for k in filter_keys(pe.greaters) do res.add self[k]
+ return res
+ end
+
+ # Combine the values in `pe.greaters` from the most smaller elements that have a value.
+ #
+ # Elements without values are ignored.
+ #
+ # Basically, values defined in nearest greater elements of `pe` are inherited.
+ #
+ # ~~~
+ # var pos = new POSet[String]
+ # pos.add_chain(["E", "D", "C", "B", "A"])
+ # pos.add_chain(["D", "X", "B"])
+ #
+ # var map = new HashMap[String, String]
+ # map["A"] = "a"
+ # map["C"] = "c"
+ # map["X"] = "x"
+ # map["E"] = "e"
+ #
+ # assert map.lookup_values(pos["B"]).has_exactly(["a"])
+ # assert map.lookup_values(pos["C"]).has_exactly(["c"])
+ # assert map.lookup_values(pos["D"]).has_exactly(["c", "x"])
+ # ~~~
+ fun lookup_values(pe: POSetElement[K]): Set[V]
+ do
+ var res = new Set[V]
+ for k in pe.poset.select_smallest(filter_keys(pe.greaters)) do res.add self[k]
+ return res
end
end