PageRenderTime 22ms CodeModel.GetById 2ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/support/z_toposort.erl

http://github.com/zotonic/zotonic
Erlang | 81 lines | 51 code | 11 blank | 19 comment | 5 complexity | c4d60768fafc22a977b6f920fd0fea21 MD5 | raw file
 1%% @author Marc Worrell <marc@worrell.nl>
 2%% @copyright 2011 Marc Worrell
 3%% @doc Simple topological sort of tuples {item, [depends], [provides]}
 4
 5%% Copyright 2011 Marc Worrell
 6%%
 7%% Licensed under the Apache License, Version 2.0 (the "License");
 8%% you may not use this file except in compliance with the License.
 9%% You may obtain a copy of the License at
10%% 
11%%     http://www.apache.org/licenses/LICENSE-2.0
12%% 
13%% Unless required by applicable law or agreed to in writing, software
14%% distributed under the License is distributed on an "AS IS" BASIS,
15%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16%% See the License for the specific language governing permissions and
17%% limitations under the License.
18
19-module(z_toposort).
20
21-export([
22    sort/1
23]).
24
25
26%% @doc Return the topological sort of a list.
27-type topoitems() :: {Name::term(), Depends::list(), Provides::list()}.  
28-spec sort(topoitems()) -> {ok, list()} | {error, {cyclic, list()}}.
29sort([]) ->
30    {ok, []};
31sort(L) ->
32    G = digraph:new(),
33    Vs = [ {N, digraph:add_vertex(G)} || {N, _, _} <- L ],
34    add_node(G, L, L, Vs).
35
36
37add_node(G, _Nodes, [], Vs) ->
38    case digraph_utils:is_acyclic(G) of
39        true ->
40            SortedVs = digraph_utils:topsort(G),
41            digraph:delete(G),
42            {ok, vertices_to_nodes(SortedVs, Vs)};
43        false ->
44            Cycles = digraph_utils:cyclic_strong_components(G),
45            digraph:delete(G),
46            {error, {cyclic, [ vertices_to_nodes(Components, Vs) || Components <- Cycles ]}}
47    end;
48add_node(G, Nodes, [{_N, [], _Provides}|L], Vs) ->
49    add_node(G, Nodes, L, Vs);
50add_node(G, Nodes, [{Node, Depends, _Provides}|L], Vs) ->
51    {Node, NVx} = proplists:lookup(Node, Vs), 
52    DepNodes = lists:flatten([ find_node(Nodes, [], Depend) || Depend <- Depends ]),
53    [ 
54      begin
55          {N, Vx} = proplists:lookup(N, Vs),
56          digraph:add_edge(G, Vx, NVx)
57      end
58      || N <- DepNodes
59    ],
60    add_node(G, Nodes, L, Vs).
61    
62% find_node([], [], D) ->
63%     throw({error, {missing_provide, D}});
64find_node([], Fs, _D) ->
65    Fs;
66find_node([{N, _, Provides}|L], Fs, D) ->
67    case lists:member(D, Provides) of
68        true -> find_node(L, [N|Fs], D);
69        false -> find_node(L, Fs, D)
70    end.
71
72
73vertices_to_nodes(Vertices, Nodes) ->
74    [ 
75        begin
76            {value, {N,_}} = lists:keysearch(V, 2, Nodes),
77            N
78        end
79        || V <- Vertices 
80    ].
81