<commit_before>%%%-------------------------------------------------------------------
%%% @author <NAME> <<EMAIL>>
%%% @copyright (C) 2014, <NAME>
%%% @doc
%%% Procedures to write a diagram to a .dot file
%%% @end
%%% Created : 17 Jun 2014 by <NAME>
%%%-------------------------------------------------------------------
%%% All rights reserved.
%%%
%%% Redistribution and use in source and binary forms, with or without
%%% modification, are permitted provided that the following conditions
%%% are met:
%%%
%%% 1. Redistributions of source code must retain the above copyright
%%% notice, this list of conditions and the following disclaimer.
%%%
%%% 2. Redistributions in binary form must reproduce the above
%%% copyright notice, this list of conditions and the following
%%% disclaimer in the documentation and/or other materials provided
%%% with the distribution.
%%%
%%% 3. Neither the name of the copyright holder nor the names of its
%%% contributors may be used to endorse or promote products derived
%%% from this software without specific prior written permission.
%%%
%%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
%%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
%%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
%%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
%%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
%%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
%%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
%%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
%%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
%%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
%%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%%% POSSIBILITY OF SUCH DAMAGE.
%%%-------------------------------------------------------------------
-module(diagram_tool).

-export([write_diagram/2]).

-include("records.hrl").

write_diagram(Nodes, Arcs) ->
    ["digraph Example { \n",
     "ordering = \"out\" ;\n",
     "ranksep = 2;\n",
     "ratio = \"auto\" ;\n",
     "label = \"Java Interactions\" ;\n",
     write_clustered_nodes(Nodes),
     write_arcs(arc_sorter:sort_arcs(Arcs)),
     "}\n\n"].

write_clustered_nodes(Nodes) ->
    Clusters = utils:group_by(fun compare_clusters/2, Nodes),
    lists:map(fun write_node_cluster/1, Clusters).

write_node_cluster([DiaNode|_] = List) ->
    case get_cluster(DiaNode) of
	none -> write_nodes(List);
	{cluster, Name, Title} -> [cluster_open(Name, Title),
				   write_nodes(List),
				   cluster_close()]
    end.

cluster_open(Name, Title) ->
    ["subgraph cluster" ++ Name ++ " {\n",
     "label = \"" ++ Title ++ "\";\n"].

cluster_close() -> "}\n".

compare_clusters(A, B) ->
    case {get_cluster(A), get_cluster(B)} of
	{{cluster, C, _}, {cluster, C, _}} -> true;
	_ -> false
    end.

get_cluster(#diagram_node{cluster = {cluster, Id, Title}}) -> {cluster, Id, Title};
get_cluster(_) -> none.

write_nodes([]) -> "";
write_nodes([#diagram_node{id = Id, label = Label, properties = OldOpts,
			   is_label_term = IsLabelTerm,
			   tags = Tags}|Rest]) ->
    EscLabel = case IsLabelTerm of
		   true -> parser_utils:print_escaped(Label);
		   false -> parser_utils:escape(Label)
	       end,
    Opts = add_tags_to_opts(OldOpts, Tags),
    [[Id, " [label = \"", EscLabel, "\"",
     comma_if_non_empty(Opts), write_opts(Opts), "] ;\n"], write_nodes(Rest)].
write_arcs([]) -> "";
write_arcs([#diagram_arc{id_start = From,
			 id_end = To,
			 properties = Opts}|Rest]) ->
     [[From, " -> ", To, " [", write_opts([{color, 128, 128, 128},arrow_head|Opts]),
      "] ;\n"], write_arcs(Rest)].

comma_if_non_empty([]) -> ", ";
comma_if_non_empty(_) -> " ".

add_tags_to_opts(Opts, Tags) ->
    case lists:foldl(fun find_color/2, {false, false, false}, Tags) of
	{true, true, true} -> [{color, 0, 0, 0}|Opts];
	{false, false, false} -> [{color, 128, 128, 128}|Opts];
	{G, B, R} -> [{color, translate_bool_to_col(R),
		       translate_bool_to_col(G),
		       translate_bool_to_col(B)}|Opts]<commit_msg>Removed duplicated color declarations from graph<commit_after>%%%-------------------------------------------------------------------
