(* camlp4r ./pa_html.cmo *) (* $Id: relationLink.ml,v 4.12 2004/12/14 09:30:17 ddr Exp $ *) (* Copyright (c) 1998-2005 INRIA *) open Config; open Def; open Gutil; open Util; (* Algorithm *) type info = { ip : iper; sp : sex; ip1 : iper; ip2 : iper; b1 : list (iper * sex); b2 : list (iper * sex); c1 : int; c2 : int; pb1 : option (list (iper * sex)); pb2 : option (list (iper * sex)); nb1 : option (list (iper * sex)); nb2 : option (list (iper * sex)); sp1 : option person; sp2 : option person; bd : int; td_prop : string } ; type dist = { dmin : mutable int; dmax : mutable int; mark : bool }; value infinity = 1000; value threshold = ref 10; value phony_dist_tab = (fun _ -> 0, fun _ -> infinity); value tsort_leq tstab x y = if tstab.(x) = tstab.(y) then x >= y else tstab.(x) < tstab.(y) ; value make_dist_tab conf base ia maxlev = if maxlev <= threshold.val then phony_dist_tab else (* optimization to be used 1/ if database not too big or 2/ running on machines with much memory *) (* let _ = base.data.unions.array () in let _ = base.data.descends.array () in *) (**) let tstab = Util.create_topological_sort conf base in let module Pq = Pqueue.Make (struct type t = int; value leq x y = not (tsort_leq tstab x y); end) in let default = {dmin = infinity; dmax = 0; mark = False} in let dist = Array.create base.data.persons.len default in let q = ref Pq.empty in let add_children ip = let u = uget conf base ip in for i = 0 to Array.length u.family - 1 do { let des = doi base u.family.(i) in for j = 0 to Array.length des.children - 1 do { let k = Adef.int_of_iper des.children.(j) in let d = dist.(k) in if not d.mark then do { dist.(k) := {dmin = infinity; dmax = 0; mark = True}; q.val := Pq.add k q.val } else () } } in do { dist.(Adef.int_of_iper ia) := {dmin = 0; dmax = 0; mark = True}; add_children ia; while not (Pq.is_empty q.val) do { let (k, nq) = Pq.take q.val in q.val := nq; match parents (aget conf base (Adef.iper_of_int k)) with [ Some ifam -> let cpl = coi base ifam in let dfath = dist.(Adef.int_of_iper (father cpl)) in let dmoth = dist.(Adef.int_of_iper (mother cpl)) in do { dist.(k).dmin := min dfath.dmin dmoth.dmin + 1; dist.(k).dmax := max dfath.dmax dmoth.dmax + 1; if dist.(k).dmin > maxlev then () else add_children (Adef.iper_of_int k) } | None -> () ] }; (fun ip -> dist.(Adef.int_of_iper ip).dmin, fun ip -> dist.(Adef.int_of_iper ip).dmax) } ; value find_first_branch conf base (dmin, dmax) ia = find [] where rec find br len ip sp = if ip == ia then if len == 0 then Some br else None else if len == 0 then None else if len < dmin ip || len > dmax ip then None else match parents (aget conf base ip) with [ Some ifam -> let cpl = coi base ifam in match find [(ip, sp) :: br] (len - 1) (father cpl) Male with [ Some _ as r -> r | None -> find [(ip, sp) :: br] (len - 1) (mother cpl) Female ] | None -> None ] ; value rec next_branch_same_len conf base dist backward missing ia sa ipl = if backward then match ipl with [ [] -> None | [(ip, sp) :: ipl1] -> match sa with [ Female -> next_branch_same_len conf base dist True (missing + 1) ip sp ipl1 | Male -> match parents (aget conf base ip) with [ Some ifam -> let cpl = coi base ifam in next_branch_same_len conf base dist False missing (mother cpl) Female ipl | _ -> failwith "next_branch_same_len" ] | Neuter -> assert False ] ] else if missing == 0 then Some (ia, sa, ipl) else if missing < fst dist ia || missing > snd dist ia then next_branch_same_len conf base dist True missing ia sa ipl else match parents (aget conf base ia) with [ Some ifam -> let cpl = coi base ifam in next_branch_same_len conf base dist False (missing - 1) (father cpl) Male [(ia, sa) :: ipl] | None -> next_branch_same_len conf base dist True missing ia sa ipl ] ; value find_next_branch conf base dist ia sa ipl = loop ia sa ipl where rec loop ia1 sa1 ipl = match next_branch_same_len conf base dist True 0 ia1 sa1 ipl with [ Some (ia1, sa1, ipl) -> if ia == ia1 then Some ipl else loop ia1 sa1 ipl | _ -> None ] ; value rec prev_branch_same_len conf base dist backward missing ia sa ipl = if backward then match ipl with [ [] -> None | [(ip, sp) :: ipl1] -> match sa with [ Male -> prev_branch_same_len conf base dist True (missing + 1) ip sp ipl1 | Female -> match parents (aget conf base ip) with [ Some ifam -> let cpl = coi base ifam in prev_branch_same_len conf base dist False missing (father cpl) Male ipl | _ -> failwith "prev_branch_same_len" ] | Neuter -> assert False ] ] else if missing == 0 then Some (ia, sa, ipl) else if missing < fst dist ia || missing > snd dist ia then prev_branch_same_len conf base dist True missing ia sa ipl else match parents (aget conf base ia) with [ Some ifam -> let cpl = coi base ifam in prev_branch_same_len conf base dist False (missing - 1) (mother cpl) Female [(ia, sa) :: ipl] | None -> prev_branch_same_len conf base dist True missing ia sa ipl ] ; value find_prev_branch conf base dist ia sa ipl = loop ia sa ipl where rec loop ia1 sa1 ipl = match prev_branch_same_len conf base dist True 0 ia1 sa1 ipl with [ Some (ia1, sa1, ipl) -> if ia == ia1 then Some ipl else loop ia1 sa1 ipl | _ -> None ] ; (* Printing *) value someone_text conf base ip = let p = pget conf base ip in referenced_person_title_text conf base p ^ Date.short_dates_text conf base p ; value spouse_text conf base end_sp ip ipl = match (ipl, (p_getenv conf.env "spouse", p_getenv conf.env "opt")) with [ ([(ips, _) :: _], (Some "on", _) | (_, Some "spouse")) -> let a = aget conf base ips in match parents a with [ Some ifam -> let c = coi base ifam in let fam = foi base ifam in let sp = if ip = (father c) then (mother c) else (father c) in let d = Date.short_marriage_date_text conf base fam (pget conf base (father c)) (pget conf base (mother c)) in (someone_text conf base sp, d, Some sp) | _ -> ("", "", None) ] | ([], _) -> match end_sp with [ Some p -> (someone_text conf base p.cle_index, "", None) | _ -> ("", "", None) ] | _ -> ("", "", None) ] ; value print_someone_and_spouse conf base info in_tab ip n ipl = let (s, d, spo) = spouse_text conf base n ip ipl in do { if in_tab && (info.bd > 0 || info.td_prop <> "") then Wserver.wprint "
" info.bd info.td_prop else (); Wserver.wprint "%s\n" (someone_text conf base ip); Wserver.wprint "%s" (Dag.image_txt conf base (pget conf base ip)); if s <> "" then do { Wserver.wprint "&%s" d; html_br conf; Wserver.wprint "%s\n" s; match spo with [ Some ip -> Wserver.wprint "%s" (Dag.image_txt conf base (pget conf base ip)) | _ -> () ] } else (); if in_tab && (info.bd > 0 || info.td_prop <> "") then Wserver.wprint "
" else (); } ; value rec print_both_branches conf base info pl1 pl2 = if pl1 = [] && pl2 = [] then () else do { let (p1, pl1) = match pl1 with [ [(p1, _) :: pl1] -> (Some p1, pl1) | [] -> (None, []) ] in let (p2, pl2) = match pl2 with [ [(p2, _) :: pl2] -> (Some p2, pl2) | [] -> (None, []) ] in tag "tr" "align=left" begin stag "td" "align=center" begin match p1 with [ Some p1 -> Wserver.wprint "|" | None -> Wserver.wprint " " ]; end; stag "td" begin Wserver.wprint " "; end; stag "td" "align=center" begin match p2 with [ Some p2 -> Wserver.wprint "|" | None -> Wserver.wprint " " ]; end; Wserver.wprint "\n"; end; tag "tr" "align=left" begin tag "td" "valign=top align=center width=\"50%%\"" begin match p1 with [ Some p1 -> print_someone_and_spouse conf base info True p1 info.sp1 pl1 | None -> Wserver.wprint " " ]; end; tag "td" begin Wserver.wprint " "; end; tag "td" "valign=top align=center width=\"50%%\"" begin match p2 with [ Some p2 -> print_someone_and_spouse conf base info True p2 info.sp2 pl2 | None -> Wserver.wprint " " ]; end; end; print_both_branches conf base info pl1 pl2 } ; value rec print_both_branches_pre conf base info sz pl1 pl2 = if pl1 = [] && pl2 = [] then () else do { let (p1, pl1) = match pl1 with [ [(p1, _) :: pl1] -> (Some p1, pl1) | [] -> (None, []) ] in let (p2, pl2) = match pl2 with [ [(p2, _) :: pl2] -> (Some p2, pl2) | [] -> (None, []) ] in let s1 = match p1 with [ Some p1 -> "|" | None -> " " ] in let s2 = match p2 with [ Some p2 -> "|" | None -> " " ] in print_pre_center sz (s1 ^ String.make (sz / 2) ' ' ^ s2); match p1 with [ Some p1 -> do { print_pre_left sz (someone_text conf base p1); let (s, d, _) = spouse_text conf base info.sp1 p1 pl1 in if s <> "" then print_pre_left sz ("&" ^ d ^ " " ^ s) else () } | None -> Wserver.wprint "\n" ]; match p2 with [ Some p2 -> do { print_pre_right sz (someone_text conf base p2); let (s, d, _) = spouse_text conf base info.sp2 p2 pl2 in if s <> "" then print_pre_right sz ("&" ^ d ^ " " ^ s) else () } | None -> Wserver.wprint "\n" ]; print_both_branches_pre conf base info sz pl1 pl2 } ; value include_marr conf base n = match find_person_in_env conf base n with [ Some p -> ";" ^ acces_n conf base n p | None -> "" ] ; value sign_text conf base sign info b1 b2 c1 c2 = " ";spouse=on" | _ -> "" ]) ^ (match p_getenv conf.env "image" with [ Some "on" -> ";image=on" | _ -> "" ]) ^ (match p_getenv conf.env "bd" with [ None | Some ("0" | "") -> "" | Some x -> ";bd=" ^ x ]) ^ (match p_getenv conf.env "td" with [ None | Some "" -> "" | Some x -> ";td=" ^ x ]) ^ (match p_getenv conf.env "color" with [ None | Some "" -> "" | Some x -> ";color=" ^ x ]) ^ include_marr conf base "3" ^ include_marr conf base "4" ^ "\">" ^ sign ^ "" ; value prev_next_1_text conf base info pb nb = let s = match pb with [ Some b1 -> let sign = "<<" in sign_text conf base sign info b1 info.b2 (info.c1 - 1) info.c2 ^ " " | _ -> "" ] in let s = match (pb, nb) with [ (None, None) -> s | _ -> s ^ "" ^ string_of_int info.c1 ^ "" ] in match nb with [ Some b1 -> let sign = ">>" in s ^ " " ^ sign_text conf base sign info b1 info.b2 (info.c1 + 1) info.c2 | _ -> s ] ; value prev_next_2_text conf base info pb nb = let s = match pb with [ Some b2 -> let sign = "<<" in sign_text conf base sign info info.b1 b2 info.c1 (info.c2 - 1) ^ " " | _ -> "" ] in let s = match (pb, nb) with [ (None, None) -> s | _ -> s ^ "" ^ string_of_int info.c2 ^ "" ] in match nb with [ Some b2 -> let sign = ">>" in s ^ " " ^ sign_text conf base sign info info.b1 b2 info.c1 (info.c2 + 1) | _ -> s ] ; value print_prev_next_1 conf base info pb nb = Wserver.wprint "%s\n" (prev_next_1_text conf base info pb nb) ; value print_prev_next_2 conf base info pb nb = Wserver.wprint "%s\n" (prev_next_2_text conf base info pb nb) ; value other_parent_text_if_same conf base info = match (info.b1, info.b2) with [ ([(sib1, _) :: _], [(sib2, _) :: _]) -> match (parents (aget conf base sib1), parents (aget conf base sib2)) with [ (Some ifam1, Some ifam2) -> let cpl1 = coi base ifam1 in let cpl2 = coi base ifam2 in let other_parent = if (father cpl1) = info.ip then if (mother cpl1) = (mother cpl2) then Some (mother cpl1) else None else if (father cpl1) = (father cpl2) then Some (father cpl1) else None in match other_parent with [ Some ip -> let d = Date.short_marriage_date_text conf base (foi base ifam1) (pget conf base (father cpl1)) (pget conf base (mother cpl1)) in Some ("&" ^ d ^ " " ^ someone_text conf base ip, ip) | _ -> None ] | _ -> None ] | _ -> None ] ; value print_someone_and_other_parent_if_same conf base info = do { if info.bd > 0 || info.td_prop <> "" then Wserver.wprint "
" info.bd info.td_prop else (); Wserver.wprint "%s\n" (someone_text conf base info.ip); Wserver.wprint "%s" (Dag.image_txt conf base (pget conf base info.ip)); match other_parent_text_if_same conf base info with [ Some (s, ip) -> do { Wserver.wprint "%s" s; Wserver.wprint "%s" (Dag.image_txt conf base (pget conf base ip)) } | None -> () ]; if info.bd > 0 || info.td_prop <> "" then Wserver.wprint "
" else (); } ; value rec list_iter_hd_tl f = fun [ [x :: l] -> do { f x l; list_iter_hd_tl f l } | [] -> () ] ; value print_one_branch_no_table conf base info = let b = if info.b1 = [] then info.b2 else info.b1 in let sp = if info.b1 = [] then info.sp2 else info.sp1 in tag "center" begin print_someone_and_spouse conf base info False info.ip sp b; html_br conf; list_iter_hd_tl (fun (ip1, _) ipl1 -> do { Wserver.wprint "|"; html_br conf; print_someone_and_spouse conf base info False ip1 sp ipl1; html_br conf; }) b; end ; value print_one_branch_with_table conf base info = let b = if info.b1 = [] then info.b2 else info.b1 in let sp = if info.b1 = [] then info.sp2 else info.sp1 in tag "table" "border=%d cellspacing=0 cellpadding=0 width=\"100%%\"" conf.border begin tag "tr" begin tag "td" "align=center" begin print_someone_and_spouse conf base info True info.ip sp b; end; list_iter_hd_tl (fun (ip1, _) ipl1 -> do { tag "tr" begin tag "td" "align=center" begin Wserver.wprint "|"; end; end; tag "tr" begin tag "td" "align=center" begin print_someone_and_spouse conf base info True ip1 sp ipl1; end; end; }) b; end; end ; value print_two_branches_with_pre conf base info = let sz = 79 in tag "pre" begin print_pre_center sz (someone_text conf base info.ip); match other_parent_text_if_same conf base info with [ Some (s, ip) -> print_pre_center sz s | None -> () ]; print_pre_center sz "|"; print_pre_center sz (String.make (sz / 2) '_'); print_both_branches_pre conf base info sz info.b1 info.b2; if info.pb1 <> None || info.nb1 <> None || info.pb2 <> None || info.nb2 <> None then do { Wserver.wprint "\n"; if info.pb1 <> None || info.nb1 <> None then let s = prev_next_1_text conf base info info.pb1 info.nb1 in print_pre_left sz s else (); if info.pb2 <> None || info.nb2 <> None then let s = prev_next_2_text conf base info info.pb2 info.nb2 in print_pre_right sz s else () } else (); end ; value print_two_branches_with_table conf base info = tag "table" "border=%d cellspacing=0 cellpadding=0 width=\"100%%\"" conf.border begin tag "tr" "align=left" begin stag "td" "colspan=3 align=center" begin print_someone_and_other_parent_if_same conf base info; end; end; tag "tr" "align=left" begin stag "td" "colspan=3 align=center" begin Wserver.wprint "|"; end; end; tag "tr" "align=left" begin stag "td" "align=right" begin Wserver.wprint "
"; end; Wserver.wprint "\n"; stag "td" begin Wserver.wprint "
"; end; Wserver.wprint "\n"; stag "td" "align=left" begin Wserver.wprint "
"; end; Wserver.wprint "\n"; end; print_both_branches conf base info info.b1 info.b2; if not conf.cancel_links && (info.pb1 <> None || info.nb1 <> None || info.pb2 <> None || info.nb2 <> None) then tag "tr" "align=left" begin tag "td" begin if info.pb1 <> None || info.nb1 <> None then do { html_br conf; print_prev_next_1 conf base info info.pb1 info.nb1 } else Wserver.wprint " "; end; tag "td" begin Wserver.wprint " "; end; tag "td" begin if info.pb2 <> None || info.nb2 <> None then do { html_br conf; print_prev_next_2 conf base info info.pb2 info.nb2 } else Wserver.wprint " "; end; end else (); end ; value print_relation_path conf base info = let with_table = match p_getenv conf.env "tab" with [ Some "on" -> True | Some "off" -> False | _ -> not (browser_doesnt_have_tables conf) ] in if info.b1 = [] || info.b2 = [] then do { if (info.bd > 0 || info.td_prop <> "") && with_table then print_one_branch_with_table conf base info else print_one_branch_no_table conf base info; if not conf.cancel_links && (info.pb1 <> None || info.nb1 <> None || info.pb2 <> None || info.nb2 <> None) then do { html_br conf; if info.pb1 <> None || info.nb1 <> None then print_prev_next_1 conf base info info.pb1 info.nb1 else (); if info.pb2 <> None || info.nb2 <> None then print_prev_next_2 conf base info info.pb2 info.nb2 else () } else () } else if with_table then print_two_branches_with_table conf base info else print_two_branches_with_pre conf base info ; value print_relation_ok conf base info = let title _ = do { Wserver.wprint "%s" (capitale (transl_nth conf "relationship link/relationship links" 0)); match (info.pb1, info.nb1) with [ (None, None) -> () | _ -> Wserver.wprint " %d" info.c1 ]; match (info.pb2, info.nb2) with [ (None, None) -> () | _ -> Wserver.wprint " %d" info.c2 ] } in do { header_no_page_title conf title; print_relation_path conf base info; trailer conf } ; value print_relation_no_dag conf base po ip1 ip2 = let params = match (po, p_getint conf.env "l1", p_getint conf.env "l2") with [ (Some p, Some l1, Some l2) -> let ip = p.cle_index in let dist = make_dist_tab conf base ip (max l1 l2 + 1) in let b1 = find_first_branch conf base dist ip l1 ip1 Neuter in let b2 = find_first_branch conf base dist ip l2 ip2 Neuter in Some (ip, (pget conf base ip).sex, dist, b1, b2, 1, 1) | _ -> match (p_getenv conf.env "b1", p_getenv conf.env "b2") with [ (Some b1str, Some b2str) -> let n1 = Num.of_string b1str in let n2 = Num.of_string b2str in match (branch_of_sosa conf base ip1 n1, branch_of_sosa conf base ip2 n2) with [ (Some [(ia1, sa1) :: b1], Some [(ia2, sa2) :: b2]) -> if ia1 == ia2 then let c1 = match p_getint conf.env "c1" with [ Some n -> n | None -> 0 ] in let c2 = match p_getint conf.env "c2" with [ Some n -> n | None -> 0 ] in let dist = if c1 > 0 || c2 > 0 then let maxlev = max (List.length b1) (List.length b2) + 1 in make_dist_tab conf base ia1 maxlev else phony_dist_tab in Some (ia1, sa1, dist, Some b1, Some b2, c1, c2) else None | _ -> None ] | _ -> None ] ] in match params with [ Some (ip, sp, dist, Some b1, Some b2, c1, c2) -> let pb1 = if c1 <= 1 then None else find_prev_branch conf base dist ip sp b1 in let nb1 = if c1 == 0 then None else find_next_branch conf base dist ip sp b1 in let pb2 = if c2 <= 1 then None else find_prev_branch conf base dist ip sp b2 in let nb2 = if c2 == 0 then None else find_next_branch conf base dist ip sp b2 in let sp1 = find_person_in_env conf base "3" in let sp2 = find_person_in_env conf base "4" in let bd = match p_getint conf.env "bd" with [ Some x -> x | None -> 0 ] in let td_prop = match Util.p_getenv conf.env "td" with [ Some x -> " " ^ x | _ -> match Util.p_getenv conf.env "color" with [ None | Some "" -> "" | Some x -> " bgcolor=" ^ x ] ] in let info = {ip = ip; sp = sp; ip1 = ip1; ip2 = ip2; b1 = b1; b2 = b2; c1 = c1; c2 = c2; pb1 = pb1; pb2 = pb2; nb1 = nb1; nb2 = nb2; sp1 = sp1; sp2 = sp2; bd = bd; td_prop = td_prop} in print_relation_ok conf base info | _ -> incorrect_request conf ] ; value print_relation_dag conf base a p1 p2 l1 l2 = let ia = a.cle_index in let add_branches dist set n ip l = let b = find_first_branch conf base dist ia l ip Neuter in let rec loop set n b = if n > 100 then raise Exit else match b with [ Some b -> let set = List.fold_left (fun set (ip, _) -> Dag.Pset.add ip set) set b in loop set (n + 1) (find_next_branch conf base dist ia a.sex b) | None -> (set, n) ] in loop set n b in try let set = List.fold_left (fun set l1 -> List.fold_left (fun set l2 -> let dist = make_dist_tab conf base ia (max l1 l2 + 1) in let (set, n) = add_branches dist set 0 p1.cle_index l1 in let (set, n) = add_branches dist set n p2.cle_index l2 in set) set l2) (Dag.Pset.add ia Dag.Pset.empty) l1 in let spl = List.fold_right (fun (ip, s) spl -> match find_person_in_env conf base s with [ Some sp -> [(ip, (sp.cle_index, None)) :: spl] | None -> spl ]) [(p1.cle_index, "3"); (p2.cle_index, "4")] [] in let list = Dag.Pset.elements set in let d = Dag.make_dag conf base list in Dag.print_dag conf base set spl d with [ Exit -> Util.incorrect_request conf ] ; value int_list s = loop 0 0 where rec loop i n = if i = String.length s then [n] else match s.[i] with [ '0'..'9' as d -> loop (i + 1) (n * 10 + Char.code d - Char.code '0') | _ -> [n :: loop (i + 1) 0] ] ; value print_relation conf base p1 p2 = let l1 = p_getenv conf.env "l1" in let l2 = p_getenv conf.env "l2" in let po = find_person_in_env conf base "" in match (p_getenv conf.env "dag", po, l1, l2) with [ (Some "on", Some p, Some l1, Some l2) -> print_relation_dag conf base p p1 p2 (int_list l1) (int_list l2) | _ -> print_relation_no_dag conf base po p1.cle_index p2.cle_index ] ; value print conf base = match (find_person_in_env conf base "1", find_person_in_env conf base "2") with [ (Some p1, Some p2) -> print_relation conf base p1 p2 | _ -> incorrect_request conf ] ;