defmodule Paradigm.Abstraction do @moduledoc """ Functions for moving between Paradigm structs and corresponding graph data. """ @doc """ Takes any `Paradigm` and produces the corresponding graph that is invariant against the Metamodel paradigm. """ def embed(paradigm, graph \\ nil) do graph = graph || Paradigm.Graph.MapGraph.new(name: paradigm.name, description: paradigm.description) graph |> add_primitive_types(paradigm) |> add_packages(paradigm) |> add_classes(paradigm) |> add_enumerations(paradigm) |> add_properties(paradigm) end defp add_primitive_types(graph, paradigm) do Enum.reduce(paradigm.primitive_types, graph, fn {id, primitive_type}, acc -> data = %{ "name" => primitive_type.name } Paradigm.Graph.insert_node(acc, id, "primitive_type", data) end) end defp add_packages(graph, paradigm) do Enum.reduce(paradigm.packages, graph, fn {id, package}, acc -> data = %{ "name" => package.name, "uri" => package.uri, "nested_packages" => package.nested_packages, "owned_types" => package.owned_types } Paradigm.Graph.insert_node(acc, id, "package", data) end) end defp add_classes(graph, paradigm) do Enum.reduce(paradigm.classes, graph, fn {id, class}, acc -> data = %{ "name" => class.name, "is_abstract" => class.is_abstract, "owned_attributes" => class.owned_attributes, "super_classes" => class.super_classes } Paradigm.Graph.insert_node(acc, id, "class", data) end) end defp add_enumerations(graph, paradigm) do Enum.reduce(paradigm.enumerations, graph, fn {id, enum}, acc -> data = %{ "name" => enum.name, "literals" => enum.literals } Paradigm.Graph.insert_node(acc, id, "enumeration", data) end) end defp add_properties(graph, paradigm) do Enum.reduce(paradigm.properties, graph, fn {id, property}, acc -> data = %{ "name" => property.name, "is_ordered" => property.is_ordered, "type" => property.type, "is_composite" => property.is_composite, "lower_bound" => property.lower_bound, "upper_bound" => property.upper_bound, "default_value" => property.default_value } Paradigm.Graph.insert_node(acc, id, "property", data) end) end @doc """ Takes a graph of metamodel type, and returns a top-level `Paradigm` object. """ def extract(graph) do primitive_types = Paradigm.Graph.get_all_nodes_of_class(graph, "primitive_type") |> Enum.map(fn id -> {id, Paradigm.Graph.get_node(graph, id)} end) |> Enum.map(fn {id, node} -> {id, %Paradigm.PrimitiveType{ name: node.data["name"] }} end) |> Map.new() packages = Paradigm.Graph.get_all_nodes_of_class(graph, "package") |> Enum.map(fn id -> {id, Paradigm.Graph.get_node(graph, id)} end) |> Enum.map(fn {id, node} -> {id, %Paradigm.Package{ name: node.data["name"], uri: node.data["uri"], nested_packages: node.data["nested_packages"], owned_types: node.data["owned_types"] }} end) |> Map.new() classes = Paradigm.Graph.get_all_nodes_of_class(graph, "class") |> Enum.map(fn id -> {id, Paradigm.Graph.get_node(graph, id)} end) |> Enum.map(fn {id, node} -> {id, %Paradigm.Class{ name: node.data["name"], is_abstract: node.data["is_abstract"], owned_attributes: node.data["owned_attributes"], super_classes: node.data["super_classes"] }} end) |> Map.new() enumerations = Paradigm.Graph.get_all_nodes_of_class(graph, "enumeration") |> Enum.map(fn id -> {id, Paradigm.Graph.get_node(graph, id)} end) |> Enum.map(fn {id, node} -> {id, %Paradigm.Enumeration{ name: node.data["name"], literals: node.data["literals"] }} end) |> Map.new() properties = Paradigm.Graph.get_all_nodes_of_class(graph, "property") |> Enum.map(fn id -> {id, Paradigm.Graph.get_node(graph, id)} end) |> Enum.map(fn {id, node} -> {id, %Paradigm.Property{ name: node.data["name"], is_ordered: node.data["is_ordered"], type: node.data["type"], is_composite: node.data["is_composite"], lower_bound: node.data["lower_bound"], upper_bound: node.data["upper_bound"], default_value: node.data["default_value"] }} end) |> Map.new() %Paradigm{ name: graph.metadata[:name], description: graph.metadata[:description], primitive_types: primitive_types, packages: packages, classes: classes, enumerations: enumerations, properties: properties } end end