1/*****************************************************************************
    2 * This file is part of the Prolog Development Tool (PDT)
    3 * 
    4 * WWW: http://sewiki.iai.uni-bonn.de/research/pdt/start
    5 * Mail: pdt@lists.iai.uni-bonn.de
    6 * Copyright (C): 2004-2012, CS Dept. III, University of Bonn
    7 * 
    8 * All rights reserved. This program is  made available under the terms
    9 * of the Eclipse Public License v1.0 which accompanies this distribution,
   10 * and is available at http://www.eclipse.org/legal/epl-v10.html
   11 * 
   12 ****************************************************************************/
   13
   14:- module(pl_to_graphML, [	%write_project_graph_to_file/2,
   15								write_focus_to_graphML/4,
   16								write_global_to_graphML/3,
   17								write_dependencies_to_graphML/3,
   18								write_logtalk_project_files_to_graphML/4,
   19								write_logtalk_library_to_graphML/3,
   20								write_logtalk_recursive_library_to_graphML/3,
   21								call_term_position/7]).   22
   23:- use_module(graphML_api).   24:- use_module(util_for_graphML).   25:- use_module(pdt_common_pl('callgraph/pdt_call_graph')).   26:- use_module(pdt_common_pl(pdt_search)).   27:- use_module(pdt_prolog_library(utils4modules_visibility)).   28:- use_module(library(lists), [list_to_set/2, append/3, member/2]).   29:- use_module( prolog_connector_pl(split_file_path), [
   30	split_file_path/5 % (File,Folder,FileName,BaseName,Extension)
   31]).   32:- use_module(pdt_prolog_library(compatibility), [
   33	pdt_source_file/2
   34]).   35
   36:- op(600, xfy, ::).   % Logtalk message sending operator
   37
   38%write_project_graph_to_file(Project, OutputFile):-
   39%	parse_util:generate_facts(Project),
   40%	writeln('generating graphml-file'),
   41%    time(write_facts_to_graphML(Project,OutputFile)).
   42
   43%/*
   44% * write_facts_to_graphML(+Project,+File)
   45% *   Arg1 has to be a fully qualified file name. The file must not exist.
   46% *   The predicated collects the relevant informations about the following 
   47% *   facts and converts them into abba-sources for Bashaars Tool. This 
   48% *   sources are written into the file specified by arg1-
   49% *   The facts that are considered are:
   50% *   ###### to be completed #########
   51% **/
   52%write_facts_to_graphML(Project, File):-
   53%    prepare_for_writing(File,OutStream),
   54%    member(FirstProject,Project),
   55%    write_all_files(FirstProject,OutStream), flush_output(OutStream),
   56%  	write_load_edges(OutStream), flush_output(OutStream),
   57%  	write_call_edges(OutStream), flush_output(OutStream),
   58% 	finish_writing(OutStream).
   59
   60
   61write_focus_to_graphML(FocusFile, GraphFile, DependentFiles, Settings):-
   62    write_to_graphML(GraphFile, write_focus_facts_to_graphML(FocusFile, DependentFiles, Settings)).  
   63
   64write_global_to_graphML(ProjectFilePaths, GraphFile, Settings):-
   65    filter_consulted(ProjectFilePaths, ConsultedFilePaths),
   66    write_to_graphML(GraphFile, write_global_facts_to_graphML(ConsultedFilePaths, Settings)).
   67
   68write_dependencies_to_graphML(ProjectFilePaths, ProjectPath, GraphFile):-
   69    filter_consulted(ProjectFilePaths, ConsultedFilePaths),
   70    write_to_graphML(GraphFile, write_dependencies_facts_to_graphML(ProjectPath, ConsultedFilePaths)).
   71
   72write_logtalk_project_files_to_graphML(DiagramType, ProjectFilePaths, ProjectPath, GraphFile):-
   73    (	current_predicate(logtalk_load/1)
   74    ->	filter_consulted(ProjectFilePaths, ConsultedFilePaths),
   75    	filter_logtalk(ConsultedFilePaths, _, ConsultedLogtalkFilePaths),
   76    	split_file_path(GraphFile, Directory, FileName, _, _),
   77    	graphml_writer::set_file_name(FileName),
   78    	DiagramObject =.. [DiagramType, graphml],
   79    	DiagramObject::files(ProjectPath, ConsultedLogtalkFilePaths, [output_directory(Directory)])
   80    ;	true
   81    ).
   82
   83write_logtalk_library_to_graphML(DiagramType, Library, GraphFile):-
   84    (	current_predicate(logtalk_load/1)
   85    ->	split_file_path(GraphFile, Directory, FileName, _, _),
   86    	graphml_writer::set_file_name(FileName),
   87    	DiagramObject =.. [DiagramType, graphml],
   88    	DiagramObject::library(Library, [output_directory(Directory)])
   89    ;	true
   90    ).
   91
   92write_logtalk_recursive_library_to_graphML(DiagramType, Library, GraphFile):-
   93    (	current_predicate(logtalk_load/1)
   94    ->	split_file_path(GraphFile, Directory, FileName, _, _),
   95    	graphml_writer::set_file_name(FileName),
   96    	DiagramObject =.. [DiagramType, graphml],
   97    	DiagramObject::rlibrary(Library, [output_directory(Directory)])
   98    ;	true
   99    ).
  100
  101relative_paths(_, [], []).
  102relative_paths(BasePath, [H|T], [FormattedH|FormattedT]) :-
  103    relative_path(BasePath, H, FormattedH),
  104    relative_paths(BasePath, T, FormattedT).
  105    
  106relative_path(BasePath, Path, RelativePath) :-
  107	atom_chars(BasePath, BasePathChars),
  108    atom_chars(Path, PathChars),
  109    relative_path_(BasePathChars, PathChars, RelativePathChars),
  110    atom_chars(RelativePath, RelativePathChars).
  111        
  112
  113relative_path_([], Head, Head).
  114relative_path_([H|T], [H|T2], Result) :-
  115    relative_path_(T, T2, Result).
  116
  117filter_logtalk([], [], []) :- !.
  118filter_logtalk([F|Fs], Ps, [F|Ls]) :-
  119	(	atom_concat(_, lgt, F)
  120	;	atom_concat(_, logtalk, F)
  121	),
  122	!,
  123	filter_logtalk(Fs, Ps, Ls).
  124filter_logtalk([F|Fs], [F|Ps], Ls) :-
  125	filter_logtalk(Fs, Ps, Ls).
  126
  127:- meta_predicate(write_to_graphML(+, 1)).  128write_to_graphML(GraphFile, CALL) :-
  129    with_mutex(prolog_factbase,
  130    	with_mutex(meta_pred_finder,
  131    		setup_call_cleanup(
  132    			prepare_for_writing(GraphFile,OutStream),
  133				call(CALL, OutStream),
  134  	  			finish_writing(OutStream)
  135  	  		)
  136  	 	)
  137  	 ).
  138  	 
  139filter_consulted(ProjectFilePaths, ConsultedFilePaths) :-
  140    findall(Path, (
  141    		member(Path, ProjectFilePaths), source_file(Path)
  142    	), ConsultedFilePaths).
  143
  144pdt_location(Location) :-
  145	predicate_property(pl_to_graphML:pdt_location(_), file(F)),
  146	atom_concat(Location, 'pdt.contextview/pl/pl_to_graphml.pl', F).
  147
  148hide_swi_predicates(M:F/A) :- functor(H, F, A), not(predicate_property(M:H, built_in)).
  149hide_swi_metapredicates(M:F/A) :- functor(H, F, A), not((predicate_property(M:H, built_in), predicate_property(M:H, (meta_predicate _)))).
  150
  151hide_pdt_predicates(M:F/A) :- functor(H, F, A), not((predicate_property(M:H, file(File)), pdt_location(Location), atom_concat(Location, _, File))).
  152hide_pdt_metapredicates(M:F/A) :- functor(H, F, A), not((predicate_property(M:H, file(File)), pdt_location(Location), atom_concat(Location, _, File), predicate_property(M:H, (meta_predicate _)))).
  153
  154filter(Setting,  pl_to_graphML:Setting).
  155
  156init_filters(Settings, Filters) :- 
  157	findall(F, (member(S, Settings), filter(S, F)), Filters).
  158
  159:- dynamic focus_facts_filter/1.  160
  161write_focus_facts_to_graphML(FocusFile, DependentFiles, Settings, OutStream):-
  162    source_file(FocusFile),
  163    !,
  164	
  165	ensure_call_graph_generated,
  166	collect_ids_for_focus_file(FocusFile, DependentFiles, ReferencedPredicates, Calls),
  167	
  168	retractall(focus_facts_filter(_)),
  169	assert((focus_facts_filter(X) :- member(X, ReferencedPredicates))),
  170	
  171	init_filters(Settings, Filters),
  172	
  173   	write_files(FocusFile, DependentFiles, [pl_to_graphML:focus_facts_filter|Filters], OutStream),
  174    forall(
  175    	member((SourceModule:SourceName/SourceArity, TargetModule:TargetName/TargetArity), Calls),
  176    	write_call_edge(OutStream, SourceModule, SourceName, SourceArity, TargetModule, TargetName, TargetArity, DependentFiles)
  177    ).
  178    
  179write_global_facts_to_graphML(ProjectFiles, Settings, OutStream) :-
  180    
  181    ensure_call_graph_generated,
  182    
  183    init_filters(Settings, Filters),
  184    
  185    forall(member(File, ProjectFiles),
  186		(	
  187			main_module_of_file(File, Module),
  188			write_file(OutStream, File, Filters, File, Module),
  189    		flush_output(OutStream)
  190    	)
  191    ),
  192    
  193    findall(Module:Name/Arity,
  194    	(
  195			member(File, ProjectFiles),
  196    		predicate_in_file(File, Module, Name, Arity)
  197    	),
  198    	Predicates
  199    ),
  200	forall((
  201		member(SourceModule:SourceName/SourceArity, Predicates),
  202		calls(TargetModule, TargetName, TargetArity, SourceModule, SourceName, SourceArity, _NumberOfCalls),
  203		once(member(TargetModule:TargetName/TargetArity, Predicates))
  204	),(
  205		write_call_edge(OutStream, SourceModule, SourceName, SourceArity, TargetModule, TargetName, TargetArity, ProjectFiles)
  206	)).
  207    
  208write_dependencies_facts_to_graphML(ProjectPath, ProjectFilePaths, OutStream) :-
  209    
  210	findall((SourceFile, TargetFile),
  211		(
  212			member(SourceFile, ProjectFilePaths),
  213			member(TargetFile, ProjectFilePaths),
  214    		loaded_by(TargetFile, SourceFile, _, _)
  215    	),
  216    	FoundDependencies
  217    ),
  218    
  219    forall(
  220    	(
  221    		nth1(Id, ProjectFilePaths, FilePath),
  222    		file_node_name(FilePath, ProjectPath, FileNodeName),
  223    		file_exports(FilePath, FileType, ExportedStaticPredicates, ExportedDynamicPredicates)
  224    	),	
  225		write_file_as_element(OutStream, Id, FilePath, FileNodeName, FileType, ExportedStaticPredicates, ExportedDynamicPredicates)
  226    ),
  227
  228    forall(
  229    	(
  230    		member((S, T), FoundDependencies),
  231    		nth1(SId, ProjectFilePaths, S),
  232    		nth1(TId, ProjectFilePaths, T),
  233    		file_imports(S, T, Imports),
  234    		length(Imports, NImports)
  235    	),
  236		(	NImports > 0
  237		->	write_load_edge(OutStream, SId, TId, Imports, NImports)
  238		;	write_load_edge(OutStream, SId, TId, Imports)
  239		)
  240    ).
  241   
  242file_exports(FilePath, module, ExportedStaticPredicates, ExportedDynamicPredicates) :-
  243	module_property(Module, file(FilePath)),
  244	module_property(Module, exports(Exports)),
  245	exports_classification(Exports, ExportedStaticPredicates, ExportedDynamicPredicates, Module),
  246	!.
  247file_exports(FilePath, non_module_file, StaticPredicates, DynamicPredicates) :-
  248	findall(
  249		N/A,
  250		(	pdt_source_file(user:H, FilePath),
  251			functor(H, N, A),
  252			\+ atom_concat('$', _, N)
  253		;	loaded_by(LoadedFile, FilePath, _, _),
  254			file_imports(FilePath, LoadedFile, Imports),
  255			member(N/A, Imports)
  256		),
  257		Predicates
  258	),
  259	exports_classification(Predicates, StaticPredicates, DynamicPredicates, user),
  260	!.
  261
  262exports_classification([Name/Arity|Tail], S, [Name/Arity|DTail], Module) :-
  263	functor(H, Name, Arity),
  264	predicate_property(Module:H, dynamic),
  265	!,
  266	exports_classification(Tail, S, DTail, Module).
  267exports_classification([E|Tail], [E|STail], D, Module) :-
  268    exports_classification(Tail, STail, D, Module).
  269exports_classification([], [], [], _Module) :- !.
  270
  271file_imports(File1, File2, PredNames) :-
  272    once(module_of_file(File1,M1)),
  273    once(module_of_file(File2,M2)), 
  274    module_imports_from(M1,M2,Preds),
  275    findall(Name/Arity,
  276    	(
  277    		member(P, Preds),
  278    		functor(P, Name, Arity)
  279    	),
  280    	PredNames).
  281    
  282module_imports_from(M1,M2,Preds) :-   
  283    setof( Head,
  284           predicate_property(M1:Head, imported_from(M2)),
  285           Preds),
  286    !.
  287module_imports_from(_M1,_M2,[]).
  288
  289% Module consults all Preds from File:
  290module_consults_from(Module,File,Preds) :-
  291    setof( Head,
  292           module_consults_from__(Module,File,Head),
  293           Preds).
  294
  295% Module consults Head from File:
  296module_consults_from__(Module,File,Head) :-
  297    module_of_file(ModuleFile,Module),
  298    declared_in_module(Module, Head),
  299    predicate_property(Module:Head, file(File)),
  300    File \== ModuleFile.
  301
  302
  303file_node_name(FilePath, _, ModuleName) :-
  304	module_property(ModuleName, file(FilePath)), !.
  305		
  306file_node_name(FilePath, _, Name)	:-
  307	directory_file_path(_, Name, FilePath), !.
  308	%relative_path(ProjectPath, FilePath, RelativePath), !.
  309	
  310file_node_name(FilePath, _, FilePath).
  311    
  312    
  313file_node_type(FilePath, Dependencies, 'top') :-
  314    not(member((_, FilePath), Dependencies)), !.
  315        
  316    
  317file_node_type(FilePath, Dependencies, 'bottom') :-
  318    not(member((FilePath, _), Dependencies)), !.
  319    
  320file_node_type(_, _, 'intermediate') :- !.
  321    
  322    
  323file_node_type(FilePath, Dependencies, 'top') :-
  324    not(member((_, FilePath), Dependencies)), !.
  325    
  326file_node_type(FilePath, Dependencies, 'bottom') :-
  327    not(member((FilePath, _), Dependencies)), !.
  328    
  329file_node_type(_, _, 'intermediate') :- !.
  330
  331write_logtalk_entity_facts_to_graphML(ProjectPath, ConsultedLogtalkFilePaths, OutStream) :-
  332	lgt_to_graphML::write_logtalk_entity_facts_to_graphML(ProjectPath, ConsultedLogtalkFilePaths, OutStream).
  333	
  334collect_ids_for_focus_file(FocusFile, Files, CalledPredicates, Calls):-
  335    findall(
  336    	Module:Name/Arity,
  337    	predicate_in_file(FocusFile, Module, Name, Arity),
  338    	OwnPredicates
  339    ),
  340    collect_calls_to_predicates(OwnPredicates, [], IncomingCalls),
  341    collect_calling_predicates_and_files(IncomingCalls, OwnPredicates, CallingPreds, [FocusFile], CallingFiles),
  342    list_to_set(CallingFiles, CallingFiles0),
  343    collect_calls_from_predicates(OwnPredicates,[], OutgoingCalls, FocusFile),
  344    collect_called_predicates_and_files(OutgoingCalls, CallingPreds, AllPreds, CallingFiles0, AllFiles),
  345    list_to_set(AllPreds, CalledPredicates),
  346    list_to_set(AllFiles, Files),
  347    append(IncomingCalls, OutgoingCalls, CallsList),
  348    list_to_set(CallsList, Calls).
  349    
  350collect_calls_to_predicates([],KnownCalls,KnownCalls).
  351collect_calls_to_predicates([TargetModule:TargetName/TargetArity|OtherPredicates], KnownCalls, AllCalls):-
  352    findall(
  353    	(SourceModule:SourceName/SourceArity, TargetModule:TargetName/TargetArity),
  354    	calls(TargetModule, TargetName, TargetArity, SourceModule, SourceName, SourceArity, _NumberOfCalls),
  355    	FoundCalls
  356    ),
  357    (	FoundCalls \= []
  358   	->	(	append(FoundCalls,KnownCalls,CallList), 
  359   			list_to_set(CallList,CallSet)
  360   		)
  361   	;	CallSet = KnownCalls
  362   	),
  363    collect_calls_to_predicates(OtherPredicates,CallSet,AllCalls).
  364    
  365collect_calls_from_predicates([],KnownCalls,KnownCalls, _FocusFile).
  366collect_calls_from_predicates([SourceModule:SourceName/SourceArity|OtherPredicates],KnownCalls,AllCalls, FocusFile):-
  367    findall(
  368    	(SourceModule:SourceName/SourceArity, TargetModule:TargetName/TargetArity),
  369		(	functor(SourceHead, SourceName, SourceArity),
  370			(	predicate_property(SourceModule:SourceHead, multifile)
  371			->	calls_multifile(TargetModule, TargetName, TargetArity, SourceModule, SourceName, SourceArity, FocusFile, _)
  372	    	;	calls(TargetModule, TargetName, TargetArity, SourceModule, SourceName, SourceArity, _)
  373	    	)
  374    	),
  375    	FoundCalls
  376    ),
  377	(	FoundCalls \= []
  378   	->	(	append(FoundCalls,KnownCalls,CallList), 
  379   			list_to_set(CallList,CallSet)
  380   		)
  381   	;	CallSet = KnownCalls
  382   	),
  383    collect_calls_from_predicates(OtherPredicates, CallSet, AllCalls, FocusFile).   
  384    
  385collect_calling_predicates_and_files([],Preds,Preds,Files,Files).    
  386collect_calling_predicates_and_files([(SourceModule:SourceName/SourceArity,TargetModule:TargetName/TargetArity)|OtherCalls],KnownCalledPreds, CalledPreds,KnownCalledFiles,CalledFiles):-
  387    findall(
  388    	File,
  389		(	functor(SourceHead, SourceName, SourceArity),
  390			(	predicate_property(SourceModule:SourceHead, multifile)
  391			->	calls_multifile(TargetModule, TargetName, TargetArity, SourceModule, SourceName, SourceArity, File, _)
  392	    	;	predicate_property(SourceModule:SourceHead, file(File))
  393	    	)
  394    	),
  395    	Files
  396    ),
  397    append(Files, KnownCalledFiles, NewKnownCalledFiles),
  398    collect_calling_predicates_and_files(OtherCalls,[SourceModule:SourceName/SourceArity|KnownCalledPreds],CalledPreds,NewKnownCalledFiles,CalledFiles). 
  399 
  400    
  401collect_called_predicates_and_files([],Preds,Preds,Files,Files).    
  402collect_called_predicates_and_files([(_Caller,TargetModule:TargetName/TargetArity)|OtherCalls],KnownCalledPreds, CalledPreds,KnownCalledFiles,CalledFiles):-
  403    findall(File, file_of_predicate(TargetModule, TargetName, TargetArity, File), Files),
  404    append(Files, KnownCalledFiles, NewKnownCalledFiles),
  405    collect_called_predicates_and_files(OtherCalls,[TargetModule:TargetName/TargetArity|KnownCalledPreds],CalledPreds,NewKnownCalledFiles,CalledFiles).
  406    
  407%/*
  408% * write_files(+Stream)
  409% *    writes #### dito ####
  410% */
  411%write_all_files(RelativePath,Stream):-
  412%    forall(	fileT(Id,File,Module),
  413%    		(	write_file(Stream,RelativePath,all_preds,Id,File,Module),
  414%    			flush_output(Stream)
  415%    		)
  416%    	  ).
  417		
  418
  419write_files(RelativePath, Files, Filters, Stream):-
  420	forall(	
  421		member(File,Files),
  422		(	main_module_of_file(File,Module),
  423			write_file(Stream, RelativePath, Filters, File, Module),
  424    		flush_output(Stream)
  425    	)
  426    ).	
  427
  428%write_load_edges(Stream):-
  429%    forall(load_edge(LoadingFileId,FileId,_,_),
  430%    	(	(	fileT(LoadingFileId,_,_),
  431%    			fileT(FileId,_,_)
  432%    		)
  433%    	->	write_load_edge(Stream,LoadingFileId,FileId)
  434%    		%format(Stream,'<edge source="~w" target="~w"/>~n', [LoadingFileId, FileId])
  435%    	;	format('Problem with load-edge: ~w, ~w~n',[LoadingFileId, FileId])
  436%	    )
  437%	).
  438
  439
  440	
  441
  442%write_call_edges(Stream):-
  443%    ensure_call_graph_generated,
  444%    forall(call_edges_for_predicates(SourceId,TargetId,_Counter),
  445%    	(	write_call_edge(Stream,SourceId,TargetId)
  446%    	)
  447%    ).
  448    
  449
  450%pl_test_graph:-	
  451%    pl_test_graph(['Z:/Git-Data/pdt.git/pdt.runtime.builder/prolog-src'],'Z:/Workspaces/WorkspaceFresh/test6.graphml'). 
  452%pl_test_graph(Project, OutputFile):-
  453%	write_project_graph_to_file(Project, OutputFile).