Plans for stricter record semantics in R8

Luc Taesch <>
Sun Oct 15 21:09:54 CEST 2000


> Bjorn Gustavsson wrote:
> >
> > In R8, we preliminary plan to stricten the test for valid records
>
> Great. Are there any plans of inserting generic functions on records as
> well (or implementing O'Keef's suggestion from 1998)?
>

i would not mind a link ?


> Due to their
> implementation, it is today impossible to write generic code using
> different record types. There are however many cases where different
> records can be treated equally as they share some common denominators.
>
> Generic code of this kind is today handled by using the tuple
> representation of records. (Do as I say, don't do as I do is what I end
> up preaching....) And the code is static anyhow, as every time you add a
> new record, you have to recompile the file with the generic stuff (or go
> through a call back module which makes the code yucky and hard to
> follow).
>
> Functions (BIFs) could include
>
> get_record(RecordType) -> #RecordType{}
> get_record_name(Record) -> RecordType
> get_record_fields(RecordName) -> [FieldName1,...]
> lookup_record_field(Record, FieldName1) -> Element
>
here are the corresponding functions in attached files


Function : prototype/2
%% Purpose : create One Empty record  of type Table, according to Schema

%%---------------------------------------
%%---------------------------------------
%% Function : info/2
%% Purpose : entity | relations for a Record
%% Arg: entity| relation, Record, grmSchema
%%---------------------------------------
grm_mnesia:field_value(Record,Field)->
%%---------------------------------------
  grm_mnesia:retrieve(Table,Key) when atom(Key)
%%---------------------------------------
 Function :grm_mnesia: retrieve/3
%% Purpose : retrieve the records referenced by a given record
%% Arg: Relation, Record | [Record], Schema

in the attached grm_mnesia, files, ull find the corresponding functions.

(still work in progress).
they suppose your database is defined in a grm schema like

