Plasma GitLab Archive
Projects Blog Knowledge

(*
 * <COPYRIGHT>
 * Copyright 2002 Joachim Schrod Network and Publication Consultance GmbH, Gerd Stolpmann
 *
 * <GPL>
 * This file is part of WDialog.
 *
 * WDialog is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * WDialog is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WDialog; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * </>
 *)

(* $Id: wd_var_functions.ml,v 1.7 2006-03-08 16:05:02 stolpmann Exp $
 * ----------------------------------------------------------------------
 *
 *)

open Wd_types

let bool_string b =
  if b then
    String_value "1"
  else
    String_value "0"
;;


let id dlg args =
  match args with
    | [a] -> a
    | _ ->
	failwith "function `id': expects exactly one argument"
;;


let size (dlg : dialog_type) args =
  match args with
      [a1] ->
	(match a1 with
	     String_value s -> 
	       let enc = (dlg#application#charset :> Pxp_types.encoding) in
	       let n = Netconversion.ustring_length enc s in
	       String_value (string_of_int n)
	   | Enum_value e ->
	       String_value (string_of_int (List.length e))
	   | Dyn_enum_value d ->
	       String_value (string_of_int (List.length d))
	   | Alist_value a ->
	       String_value (string_of_int (List.length a))
	   | Dialog_value _ ->
	       failwith "function `size': not defined for dialogs"
	)
    | _ ->
	failwith "function `size': expects exactly one argument"
;;


let length (dlg : dialog_type) args =
  match args with
      [a1] ->
	(match a1 with
	     String_value s -> 
	       let enc = (dlg#application#charset :> Pxp_types.encoding) in
	       let n = Netconversion.ustring_length enc s in
	       String_value (string_of_int n)
	   | _ ->
	       failwith "function `length': not defined for non-strings"
	)
    | _ ->
	failwith "function `length': expects exactly one argument"
;;


let split_re = Pcre.regexp "[ \t\r\n]+";;

let split s = Netstring_pcre.split split_re s;;


let card (dlg : dialog_type) args =
  match args with
      [a1] ->
	(match a1 with
	     String_value s -> 
	       let l = split s in
	       String_value (string_of_int (List.length l))
	   | Enum_value e ->
	       String_value (string_of_int (List.length e))
	   | Dyn_enum_value d ->
	       String_value (string_of_int (List.length d))
	   | Alist_value a ->
	       String_value (string_of_int (List.length a))
	   | Dialog_value _ ->
	       failwith "function `card': not defined for dialogs"
	)
    | _ ->
	failwith "function `card': expects exactly one argument"
;;


let list_string_op name op dlg args =
  match args with
      [a1; a2] ->
	let v1 =
	  match a1 with
	      String_value s -> 
		split s
	    | Enum_value e ->
		e
	    | Dyn_enum_value d ->
		List.map fst d
	    | Alist_value a ->
		List.map fst a
	    | Dialog_value _ ->
		failwith ("function `" ^ name ^ " ': not defined for dialogs")
	in
	let v2 =
	  match a2 with
	      String_value s ->
		s
	    | _ ->
		failwith ("function `" ^ name ^ "': bad type of operands")
	in
	op dlg v1 v2
    | _ ->
	failwith ("function `" ^ name ^ "': expects exactly two arguments")
;;


let contains =
  list_string_op "contains"
    (fun dlg l s -> bool_string (List.mem s l)) ;;


let mentions dlg args =
  match args with
      [a1; a2] ->
	let s =
	  match a2 with
	      String_value s ->
		s
	    | _ ->
		failwith ("function `mentions': bad type of operands")
	in
	( match a1 with
	    | Dyn_enum_value d ->
		bool_string(List.mem s (List.map snd d))
	    | Alist_value a ->
		let l =
		  List.flatten
		    (List.map
		       (function
			    (_,String_value s) -> [s]
			  | (_,Enum_value e) -> e
			  | (_,Dyn_enum_value d') -> List.map fst d'  (* ??? *)
			  | _ ->
			      failwith("function `mentions': First operand has bad type")
		       )
		       a
		    )
		in
		bool_string(List.mem s l)
	    | _ ->
		failwith ("function `mentions': bad type of operands")
	)
    | _ ->
	failwith ("function `mentions': expects exactly two arguments")
;;


let sequential_int_operation name op dlg args =
  let rec compute args =
    match args with
	[] ->
	  failwith ("function `" ^ name ^ "': expects at least one argument")
      | [ r ] ->
	  r
      | a1 :: a2 :: args' ->
	  ( match (a1,a2) with
		(String_value s1,String_value s2) ->
		  let n1 = int_of_string s1 in
		  let n2 = int_of_string s2 in
		  let r = String_value(string_of_int(op n1 n2)) in
		  compute (r :: args')
	      | _ ->
		  failwith ("function `" ^ name ^ "': operand has bad type")
	  )
  in
  compute args
;;


let add =
  sequential_int_operation "add" ( + ) ;;

let sub =
  sequential_int_operation "sub" ( - ) ;;

let mul =
  sequential_int_operation "mul" ( * ) ;;

let div =
  let divide n1 n2 =
    if n2 = 0 then failwith "Division by zero" else n1/n2 in
  sequential_int_operation "div" divide ;;

let modulo =
  let _mod n1 n2 =
    if n2 = 0 then failwith "Division by zero" else n1 mod n2 in
  sequential_int_operation "modulo" _mod ;;

let alist_operation name op dlg args =
  match args with
      [a1; a2] ->
	( match (a1,a2) with
	      (Alist_value alist, String_value s) ->
		op alist s
	    | _ ->
		failwith ("function `" ^ name ^ "': bad type of operands")
	)
    | _ ->
	failwith ("function `" ^ name ^ "': expects exactly two arguments")
;;

let assoc =
  alist_operation "assoc"
    (fun alist s ->
       try List.assoc s alist
       with Not_found ->
	 failwith "function `assoc': element not found")
;;

let nth =
  alist_operation "nth"
    (fun alist s ->
       try snd(List.nth alist (int_of_string s))
       with Failure _ ->
	 failwith "function `nth': element not found")
;;


let enum_operation name op dlg args =
  match args with
      [a1; a2] ->
	( match (a1,a2) with
	      (Dyn_enum_value de, String_value s) ->
		op de s
	    | (String_value t, String_value s) ->
		( try
		    let enum = dlg # declaration # enumeration t in
		    op enum.enum_definition s
		  with
		      Not_found ->
			failwith ("function `" ^ name ^ "': enumeration not found")
		)
	    | _ ->
		failwith ("function `" ^ name ^ "': bad type of operands")
	)
    | _ ->
	failwith ("function `" ^ name ^ "': expects exactly two arguments")
;;

let translate =
  enum_operation "translate"
    (fun dynenum s ->
       try String_value (List.assoc s dynenum)
       with Not_found ->
	 failwith "function `translate': element not found")
;;

let rev_translate =
  enum_operation "rev-translate"
    (fun dynenum s ->
       let dynenum' = List.map (fun (l,r) -> (r,l)) dynenum in
       try String_value (List.assoc s dynenum')
       with Not_found ->
	 failwith "function `rev-translate': element not found")
;;

let substring (dlg : dialog_type) args =
  let enc = (dlg#application#charset :> Pxp_types.encoding) in
  let compute s start length =
    let (k1,k2) = 
      if length >= 0 then (start,start+length) else (start+length,start) in
    let n1 = max 0 k1 in
    let n2 = min (Netconversion.ustring_length enc s) k2 in
    Netconversion.ustring_sub enc n1 (n2-n1) s
  in
  match args with
      [a1;a2] ->
	( match (a1,a2) with
	      (String_value s1,String_value s2) ->
		String_value(compute s1 (int_of_string s2) (String.length s1))
	    | _ ->
		failwith "function `substring': bad type of operand"
	)
    | [a1;a2;a3] ->
	( match (a1,a2,a3) with
	      (String_value s1,String_value s2,String_value s3) ->
		String_value(compute s1 (int_of_string s2) (int_of_string s3))
	    | _ ->
		failwith "function `substring': bad type of operand"
	)
    | _ ->
	failwith "function `substring': expects two or three arguments"
;;

let concat dlg args =
  let strings =
    List.map
      (function
	   String_value s -> s
	 | _ ->
	     failwith "function `concat': bad type of operand")
      args 
  in
  String_value(String.concat "" strings)
;;

let unary_string_operation name op dlg args =
  match args with
      [a1] ->
	( match a1 with
	      (String_value s) ->
		op dlg s
	    | _ ->
		failwith ("function `" ^ name ^ "': bad type of operands")
	)
    | _ ->
	failwith ("function `" ^ name ^ "': expects exactly one argument")
;;


let binary_string_operation name op dlg args =
  match args with
      [a1;a2] ->
	( match a1, a2 with
	      (String_value s1), (String_value s2) ->
		op dlg s1 s2
	    | _ ->
		failwith ("function `" ^ name ^ "': bad type of operands")
	)
    | _ ->
	failwith ("function `" ^ name ^ "': expects exactly two arguments")
;;


let rec count_height s n k =
  let next_cr_or_lf = Pxp_lib.crlf_index_from s k in
  if next_cr_or_lf >= 0 then begin
    match s.[next_cr_or_lf] with
        '\010' ->
          count_height s (n+1) (next_cr_or_lf+1)
      | '\013' ->
          let l = String.length s in
          if (next_cr_or_lf+1 < l && s.[next_cr_or_lf+1] = '\010') then
            count_height s (n+1) (next_cr_or_lf+2)
          else
            count_height s (n+1) (next_cr_or_lf+1)
      | _ ->
          assert false
  end
  else n
;;


let height =
  unary_string_operation "height"
    (fun dlg s ->
       String_value(string_of_int(count_height s 1 0)))
;;


let rec count_width enc s w k =
  let next_cr_or_lf = Pxp_lib.crlf_index_from s k in
  if next_cr_or_lf >= 0 then begin
    match s.[next_cr_or_lf] with
        '\010' ->
	  let n = Netconversion.ustring_length 
		    enc ~range_pos:k ~range_len:(next_cr_or_lf - k) s in
	  let w' = max w n in
          count_width enc s w' (next_cr_or_lf+1)
      | '\013' ->
	  let n = Netconversion.ustring_length
		    enc ~range_pos:k ~range_len:(next_cr_or_lf - k) s in
	  let w' = max w n in
          let l = String.length s in
          if (next_cr_or_lf+1 < l && s.[next_cr_or_lf+1] = '\010') then
            count_width enc s w' (next_cr_or_lf+2)
          else
            count_width enc s w' (next_cr_or_lf+1)
      | _ ->
          assert false
  end
  else w
;;


let width =
  unary_string_operation "width"
    (fun (dlg : dialog_type) s ->
       let enc = (dlg#application#charset :> Pxp_types.encoding) in
       String_value(string_of_int(count_width enc s 0 0)))
;;


let eq =
  binary_string_operation "eq"
    (fun dlg s1 s2 -> bool_string (s1 = s2)) ;;

let ne =
  binary_string_operation "eq"
    (fun dlg s1 s2 -> bool_string (s1 <> s2)) ;;

let compile_regexp s =
  try
    Pcre.regexp s
  with
      Pcre.Error(Pcre.BadPattern (msg, pos)) ->
	failwith ("Bad regular expression, at position " ^
		  string_of_int pos ^ ": " ^ msg)
;;

let match_ = 
  binary_string_operation "match"
    (fun dlg s1 s2 -> bool_string (Pcre.pmatch ~rex:(compile_regexp s2) s1)) ;;

let nomatch = 
  binary_string_operation "nomatch"
    (fun dlg s1 s2 -> bool_string (not (Pcre.pmatch ~rex:(compile_regexp s2) s1))) ;;

let int_operation name op dlg args =
  let int_args =
    List.map
      (fun a ->
	 match a with
	   | String_value s ->
	       ( try int_of_string s
		 with _ ->
		   failwith ("function `" ^ name ^ "': an arg is not an integer"))
	   | _ ->
	       failwith ("function `" ^ name ^ "': bad type of operands")
      ) args in
  op dlg int_args;;


let unary_int_operation name op dlg args =
  int_operation 
    name
    (fun dlg int_args ->
       match int_args with
	 | [i] ->
	     op dlg i
	 | _ ->
	     failwith ("function `" ^ name ^ "': expects exactly one argument"))
    dlg
    args ;;


let binary_int_operation name op dlg args =
  int_operation 
    name 
    (fun dlg int_args ->
       match int_args with
	 | [i1;i2] ->
	     op dlg i1 i2
	 | _ ->
	     failwith ("function `" ^ name ^ "': expects exactly two arguments"))
    dlg 
    args ;;


let int_eq =
  binary_int_operation "int-eq"
    (fun dlg i1 i2 -> bool_string (i1 = i2)) ;;

let int_ne =
  binary_int_operation "int-ne"
    (fun dlg i1 i2 -> bool_string (i1 <> i2)) ;;

let int_lt =
  binary_int_operation "int-lt"
    (fun dlg i1 i2 -> bool_string (i1 < i2)) ;;

let int_le =
  binary_int_operation "int-le"
    (fun dlg i1 i2 -> bool_string (i1 <= i2)) ;;

let int_gt =
  binary_int_operation "int-gt"
    (fun dlg i1 i2 -> bool_string (i1 > i2)) ;;

let int_ge =
  binary_int_operation "int-ge"
    (fun dlg i1 i2 -> bool_string (i1 >= i2)) ;;

let int_abs =
  unary_int_operation "int-abs"
    (fun dlg i -> String_value(string_of_int(abs i))) ;;

let int_sign =
  let sign n = if n = 0 then 0 else if n > 0 then 1 else (-1) in
  unary_int_operation "int-sign"
    (fun dlg i -> String_value(string_of_int(sign i))) ;;

let int_min =
  int_operation "int-min"
    (fun dlg args ->
       match args with
	 | [] -> failwith "function `int-min': needs at least one argument"
	 | arg :: args' ->
	     String_value(string_of_int(List.fold_left min arg args'))) ;;

let int_max =
  int_operation "int-max"
    (fun dlg args ->
       match args with
	 | [] -> failwith "function `int-max': needs at least one argument"
	 | arg :: args' ->
	     String_value(string_of_int(List.fold_left max arg args'))) ;;

let card_operation name op =
  list_string_op name
    (fun dlg l s ->
       let n =
	 try int_of_string s 
	 with _ -> failwith ("function `" ^ name ^ "': second arg must be an integer") in
       op dlg (List.length l)  n) ;;

let card_eq =
  card_operation "card-eq"
    (fun dlg i1 i2 -> bool_string (i1 = i2)) ;;

let card_ne =
  card_operation "card-ne"
    (fun dlg i1 i2 -> bool_string (i1 <> i2)) ;;

let card_lt =
  card_operation "card-lt"
    (fun dlg i1 i2 -> bool_string (i1 < i2)) ;;

let card_le =
  card_operation "card-le"
    (fun dlg i1 i2 -> bool_string (i1 <= i2)) ;;

let card_gt =
  card_operation "card-gt"
    (fun dlg i1 i2 -> bool_string (i1 > i2)) ;;

let card_ge =
  card_operation "card-ge"
    (fun dlg i1 i2 -> bool_string (i1 >= i2)) ;;


let var =
  unary_string_operation "var"
    (fun dlg s ->
       try
	 dlg # variable s
       with
	   No_such_variable msg ->
	     failwith ("function `var': no such variable: `" ^ msg ^ "'")
    )
;;


let dialog_exists dlg args =
  match args with
      [a1; a2] ->
	( match (a1,a2) with
	      (Dialog_value dlg_opt, String_value s) ->
		let b =
		  match (dlg_opt, s) with
		    | Some _, "yes" -> true
		    | Some _, "no"  -> false
		    | None,   "yes" -> false
		    | None,   "no"  -> true 
		    | _, _ -> failwith "function `dialog-exists': bad value of second argument" in
		bool_string b
	    | _ ->
		failwith ("function `dialog-exists': bad type of operands")
	)
    | [a1] ->
	( match a1 with
	      Dialog_value dlg_opt ->
		bool_string (dlg_opt <> None)
	    | _ ->
		failwith ("function `dialog-exists': bad type of operands")
	)
    | _ ->
	failwith ("function `dialog-exists': expects one or two arguments")
;;

let bool_operation name op dlg args =
  let bool_args =
    List.map
      (fun a ->
	 lazy (
	   match Lazy.force a with
	       String_value s ->
		 let i = 
		   try int_of_string s
		   with _ ->
		     failwith ("function `" ^ name ^ "': an arg is not an integer") in
		 i <> 0
	     | _ ->
		 failwith ("function `" ^ name ^ "': bad type of operands")
	 )
	)
      args in
  op dlg bool_args
;;

let and_ =
  bool_operation "and" 
    (fun dlg args -> bool_string (List.for_all (fun arg -> Lazy.force arg) args)) ;;


let or_ =
  bool_operation "or" 
    (fun dlg args -> bool_string (List.exists (fun arg -> Lazy.force arg) args)) ;;

let not_ =
  unary_string_operation "not"
    (fun dlg s ->
       let i =
	 try int_of_string s
	 with _ -> failwith ("function `not': argument is not an integer") in
       bool_string (i = 0)) ;;


let if_ dlg args =
  match args with
      [a1; a2; a3] ->
	let b =
	  match Lazy.force a1 with
	    | String_value s1 ->
		let i1 = 
		  try int_of_string s1
		  with _ ->
		    failwith ("function `if': first arg is not an integer") in
		i1 <> 0
	    | _ ->
		failwith ("function `if': bad type of first operand")
	in
	if b then (Lazy.force a2) else (Lazy.force a3)
    | _ ->
	failwith ("function `if': expects exactly three arguments")
;;
		

let const_operation name op (dlg:dialog_type) args =
  if args <> [] then
    failwith ("function `" ^ name ^ "': no arguments allowed");
  op dlg
;;

let true_ =
  const_operation "true" (fun dlg -> String_value "1") ;;

let false_ =
  const_operation "true" (fun dlg -> String_value "0") ;;


let dialog =
  const_operation "dialog"
    (fun dlg -> String_value(dlg # name))
;;

let self =
  const_operation "self"
    (fun dlg -> Dialog_value (Some dlg))
;;

let page =
  const_operation "page"
    (fun dlg -> String_value(dlg # page_name))
;;

let language =
  const_operation "language"
    (fun dlg ->
       match dlg # declaration # language_variable with
	   Some v ->
	     ( try
		 dlg # variable v
	       with
		   No_such_variable msg ->
		     failwith ("function `language': no such variable: `" ^ msg ^ "'")
	     )
	 | None ->
	     String_value ""
    )

let cat_translate (dlg : dialog_type) args =
  match args with
    | [ String_value s ]
    | [ String_value s; _ ] ->    (* Optional second arg is ignored *)
	let language =
	  match dlg # declaration # language_variable with
	    | Some v ->
		( try
		    dlg # string_variable v
		  with
		      No_such_variable msg ->
			failwith ("function `cat-translate': no such variable: `" ^ msg ^ "'")
		)
	    | None -> "" in
	let s' =
	  try
	    dlg # application # lookup_message 
	      ~domain:dlg#name
	      ~language
	      s
	  with
	   | Not_found_in_catalog(_,_,_) ->
	       (* TODO: Proper logging *)
	       prerr_endline("Message not found: language=" ^ language ^ 
			       " internal=" ^ s);
	       s in
	String_value s'
    | _ ->
	failwith ("function `cat-translate': bad arguments");
;;

       

let self_base_url = 
  const_operation "self-base-url"
    (fun dlg -> 
       let cgi = (dlg # environment).cgi in
       let script_path = cgi # environment # cgi_script_name in
       let script_name = Filename.basename script_path in
        String_value(script_name)
    )
;;

let session_id = 
  const_operation "session-id"
    (fun dlg -> 
        String_value(dlg # session # session_id)
    )
;;

let create_anchor_event =
  unary_string_operation "create-anchor-event"
    (fun dlg s ->
       let ia = dlg # interactors in 
       let id = Wd_interactor.add ia.ui_anchors s "" None None in
       String_value id
    )
;;

let create_xanchor_event =
  binary_string_operation "create-xanchor-event"
    (fun dlg s1 s2 ->
       let ia = dlg # interactors in 
       let id = Wd_interactor.add ia.ui_indexed_anchors s1 s2 None None in
       String_value id
    )
;;


(* ======================================================================
 * History:
 * 
 * $Log: wd_var_functions.ml,v $
 * Revision 1.7  2006-03-08 16:05:02  stolpmann
 * Limited support for catalogs:
 *
 * The syntax <ui:catalog>...</ui:catalog> is supported, and it is
 * possible to define catalogs in ui files.
 *
 * The syntax $[m(token)] looks up the token in the current catalog.
 *
 * The function cat-translate is also available.
 *
 * ui:select has the "display" attribute.
 *
 * Revision 1.6  2005/06/11 14:24:14  stolpmann
 * Extension of bracket expressions: many new functions.
 * Functions in bracket expressions may now lazily evaluate their arguments.
 * ui:if and ui:ifvar may refer to any functions defined for bracket
 * expressions.
 * New: ui:ifexpr
 * Parsing of bracket expressions is now done with ulex. Private interfaces
 * of PXP are no longer used for this purpose.
 * Serialization has been rewritten, and it is now safe.
 * Fix: In ui:context there may now be macro references at any location.
 * Also documented all these changes.
 *
 * Revision 1.5  2004/12/12 17:57:32  stolpmann
 * 	Added <q:wd-link> and <q:wd-xlink> to generate links for
 * applications that cannot use Javascript. Limited functionality, however.
 * See stdlib.xml for details.
 *
 * Revision 1.4  2003/06/21 12:09:08  stolpmann
 * 	Updates because of changes in Ocamlnet 0.96
 *
 * Revision 1.3  2003/02/16 01:07:03  stolpmann
 * 	size, substring, width: string positions are measured as
 * the number of characters, not as the number of bytes
 *
 * Revision 1.2  2002/11/03 20:56:56  stolpmann
 * 	New functions: translate, rev_translate
 *
 * Revision 1.1  2002/10/20 19:38:17  stolpmann
 * 	Initial revision
 *
 * 
 *)

This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml