Current section

Files

Jump to
yog_ex lib yog io gexf saxy_handler.ex
Raw

lib/yog/io/gexf/saxy_handler.ex

defmodule Yog.IO.GEXF.SaxyHandler do
@moduledoc false
@behaviour Saxy.Handler
defstruct node_folder: nil,
edge_folder: nil,
graph_type: :directed,
node_attr_map: %{},
node_attr_types: %{},
edge_attr_map: %{},
edge_attr_types: %{},
nodes: [],
edges: [],
current_element: nil,
current_attrs: %{},
multigraph: false
@impl Saxy.Handler
def handle_event(:start_document, _prolog, state) do
{:ok, state}
end
@impl Saxy.Handler
def handle_event(:end_document, _data, state) do
{:ok, state}
end
@impl Saxy.Handler
def handle_event(:start_element, {"graph", attrs}, state) do
graph_type =
case List.keyfind(attrs, "defaultedgetype", 0) do
{"defaultedgetype", "undirected"} -> :undirected
_ -> :directed
end
{:ok, %{state | graph_type: graph_type}}
end
def handle_event(:start_element, {"attributes", attrs}, state) do
class =
case List.keyfind(attrs, "class", 0) do
{"class", c} -> c
_ -> "node"
end
{:ok, %{state | current_element: {:attributes, class}}}
end
def handle_event(
:start_element,
{"attribute", attrs},
%{current_element: {:attributes, class}} = state
) do
id_str =
case List.keyfind(attrs, "id", 0) do
{"id", id} -> id
_ -> nil
end
title =
case List.keyfind(attrs, "title", 0) do
{"title", t} -> t
_ -> id_str
end
type =
case List.keyfind(attrs, "type", 0) do
{"type", t} -> t
_ -> "string"
end
if id_str != nil do
case class do
"edge" ->
{:ok,
%{
state
| edge_attr_map: Map.put(state.edge_attr_map, id_str, title),
edge_attr_types: Map.put(state.edge_attr_types, id_str, type)
}}
_ ->
{:ok,
%{
state
| node_attr_map: Map.put(state.node_attr_map, id_str, title),
node_attr_types: Map.put(state.node_attr_types, id_str, type)
}}
end
else
{:ok, state}
end
end
def handle_event(:start_element, {"node", attrs}, state) do
id =
case List.keyfind(attrs, "id", 0) do
{"id", id_str} -> parse_id(id_str)
_ -> nil
end
label =
case List.keyfind(attrs, "label", 0) do
{"label", l} -> l
_ -> ""
end
{:ok, %{state | current_element: :node, current_attrs: %{"_id" => id, "label" => label}}}
end
def handle_event(:start_element, {"edge", attrs}, state) do
eid =
if state.multigraph do
case List.keyfind(attrs, "id", 0) do
{"id", id_str} -> parse_id(id_str)
_ -> nil
end
else
nil
end
source =
case List.keyfind(attrs, "source", 0) do
{"source", src} -> parse_id(src)
_ -> nil
end
target =
case List.keyfind(attrs, "target", 0) do
{"target", tgt} -> parse_id(tgt)
_ -> nil
end
weight =
case List.keyfind(attrs, "weight", 0) do
{"weight", w} -> w
_ -> ""
end
base_attrs = %{
"_source" => source,
"_target" => target,
"weight" => weight
}
current_attrs = if state.multigraph, do: Map.put(base_attrs, "_eid", eid), else: base_attrs
{:ok,
%{
state
| current_element: :edge,
current_attrs: current_attrs
}}
end
def handle_event(:start_element, {"attvalue", attrs}, state) do
for_attr =
case List.keyfind(attrs, "for", 0) do
{"for", f} -> f
_ -> nil
end
value_str =
case List.keyfind(attrs, "value", 0) do
{"value", v} -> v
_ -> ""
end
if for_attr != nil do
{attr_map, type_map} =
case state.current_element do
:edge -> {state.edge_attr_map, state.edge_attr_types}
_ -> {state.node_attr_map, state.node_attr_types}
end
key = Map.get(attr_map, for_attr, for_attr)
type = Map.get(type_map, for_attr, "string")
value = cast_value(value_str, type)
new_attrs = Map.put(state.current_attrs, key, value)
{:ok, %{state | current_attrs: new_attrs}}
else
{:ok, state}
end
end
def handle_event(:start_element, {"viz:color", attrs}, state) do
r = get_attr_int(attrs, "r", 0)
g = get_attr_int(attrs, "g", 0)
b = get_attr_int(attrs, "b", 0)
a = get_attr_float(attrs, "a", 1.0)
new_attrs = Map.put(state.current_attrs, "viz:color", %{r: r, g: g, b: b, a: a})
{:ok, %{state | current_attrs: new_attrs}}
end
def handle_event(:start_element, {"viz:size", attrs}, state) do
val = get_attr_float(attrs, "value", 1.0)
new_attrs = Map.put(state.current_attrs, "viz:size", val)
{:ok, %{state | current_attrs: new_attrs}}
end
def handle_event(:start_element, {"viz:position", attrs}, state) do
x = get_attr_float(attrs, "x", 0.0)
y = get_attr_float(attrs, "y", 0.0)
z = get_attr_float(attrs, "z", 0.0)
new_attrs = Map.put(state.current_attrs, "viz:position", %{x: x, y: y, z: z})
{:ok, %{state | current_attrs: new_attrs}}
end
def handle_event(:start_element, {"viz:shape", attrs}, state) do
val =
case List.keyfind(attrs, "value", 0) do
{"value", v} -> v
_ -> "disc"
end
new_attrs = Map.put(state.current_attrs, "viz:shape", val)
{:ok, %{state | current_attrs: new_attrs}}
end
def handle_event(:start_element, _other, state) do
{:ok, state}
end
@impl Saxy.Handler
def handle_event(:end_element, "node", state) do
id = Map.get(state.current_attrs, "_id")
label = Map.get(state.current_attrs, "label", "")
attrs_map =
state.current_attrs
|> Map.delete("_id")
|> Map.delete("label")
attrs = if label != "", do: Map.put(attrs_map, "label", label), else: attrs_map
data = state.node_folder.(attrs)
nodes = [{id, data} | state.nodes]
{:ok, %{state | nodes: nodes, current_element: nil, current_attrs: %{}}}
end
def handle_event(:end_element, "edge", state) do
source = Map.get(state.current_attrs, "_source")
target = Map.get(state.current_attrs, "_target")
base_keys = ["_source", "_target"]
keys_to_delete = if state.multigraph, do: ["_eid" | base_keys], else: base_keys
attrs_map = Map.drop(state.current_attrs, keys_to_delete)
weight = state.edge_folder.(attrs_map)
edge =
if state.multigraph do
eid = Map.get(state.current_attrs, "_eid")
{eid, source, target, weight}
else
{source, target, weight}
end
edges = [edge | state.edges]
{:ok, %{state | edges: edges, current_element: nil, current_attrs: %{}}}
end
def handle_event(:end_element, _other, state) do
{:ok, state}
end
@impl Saxy.Handler
def handle_event(:characters, _chars, state) do
{:ok, state}
end
defp cast_value(val, "integer"), do: String.to_integer(val)
defp cast_value(val, "long"), do: String.to_integer(val)
defp cast_value(val, "double"), do: String.to_float(val)
defp cast_value(val, "float"), do: String.to_float(val)
defp cast_value("true", "boolean"), do: true
defp cast_value("false", "boolean"), do: false
defp cast_value(val, _), do: val
defp get_attr_int(attrs, name, default) do
case List.keyfind(attrs, name, 0) do
{^name, v} -> String.to_integer(v)
_ -> default
end
end
defp get_attr_float(attrs, name, default) do
case List.keyfind(attrs, name, 0) do
{^name, v} ->
case Float.parse(v) do
{f, _} -> f
:error -> default
end
_ ->
default
end
end
defp parse_id(id_str) do
case Integer.parse(id_str) do
{int, ""} -> int
_ -> id_str
end
end
end