schema()->
      [
    {entity,

[description,indexing,responsabilities,services,suppliers,states,invariant,component,precondition,postcondition,predicate,menu,commands,queries,dialog]},

 {relation,
    [{component,composition,states},
     {states,reference,description},
     {component,reference,description},
     {component,composition,commands},
     {component,composition,queries},
  %   {features,reference,description},
%     {features,composition,commands},
 %    {features,composition,queries},

with manipulation functions in grm_schema
supported_relationships()->
 [attribute,dependency,reference,composition].

also an example kit_schema, and the kit_lib in case of dependencies.


this is work in progress, and need a lot of restructuring ( a few things
are in grm_schema/mnesia now, but need some to be moved out.)

(GRM stands for generic relationship management, i believe, (kilov work
on formal semantics, an iso standard now. (kilov worked in RM_ODP may be
known by you, telecom guys. i can search links if interested)

i was surprised how little overhead  i found (maybe 10-20%, i was
expecting 100-200, compared to "compiled coded" records. (well , i dont
have soft real time requirement)
btw, there is a few utilitaries that create db schema, or records
headers based on the schema

%% Purpose : give the records definition (.hrl)
%% Arg: headers,Schema
%% Return : -record (tablename,[fieldsname]), string-ified.
%%---------------------------------------
info(headers,S)->

%%---------------------------------------
%% Function : info/2
%% Purpose : give the table definition (like mnesia:save_to_text would)
%% Arg: table,Schema
%% Return : {table, [{tablename,[fieldsname]}]}
%%---------------------------------------

info(tables,S)->



got some more things, if interested.


> Just in time for xmas?? :-)
>
> Regards,
>
--
First, they ignore you.
Then, they laugh at you.
Then, they fight you.
Then, you win.

--- Gandhi.

Working code is what matter, not your market capitalization.
--Kurt granroth


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20001015/9a99bcad/attachment.html>
-------------- next part --------------
%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% The Initial Developer of the Original Code is luc abom.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id: grm_schema.erl,v 1.7 2000/10/13 16:59:29 luc Exp $
%%
-module(grm_schema).
-author('').
-compile([export_all]).

-export([new/1,
	 add/3,
	 fetch/2,fetch/3,
	composition/2,dependency/2,reference/2,
	supported_relationships/0]).

%%---------------------------------------
%% Function : new/1
%% Purpose : create a new grm schema
%% Arg: schema
%% Return : empty schema
%%---------------------------------------
new(schema)->schema([],[])
    .
%%---------------------------------------
%% Function : add/3
%% Purpose : add a new entity to the schema
%% Arg: entity, Entity= atom(),Schema
%% Return : updated schema
%%---------------------------------------
add(entity,E, S)->
    false= exist(entity,E,S), %%precondition: uniqueness
    schema(
      combine_item( E,fetch(entity,S)),
      fetch(relation,S));
%%---------------------------------------
%% Function : add/4
%% Purpose : add a new relation to the schema
%% Arg: relation, FromEntity= atom(),{relation, ToEntity},Schema
%% Return : updated schema
%%---------------------------------------

add(relation, Relation, S) -> 
    is_add_able(relation,Relation,S), %% precondition : valid relation
    schema(
      fetch(entity,S),
      combine_item(Relation,fetch(relation,S))).



%%---------------------------------------
%% Function : fetch/2
%% Purpose : fetch entities or relationsfrom the schema
%% Arg: entity,Schema, or relation, Schema
%% Return : list of entities {entity,[atoms()]}, or { relation, {entity,relation,[entities] 
%%---------------------------------------

fetch(entity,[{entity,E},{relation,R}])->
%    {value,{entity,K}}=lists:keysearch(entity,1,S),
    E
    ;
fetch(relation,[{entity,E},{relation,R}]) ->

    lists:keysort(2,R).




%%---------------------------------------
%% Function : fetch/3
%% Purpose : fetch relation (resp .specific relation) entity from the schema
%% Arg: relation | supportedRelations(), FromEntity= atom(),{relation, ToEntity},Schema
%% Return : [{{From,Rel,To}}] (=filtered schema)
%%---------------------------------------

fetch(relation,From,S) ->
    R=fetch(relation,S),
    K=lists:filter(fun({FromX,Rel,To})->
			   case FromX of
			       From->true;
			       _ ->false
			   end end
			   ,R)
    %{value,K}=lists:keysearch(From,1,R),
,	K;


fetch(Relation,From,S)->
    fetch(relation,From,Relation,S).


%% not exported
fetch(relation,From,Rel,S) ->
    R=fetch(relation,S),
    K=lists:filter(fun({FromX,RelX,To})->
			   case {FromX,RelX} of
			       {From,Rel}->true;
			       _ ->false
			   end 
		   end
		   ,R)
						%{value,K}=lists:keysearch(From,1,R),
	,	K.

%%------------------------------------------
%% internals
%%----------------------------------------




%%---------------------------------------
%% Function : schema/2
%% Purpose : build a schema structure 
%% Arg: EntityList= [atom()],relationList [relations()]}
%% Return : [{entity, EntityList},{relation,relationList}]
%%---------------------------------------

schema(EntityList,RelationList)->
    [{entity,EntityList},{relation,RelationList}].


relation(From,RelationType,To)->
    {From,RelationType,To}.

composition(From,To)->
    relation(From,composition,To).
reference(From,To)->
    relation(From,reference,To).
dependency(From,To)->
    relation(From,dependency,To).
attribute(From,To)->
    relation(From,attribute,To).
%%---------------------------------------
%% Function : is_valid/3
%% Purpose : check a relationship or an entity
%% Arg: Relationship,Schema
%% Return : true if both entity exists in the schema, the relation is supported, and doesnt exists already (unique)
%%---------------------------------------

is_add_able(relation,{From,RelationType,To},Schema)->
    case {
      lists:member(RelationType,supported_relationships()),
      exist(entity,From,Schema),
      exist(entity,To,Schema),
      exist(relation,RelationType,Schema)
     } of
	{true,true,true,false}->
	    true;
	_ ->
	    false
    end.
%%---------------------------------------
%% Function : exist/3
%% Purpose : check the existence of an  entity or a relationship in the schema
%% Arg: Entity= atom(),Schema
%% Return : true | false
%%---------------------------------------
exist(entity,Entity,Schema)->
     lists:member(Entity,fetch(entity,Schema));
exist(relation,Entity,Schema)->
     lists:member(Entity,fetch(relation,Schema)).

%%---------------------------------------
%% Function : /3
%% Purpose : check the existence of an  entity or a relationship in the schema
%% Arg: Entity= atom(),Schema
%% Return : true | false
%%---------------------------------------

supported_relationships()->
%    [composition,dependency,reference,attribute].
 [attribute,dependency,reference,composition].

%%---------------------------------------
%% Function : info/2
%% Purpose : check the schema  definition 
%% Arg: schema,Schema
%% Return : ok or wrong >reason
%%---------------------------------------
%% a schema is ok if
%% any relations relates existing entities
%% relations are among the supported ones.

append_true(true,Res)->Res;
    
append_true(Acc,true) ->Acc;
append_true(Acc,Res) ->[Res | Acc ].

is_valid(relation,{From,RelationType,To},Schema)->
    lists:foldl(
      fun({Fun,ErrMess},Acc)->
	      Res=case Fun() of
		      true->true;
		      _ -> { ErrMess,{From,RelationType,To}}
		  end,
	      append_true(Acc,Res)
      end
      ,true,
      [{fun()-> lists:member(RelationType,supported_relationships()) end,"relationship not supported"},

       {fun() ->exist(entity,From,Schema) end,"wrong source entity"},
       {fun()->exist(entity,To,Schema) end,"wrong target entity"}
						%   {fun()-> exist(relation,RelationType,Schema) end,"relation already exists"}
      ]
     );
is_valid(attribute,{From,RelationType,To},Schema)->
    lists:foldl(
      fun({Fun,ErrMess},Acc)->
	      Res= case Fun() of
		       true->true;
		       _ -> { ErrMess,{From,RelationType,To}}
		   end,

	      append_true(Acc,Res)

      end
      ,true,
      [{fun()->case RelationType of attribute->true;_->false end end,"wrong keyword:attribute expected"},

       {fun() ->exist(entity,From,Schema) end,"wrong source entity"},
       {fun()->not exist(entity,To,Schema) end," entity exists with attribute name"}

      ]
     ).


info(schema,Schema)-> 
    lists:foldl(fun(R={From,RelationType,To},Acc)->
			Res =case RelationType of
				 attribute -> is_valid(attribute,R,Schema);
				 _->
				     is_valid(relation,R,Schema) 
			     end,
			append_true(Res,Acc) 
		end,
		true,
		fetch(relation,Schema));

%%---------------------------------------
%% Function : info/2
%% Purpose : give the table definition (like mnesia:save_to_text would)
%% Arg: table,Schema
%% Return : {table, [{tablename,[fieldsname]}]}
%%---------------------------------------

info(tables,S)->
    {tables,lists:map(
	      fun(E)->
		      {E, [key |lists:map( fun({From,Rel,To})->
						   To
					   end, fetch(relation,E,S))]
		      }
	      end
	      , fetch(entity,S))
    }	;
%% Purpose : give the records definition (.hrl)
%% Arg: headers,Schema
%% Return : -record (tablename,[fieldsname]), string-ified.
%%---------------------------------------
info(headers,S)->
    {tables,T}=info(tables,S),
    lists:map(
      fun({Name,ListofFiels})->
	      ["-record(",
	       atom_to_list(Name),",",
	       io_lib:format("~p",[list_to_tuple(ListofFiels)]),").\n"] end
      ,T).
    

dump_to_recordfile(File) ->
    dump_to_recordfile(mnesia_lib:is_running(), file:open(File, write)).

dump_to_recordfile(yes, {ok, F}) ->
    file:write(F,list_to_binary("-module(testdata_auto).\n"
				"-compile([export_all]).\n"
				"-import(kit_db,[store/1]).\n"
				"-include(\"kit_schema.hrl\").\n"
				"test_data()->\n")),
    Tabs = lists:delete(schema, mnesia_lib:local_active_tables()),
    lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs),
    file:write(F,list_to_binary("ok.\n")
	      ),
    file:close(F);
dump_to_recordfile(_,_) -> error.

    
dump_tab(F, T) ->
    W = mnesia_lib:val({T, wild_pattern}),
    {atomic,All} = mnesia:transaction(fun() -> mnesia:match_object(W) end),
    R=lists:map(fun(Term) -> format_record(Term)end, All),
    file:write(F,list_to_binary(R))
	.




format_record(R)->
    [T|  FieldValue]=tuple_to_list(R),
    FieldName= mnesia_lib:val({T, attributes}),
    NameAndValue=lmerge(FieldName,FieldValue),
    ["ok=store(",$#,atom_to_list(T),${,
     [fr_elem(hd(NameAndValue))|fr_tail(tl(NameAndValue))],
     $},$),$,,$\n ].

fr_elem({Name,Value})->  [io_lib:format("~p",[Name])," = ",io_lib:format("~p",[Value])].
% [ioN,$=,V].

fr_tail([])->
    "";
fr_tail([H|T]) ->[[$,,fr_elem(H)] | fr_tail(T)]
.


lmerge(L1,L2)->
    lists:reverse(lmerge(L1,L2,[])).

lmerge([],_,A)->A;
lmerge(_,[],A)->A;
lmerge([H1|L1],[H2|L2],Acc)->
	  lmerge(L1,L2, [{H1,H2}|Acc] ).


    %%---------------------------------------utils
	combine_item(L1, []) ->
		      [L1];
	combine_item(L1, L2) ->
		      [L1]++L2.

-------------- next part --------------
%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% The Initial Developer of the Original Code is luc abom.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id: grm_mnesia.erl,v 1.11 2000/10/11 16:28:06 luc Exp $
%%
-module(grm_mnesia).
-author('').
-compile([export_all]).

-export([info/2,info/3]).
-include("kit_server.hrl").
-import(grm_schema,[fetch/3]).
%-import(kit_lib,[heading/1]).
-import(kit_lib_format,[for_all_sections/2]).
-include("mnesia_test_lib.hrl").

%%---------------------------------------
%% Function : info/2
%% Purpose : entity | relations for a Record
%% Arg: entity| relation, Record, grmSchema
%% Return : entity(), relations
%%---------------------------------------


info(entity, Record)-> element(1,Record).
    
info(relation, Record, Schema) ->
    fetch(relation,info(entity,Record),Schema).

%%---------------------------------------
%% Function : retrieve/3
%% Purpose : retrieve the records referenced by a given record 
%% Arg: Relation, Record | [Record], Schema
%% Return : empty schema
%%---------------------------------------
retrieve(Relation,Records,Schema) when list(Records)->
    lists:flatten(
      lists:map( fun(OneRecord)-> retrieve(Relation,OneRecord,Schema) end
		 ,Records));

retrieve(Relation,Record,Schema)->
    Relations= fetch(Relation,info(entity,Record),Schema),
    lists:flatten(
      lists:map( fun({_From,_Relation,To})->
			 Key=field_value(Record,To),
			 Res =retrieve_ft(To,Key,Record) 
		 end,
		 Relations))

	.

%%---------------------------------------
%% Function : retrieve_ft/3
%% Purpose : retrieve the records in a table, fault tolerant manner 
%% Arg: Table,Keys ,FatherRecord (just for error message)
%% Return : Record | [Record]
%%---------------------------------------
retrieve_ft(Table, Keys,Record) when list(Keys)->

    R=lists:map(fun(X)->retrieve_ft(Table,X,Record) end, Keys),
    K=lists:flatten(R),
    K
	;
retrieve_ft(Table,Keys,Record)  when atom(Keys)->

    case Res =retrieve(Table,Keys) of
	not_found -> 
	    kit_lib:info_report(?MODULE,
	      [
				      {retrieve_ft,"key pointing wrong: ignoring\n"},
				      {pointing_to,Table},{key_to,Keys},
				      {record,Record}]),
	    []; %fault tolerant, may be parametrable

	R -> [R]
    end.






%%---------------------------------------
%% Function : retrieve/2
%% Purpose : retrieve the records in a table 
%% Arg: Table,Keys 
%% Return : Record | [Record]
%%---------------------------------------
retrieve(Any, Keys) when list(Keys)->
    case catch list_to_atom(Keys) of
	{'EXIT',_} ->
	    lists:map(fun(X)->retrieve(Any,X) end, Keys);	   
	K -> retrieve(Any,K) 
    end;


%%not exported
retrieve(Table,Key) when atom(Key)->
    F = fun() ->
		mnesia:read({Table,Key})
	end,
    case mnesia:transaction(F) of
	{atomic,[T]}  -> T;
	Msg->
	    kit_lib:info_report(?MODULE,
	      [
				      {retrieve_2,"record not found\n"},
				      {table,Table},{key,Key},
				      {message,Msg}]),
	    not_found
    end.



%%return the #Record.Field
field_value(Record,Field)->
    R=   field_rank(
	   info(entity,Record),Field),
    element(R,Record).

%%Return the Rank from Field in Table
field_rank(Table, Field)->
    T2=	mnesia:table_info(Table,attributes),
    find_rank(Field,T2).
%%count th rank from X in L, +1 (for the record name)
find_rank(X,L)->
    find_rank(X,L,2).

find_rank(_,[],N)->not_found;
find_rank(X,[X|T],N)->
    N;
find_rank(X,[_|T],N) ->find_rank(X,T,N+1).

%%---------------------------------------
%% Function : prototype/2
%% Purpose : create One Empty record  of type Table, according to Schema
%% Arg: Table,Schema
%% Return : Record | [Record]
%%---------------------------------------
prototype(Table, Schema)->

    list_to_tuple(  [Table,new_key(Table) |
		     lists:map(fun(_)->
				       [] end,
			       fetch(relation,Table,Schema))]).
%%---------------------------------------
%% Function : new_key/1
%% Purpose : create a new key
%% Arg: Table
%% Return : Record | [Record]
%%---------------------------------------
%generate a new key refering to a table
new_key(Table) when atom(Table)->
    {{Y,M,D},{H,Mi,S}}= erlang:universaltime(),
						%random:seed(),Rand=random:uniform(999),
    {_,_,Rand}=now(),
    K=io_lib:format("~w~p~p~p~p~p~p",[Y,M,D,H,Mi,S,Rand]),
    list_to_atom(lists:append([atom_to_list(Table) |K])).
%%---------------------------------------
%% Function : add_link/2
%% Arg : Son, Father
%% Purpose: add the Son Record as a link into the Father (not in the db)
%% Return : FatherRecord
%%---------------------------------------

add_link(Son,Father)->
    SonName=info(entity,Son),
    Key=element(2,Son),
    SonRank=field_rank( info(entity,Father),SonName),
    CurrentValue=element(SonRank,Father),
    NewSource=setelement(SonRank,Father, [Key | CurrentValue ] ).
%%---------------------------------------
%% Function : new_structure/2
%% Purpose : create a strucuture of new (empty) records, according to Schema 
%% Arg: Table,Schema
%% Return : {Record,  [AssociatedRecord]
%%---------------------------------------
new(Table, Schema)->
    new(Table, Schema,[]).
new(Table, Schema,Acc2)->
    {NewRec, AssociatedRecords}
	= lists:mapfoldl(fun({From,RelationType,To},Acc)->
 				 new_element(RelationType,{To,Acc},Schema)
			 end,
			 [],fetch(relation,Table,Schema)),
    NK=new_key(Table),
    NR=list_to_tuple([Table,NK|NewRec]),
     {NR,[NR|lists:append([AssociatedRecords,Acc2])]}.


new_element(attribute,{Relation,AccListe},Schema)->
     {default(Relation), AccListe};
new_element(dependency,{Relation,AccListe},Schema) ->
    {default(Relation),AccListe};
new_element(reference,{To,AccListe},Schema) ->
    {NewRecord,AccListe2}=new(To,Schema,AccListe),

    {key(NewRecord), AccListe2};
new_element(composition,{To,AccListe},Schema) ->
     {NewRecord,AccListe2}=new(To,Schema,AccListe),

    {key(NewRecord), AccListe2}.
    

default(Relation)->atom_to_list(Relation).
key(R)->[element(2,R)].

%%% context related functions, to be cleaned
context(append_to_heirs,Record, Context)->
				?log("Record: ~p Context : ~p",[Record,Context]),
				Newheirs= [[Record] | context(heirs,Context)],

				REsult= Context#context{
					  heirs=Newheirs}.
context(new,Mode)->
    #context{heirs=[], mode=Mode};
context(relation,Context)->
    Context#context.relation;
context(mode,Context)->
    Context#context.mode;
context(funNext,Context)->
    Context#context.funNext;
context(heirs,Context)->
    father(Context#context.heirs);

context(context,Context)->Context;
context(Badarg,Context)->?log("badard:~p~n",[Badarg]).

%%---------------------------------------
%% Function : father/1
%% Purpose : the father of the curernt element in a context
%% Arg: [Record], = context
%% Return :  Record
%%---------------------------------------
father([])->[];
father(Context) when list(Context)->
    father(hd(Context));
father(Context)->
    Context.

%%initialise the navigation and thecontext
navigate(Build,Record,S,Context)->
    NewContext=Context#context{schema=S,
			       funBuild=Build,
			       allrelation= grm_schema:supported_relationships()},
   next(Record,[[{composition,Record}]],NewContext).


%%navigate the record
nav_it(Record,Context)->
    AllRec =
	instance_per_relation(
	  Record,Context#context.allrelation,Context#context.schema),

     I3=fsg(AllRec,Context#context.schema) , %filter, sort , group
    next(Record,I3,Context). %go on


next(Record,ListofDuplets,Context)->						%prepare next iteration
    NewContext1 =Context#context{
 		   heirs= [[Record] | Context#context.heirs]},
    NewContext=NewContext1#context{
		 funNext = fun(R1)-> nav_it(R1,NewContext1) end},
    Build=NewContext#context.funBuild,
    Build(section,ListofDuplets,NewContext).

instance_per_relation(Record,Relations,S)->%retrieve instances per relation, and build duplets

    lists:flatten(
      lists:foldl(
	fun(Rel,Acc)->
		Rec=lists:map(
		      fun(OneRec)->
			      {Rel,OneRec} end,
		      retrieve(Rel,Record,S)),
		[Rec | Acc]
	end,[],Relations)).

fsg(ListofDuplets,Schema)->
    %Fiter, Sort, and groups duplets of {relation, records}
     I1=grm_filter(ListofDuplets),
    I2=grm_sort(I1,Schema),
    I3= group_relation_and_records(I2),
    
%,    io:format("Allrec ~p~n Sorted:noG ~p~n I3:G ~p~n",[ListofDuplets,I2,I3]),
I3				
.

grm_filter(Allrec)->
    Allrec.

grm_sort(AllRec,Schema)->%sort relation first, then records
    OrderList=grm_schema:fetch(entity,Schema),
 F= fun(A,B)->
	       {RelA,RecA}=A,
	       {RelB,RecB}=B,
	       case RelA of
		   RelB-> compare_stuff(element(1,RecA) , element(1,RecB),OrderList);
		    _->% we ressort on ordering the relationships
		       compare_relationships(RelA,RelB)
	       end
	   end,
   
    lists:sort(F,AllRec).

%%---------------------------------------
%% Function : compare_relationships/2
%% Purpose : define an ordered relation on GRM relationships
%% Arg:  R1,R2 = supportedrelationships()
%% Return : true if R1 >R2
%%---------------------------------------



compare_relationships(R1,R2)->
    OrderList=[attribute,reference,composition,dependency],
    compare_stuff(R1,R2,OrderList).

compare_stuff(R1,R2, OrderList)->

    RankR1=find_rank(R1,OrderList),
     RankR2=find_rank(R2,OrderList),
    RankR1<RankR2.







%%---------------------------------------
%% Function : group/1
%% Purpose : the grouped list , ie [a,a,a,b,b]->[[a,a,a],[b,b]]
%% Arg: [sortedRecords], fun to compare equality of records
%% Return :  [[groupedRecord],..]
%%---------------------------------------


group_with_key(L,Key)->
    {ResList,LastCurrent}=lists:foldl(
		   fun(Rec,Acc)->{GroupList,CurrentGroup}=Acc,
			   case CurrentGroup of
			       []->{GroupList,[Rec]};
			       H ->
				   CurrentRecordName = Key(hd(H)),
				   case Key(Rec) of
				       CurrentRecordName -> %same as current
					   {GroupList,[Rec | H ]};
				       _ -> %other record, new sublist
					   {[H| GroupList ],[Rec]}
				   end
			   end			   
		   end
		   , {[],[]},L),
    lists:reverse([LastCurrent|ResList])
.

group_records(L)->
    F= fun(X)->
	       element(1,X) end,
    group_with_key(L,F).

group_relation_and_records([])->[];

group_relation_and_records(L)->
%assuming L =[{rel,Record}]
    F= fun(X)->{_Rel,Rec}=X,
	       element(1,Rec) end,
    group_with_key(L,F).





-------------- next part --------------
%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% The Initial Developer of the Original Code is luc abom.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id: kit_lib_format.erl,v 1.8 2000/10/11 16:28:06 luc Exp $
%%
-module(kit_lib_format).
-author('').
-compile([export_all]).
-include("mnesia_test_lib.hrl").
-include("kit_server.hrl").
-include("kit_schema.hrl").
-import(grm_mnesia,[info/2,info/3,context/2]).
-import(kit_lib,[forge_link/1, heading/1,father/1]).





%-export([new/1,
%	 add/3,
%	 fetch/2,fetch/3,
%	composition/2,dependency/2,reference/2,
%	supported_relationships/0]).

%%---------------------------------------
%% Function : grm_browse/3
%% Purpose : browse a grm schema, from a record
%% Arg: relation, the record to browse, grm_schema, the Fun to naivigate the schema, the context of the current element
%% Return : a formatting tree
%%---------------------------------------
grm_browse(Rel,[],Context) ->
    kit_lib:info_report(?MODULE,[
			      {grm_browse_3,"received empty record: ignoring"},
			      {relation,Rel},
			      {context,Context}]),
    [];
%%following are relation related relates
grm_browse(reference,Record,Context)->
    build_format(desc,Record, Context);
grm_browse(composition,Record,Context)->
    Next=context(funNext,Context),
    Next(Record);
grm_browse(dependency,Record,Context) ->[];
grm_browse(attribute,Record,Context) ->[];
%%following are section related
grm_browse(section,Record,Context) ->
    for_all_sections(Record,Context)
.


for_all_sections(Records,Context)->
    lists:flatmap(fun(RecordSection)-> 
			  {Rel,Rec}=hd(RecordSection),
			  case Rel of
			      composition ->
				  [{section, [{heading,  heading(Rec)}],
				    lists:append(
				      [buttons_for_leafs(Rec,Context),
				       for_all_records(RecordSection,Context)])  }];
			      reference ->for_all_records(RecordSection,Context)
			  end
		  end, Records)

	.
%% Build all the Records
for_all_records(Records,Context)->
    Build=Context#context.funBuild,
    lists:flatmap(fun(Arg)->{Rel,Rec}=Arg,
			    Build(Rel,Rec,Context)
		  end, Records)
	.

buttons_for_leafs(Record,Context)->
    case context(mode,Context) of
	edit ->
	    Relations=info(relation,Record,Context#context.schema),
	    Re=lists:map( fun({From,Rel,To})->To end, Relations), 
	    R=io_lib:format("~p~p",[Re,Relations]),
%	    io:format("R:~p~n",[R]),
	    [{input,
	      [{type,"submit"},{name,name_for_submit("add",Record,Context)},{value,"+"}],
	      []},
 	     selector(Re,name_for_submit("select",Record,Context))];
	_->[]
    end
.

%%% format related functions


build_format(desc,Record, Context)->
    case context(mode, Context) of
	browse->
	    desc_to_para2(Record,kit_lib:forge_link(context(heirs,Context)));
	edit -> desc_to_input(Record);
	link-> link_to_para(Record);
	_Donk ->?log("shit mode :~p~n",[_Donk])
    end
	.



%desc(Type, L,Link) when list(L) ->
%    lists:flatten(lists:map(fun(X)->desc(Type,X,Link) end, L));
%desc(list, A,Link)  when record(A,states) ->
%    desc_to_para(A#states.description,forge_link(A));
%desc(list, A,Link)  when record(A,features) ->
%    desc_to_para(A#features.description,forge_link(A));
desc(list, A,Link)   ->
    desc_to_para(A,Link).

desc_to_para(C) when record(C,description)->
    [{link,
      [{linkend,"testlink"}],
      C#description.name},
     {'P', C#description.definition}].
desc_to_para(C,Link) when record(C,description)
			  ->   [{link,
				 [{linkend,Link}],
				 C#description.name},
				{'P', C#description.definition}].
desc_to_para2(C,Link) when record(C,description)
			   ->   [{'P',[
				      {link,
				       [{linkend,Link}],
				       C#description.name},
				      {data, ":"},
				      {data,C#description.definition}]}].
desc_to_glossentry(C,Link) when record(C,description)
				->   [{glossentry,
				       [{glossterm,
					 [{link,[{linkend,Link}],C#description.name}]},
					{glossdef, C#description.definition}]
				      }].

desc_to_input(C) when record(C,description)->
    [{'P',[input_field("name",C#description.key,C#description.name),
    input_textarea("definition",C#description.key,C#description.definition)
	]}].

link_to_para( A)  when record(A,menu) ->
    [{'P',link_to_para(A#menu.text,A#menu.link)}].


link_to_para(Text, Link) ->
 [{link,[{linkend,Link},{target,"main"}], Text}].



%%%%%%%%%%%%%%%%%%%%
%%  utils

input_field(Name,Key,Value)->
  {input,[{type,"text"},
	    {name,name_with_key(Name,Key)},
	    {value,Value}],
     ""}.
input_textarea(Name,Key,Value)->
    {textarea,[{name,name_with_key(Name,Key)},
	       {row,4},{column,70}],
     Value}.
selector(List,Name)->
    %create a list of option from a list of atoms
    {select,[{name,Name}],
		lists:map(fun(O)->{option,atom_to_list(O)} end, List) 
 		}.


name_with_key(Name, Key) when atom(Key) ->
%produce a NAME+_+KEY value, as string, to be used in field name identifier in forms
    Name++"_"++atom_to_list(Key).



name_for_submit(Action,Record,Context)->
%%produce a name with the action_Table++Key
    Action++"_"++name_with_record(Record).



name_with_record(Record)->
%%produce Table++Key
    atom_to_list(element(1,Record)) ++ atom_to_list(element(2,Record)).
-------------- next part --------------
-module(kit_schema).
-compile(export_all).

 
   
schema()->
      [{entity,
	[description,indexing,responsabilities,services,suppliers,states,invariant,component,precondition,postcondition,predicate,menu,commands,queries,dialog]},
	{relation,[{component,composition,states},
		   {states,reference,description},
		   {component,reference,description},
		   {component,composition,commands},
		   {component,composition,queries},
		%   {features,reference,description},
%		   {features,composition,commands},
	%	   {features,composition,queries},
		   {description, attribute,name},
		   {description, attribute,definition},
		   {menu,attribute,text},
		   {menu,attribute,link},
		   {menu,attribute,order},
		   {states,composition,precondition},
		   {states,composition,postcondition},
		   {precondition,reference,description},
		   {postcondition,reference,description},
		   {postcondition,composition,predicate}, 
		   {precondition,composition,predicate},
		   {predicate,reference,description},
		   {commands,reference,description},
		   {queries, reference,description},
		   {queries,composition,precondition},
		   {commands,composition,precondition}, 
		   {commands,composition,postcondition},
		   {invariant,reference,description},
		   {invariant,composition,predicate},
		   {indexing,reference,description},
		   {component,composition,indexing}, 
		   {responsabilities,reference,description},
		   {component,composition,responsabilities},
		   {suppliers,reference,description},
		   {component,composition,suppliers},
		   {services,reference,description},
		   {component,composition,services},
		   {dialog,reference,description},
		   {component,composition,dialog}
		  ]}] .

update_header()->
    true =grm_schema:info(schema,schema()),%precondition

    
     F=
	   grm_schema:info(headers,schema()), 
    write_file("../inc/kit_schema.hrl",lists:flatten(F)).
write_file(Fname,Tree)->

 
	    Pname = Fname, %"./kit.sgml",
	    case file:write_file(Pname, list_to_binary(Tree)) of
		ok -> 
		    io:format(": written file: ~p~n",
			      [Pname]);
		{error,Reason} -> 
		    io:format("writing mail failed, reason: ~p~n",
			      [?MODULE,?LINE,Reason])
		    
	    end.


More information about the erlang-questions mailing list