%%% @author <NAME> <<EMAIL>>
%%% @copyright (C) 2014, <NAME>
%%% @doc
%%% Procedures to write a diagram to a .dot file
%%% @end
%%% Created : 17 Jun 2014 by <NAME>
%%%-------------------------------------------------------------------
%%% All rights reserved.
%%%
%%% Redistribution and use in source and binary forms, with or without
%%% modification, are permitted provided that the following conditions
%%% are met:
%%%
%%% 1. Redistributions of source code must retain the above copyright
%%% notice, this list of conditions and the following disclaimer.
%%%
%%% 2. Redistributions in binary form must reproduce the above
%%% copyright notice, this list of conditions and the following
%%% disclaimer in the documentation and/or other materials provided
%%% with the distribution.
%%%
%%% 3. Neither the name of the copyright holder nor the names of its
%%% contributors may be used to endorse or promote products derived
%%% from this software without specific prior written permission.
%%%
%%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
%%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
%%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
%%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
%%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
%%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
%%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
%%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
%%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
%%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
%%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%%% POSSIBILITY OF SUCH DAMAGE.
%%%-------------------------------------------------------------------
-module(diagram_tool).

-export([write_diagram/2]).

-include("records.hrl").

write_diagram(Nodes, Arcs) ->
    ["digraph Example { \n",
     "ordering = \"out\" ;\n",
     "ranksep = 2;\n",
     "ratio = \"auto\" ;\n",
     "label = \"Java Interactions\" ;\n",
     write_clustered_nodes(Nodes),
     write_arcs(arc_sorter:sort_arcs(Arcs)),
     "}\n\n"].

write_clustered_nodes(Nodes) ->
    Clusters = utils:group_by(fun compare_clusters/2, Nodes),
    lists:map(fun write_node_cluster/1, Clusters).

write_node_cluster([DiaNode|_] = List) ->
    case get_cluster(DiaNode) of
	none -> write_nodes(List);
	{cluster, Name, Title} -> [cluster_open(Name, Title),
				   write_nodes(List),
				   cluster_close()]
    end.

cluster_open(Name, Title) ->
    ["subgraph cluster" ++ Name ++ " {\n",
     "label = \"" ++ Title ++ "\";\n"].

cluster_close() -> "}\n".

compare_clusters(A, B) ->
    case {get_cluster(A), get_cluster(B)} of
	{{cluster, C, _}, {cluster, C, _}} -> true;
	_ -> false
    end.

get_cluster(#diagram_node{cluster = {cluster, Id, Title}}) -> {cluster, Id, Title};
get_cluster(_) -> none.

write_nodes([]) -> "";
write_nodes([#diagram_node{id = Id, label = Label, properties = OldOpts,
			   is_label_term = IsLabelTerm,
			   tags = Tags}|Rest]) ->
    EscLabel = case IsLabelTerm of
		   true -> parser_utils:print_escaped(Label);
		   false -> parser_utils:escape(Label)
	       end,
    Opts = add_tags_to_opts(OldOpts, Tags),
    [[Id, " [label = \"", EscLabel, "\"",
     comma_if_non_empty(Opts), write_opts(Opts), "] ;\n"], write_nodes(Rest)].
write_arcs([]) -> "";
write_arcs([#diagram_arc{id_start = From,
			 id_end = To,
			 properties = Opts}|Rest]) ->
     [[From, " -> ", To, " [", write_opts(
				 case tol_keymember(color, 1, Opts) of
				     false -> [{color, 128, 128, 128},arrow_head|Opts];
				     true -> [arrow_head|Opts]
				 end),
      "] ;\n"], write_arcs(Rest)].

tol_keymember(_Elem, _N, []) -> false;
tol_keymember(Elem, N, [Tuple|Rest]) when is_tuple(Tuple) ->
    case tuple_to_list(Tuple) of
	L when (length(L) >= N) andalso (element(N, Tuple) =:= Elem) -> true;
	_ -> tol_keymember(Elem, N, Rest)
    end;
tol_keymember(Elem, N, [_|Rest]) -> tol_keymember(Elem, N, Rest).

	

comma_if_non_empty([]) -> ", ";
comma_if_non_empty(_) -> " ".

add_tags_to_opts(Opts, Tags) ->
    case lists:foldl(fun find_color/2, {false, false, false}, Tags) of
	{true, true, true} -> [{color, 0, 0, 0}|Opts];
	{false, false, false} -> [{color, 128, 128, 128}|Opts];
	{G, B, R} -> [{color, translate_bool_to_col(R),
		       translate_bool_to_col(G),
		       translate_bool_to_col(B)}|Opts]
{'fraction_non_alphanumeric': 0.1956373551465576, 'fraction_numerical': 0.00897523290161327, 'mean_word_length': 3.2547124214596423, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4857413', 'n_tokens_mistral': 2989, 'n_tokens_neox': 2740, 'n_words': 846}