(* * <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_types.ml,v 3.9 2005-06-11 14:24:14 stolpmann Exp $ * ---------------------------------------------------------------------- * *) type (+'a) dict = 'a Wd_dictionary.t type event = Button of string | Image_button of (string * int * int) | Indexed_button of (string * string) | Indexed_image_button of (string * string * int * int) | No_event | Popup_request of string (* event: * Button n: The button with name n has been pressed * (<ui:button name="n" ...>) * Image_button (n,x,y): The image button with name n has been pressed * at the coordinates (x,y). (<ui:imagebutton name="n" ...>) * Indexed_button (n,i): The button with name n and index i has been pressed * (<ui:button name="n" index="i" ...>) * Indexed_image_button (n,i,x,y): The image button with name n and index i * has been pressed at the coordinates (x,y). * (<ui:imagebutton name="n" index="i" ...) * No_event: This value indicates that there was no event (or no * recognized event) * Popup_request s: A server popup window has just popped up, and the contents * for the window have been requested. The argument is the second argument * of the "open" function. *) type interactors = { mutable ui_buttons : string option Wd_interactor.t; mutable ui_imagebuttons : string option Wd_interactor.t; mutable ui_anchors : string option Wd_interactor.t; mutable ui_indexed_buttons : string option Wd_interactor.t; mutable ui_indexed_imagebuttons :string option Wd_interactor.t; mutable ui_indexed_anchors : string option Wd_interactor.t; mutable ui_vars : unit Wd_interactor.t; mutable ui_enumvars : (string * string option * string) list; mutable ui_uploads : unit Wd_interactor.t; } (* Values of this type store the mapping from the (name,index) pairs * of interactor elements to the real CGI parameter names together * with an auxiliary component. * * What's the problem? Interactor elements like ui:a allow the programmer * to identify these either by a single name (which is an arbitrary * string) or by a pair of a "name" and an "index" (i.e. two arbitrary * strings). In contrast to this, CGI parameter names are much more * restricted. First, these names are only strings; there is no * built-in representation for pairs of strings. Second, you cannot * use arbitrary characters within these names because of limitations * of the transport protocol and because of bugs in browsers. (As * the "multipart/form-data" representation used in the transport * protocol bases on the RFC 822 mail format, it is neither possible to * pass 8 bit values, nor to pass control characters. A known bug * in Netscape browsers is that the double-quote and backslash characters * are incorrectly represented.) * * The solution is to generate the CGI parameter names using only * unproblematic characters, and to keep the mapping of the original * string or string * string to the generated CGI name. This record * stores the mappings for the various namespaces. The mapping is * encapsulated in the Wd_interactor module, and the type * 'a Wd_interactor.t (where 'a is arbitrary) represents such a mapping. The * CGI names are simply enumerated, and the Wd_interactor.t value stores only * the numbers (IDs) of the CGI parameters. The complete CGI name is formed * using a prefix and the number stored in Wd_interactor.t. For example, * ui:buttons without index have the prefix "button_"; and if the * ui_button record component contains entries for the IDs 0, 1, and 2, * the complete CGI names are "button_0", "button_1", and "button_2", * respectively. * * The type parameter 'a of 'a Wd_interactor.t is the type of the * auxiliary component stored with every entry. See below for the * meanings in every case. * * The Wd_interactor.t structure always maps from pairs string * string to * numerical IDs. Because of this, there are usually two mappings, one * for the (name,index) PAIRS, and one for the simple names. The record * components listed below that have a name with "indexed" are responsible * for the pairs, the corresponding component without "indexed" stores * the mapping for the simple case. * * The simple case is represented as Wd_interactor.t by using always the * empty string "" as index. * * THE COMPONENTS ARE: * * (A) BUTTON-TYPE INTERACTORS: * * ui_buttons: Enumerates the ui:button interactors * that have only a name, not an index. * CGI prefix: "button_". * ui_indexed_buttons: Enumerates the ui:button interactors * that have both a name and an index. * CGI prefix: "xbutton_". * ui_imagebuttons: Enumerates the ui:imagebutton interactors * that have only a name, not an index. * CGI prefix: "imagebutton_". * ui_indexed_imagebuttons: Enumerates the ui:imagebutton interactors * that have both a name and an index. * CGI prefix: "ximagebutton_". * ui_anchors: Enumerates the ui:a interactors * that have only a name, not an index. * CGI prefix: "anchor_". * ui_indexed_anchors: Enumerates the ui:a interactors * that have both a name and an index. * CGI prefix: "xanchor_". * * These components use the auxiliary component to store the "goto" * attribute of the interactor, if present. * * (B) BOX-LIKE INTERACTORS: * * ui_vars: Enumerates the interactors that are bound * to an object variable (i.e. they have a * "variable" attribute, like ui:text). * As variables allow either only non-indexed * names or only indexed names, this component * contains both interactors identified by a simple * string name and interactors identified by * (name,index) pairs; it is not possible that * there are conflicts between the two naming * methods. * CGI prefix: "var_". * This component does not have an auxiliary * component. * * ui_enumvars: This list enumerates the simple names * (name, None, pg) or pairs (name, Some index, pg) * that occur in checkbox, radiobutton and select * list interactors. pg is the name of the page * where the interactor occurs (knowing the page * is necessary for popup dialogues). * * ui_uploads: Enumerates the ui:file interactors. These * may only have a simple name. * CGI prefix: "upload_". * This component does not have an auxiliary * component. *) type 'dlg poly_var_value = String_value of string | Enum_value of string list | Dialog_value of 'dlg option | Dyn_enum_value of (string * string) list | Alist_value of (string * 'dlg poly_var_value) list (* (poly_)var_value is the type of instance variables of uiobjects. There are * six possibilities for var_values: * - String_value s: The instance variable contains the string s * - Enum_value [x1;x2;...]: The instance variable contains an enumerator * value with the internal items x1, x2, etc. (As variables are declared * it is known which items are possible, and whether there are corresponding * external values.) * - Dialog_value None: The instance variable does not contain a dialog. * - Dialog_value (Some obj): The variable contains the dialog obj. * - Dyn_enum_value [(x1,y1);(x2,y2);...]: The variable contains the enumerator * with internal items x1, x2,... and the corresponding external values * y1, y2, ... * - Alist_value [(i1,v1); (i2,v2); ...]: The variable contains the associative * list where the index i1 is mapped to the value v1, i2 is mapped to v2 etc. * * See also [var_value] below. *) type enum_decl = { enum_name : string; mutable (* XXX *) enum_definition : (string * string) list; } type var_type_name = String_type | Enum_type of enum_decl | Dialog_type | Dyn_enum_type type 'dlg poly_var_decl = { var_name : string; var_type : var_type_name; var_default : 'dlg poly_var_value option; var_temporary : bool; var_associative : bool; var_protected : bool; } type response_header = { mutable rh_status : Nethttp.http_status; mutable rh_content_type : string; mutable rh_cache : Netcgi_common.cache_control; mutable rh_filename : string option; mutable rh_language : string option; mutable rh_script_type : string option; mutable rh_style_type : string option; mutable rh_set_cookie : Nethttp.cookie list; mutable rh_fields : (string * string list) list; } type debug_mode_style = [ `Fully_encoded | `Partially_encoded ] type environment = { (* Overall way of operation: *) debug_mode : bool; debug_mode_style : debug_mode_style; prototype_mode : bool; server_popup_mode : bool; (* Web variables *) self_url : string; response_header : response_header; cgi : Netcgi.cgi_activation; } type trans_vars = { mutable within_popup : bool; mutable current_page : string; mutable popup_env_initialized : bool; mutable condition_code : bool; (* last result of <ui:if>, <ui:ifvar>, <ui:iflang> *) mutable serialize_session : unit -> string; } (* Some local variables that are used in the recursive transformation. * By putting them all into a record it is simpler to pass them to the * subsequent transformers. *) type ('universe,'dialog,'environment) poly_ds_buf = ('universe,'dialog,'environment) Wd_serialize_types.poly_ds_buf = { ds_str : string; (* String to deserialize *) mutable ds_pos : int; (* Current position *) ds_end : int; (* End position *) ds_universe : 'universe; ds_environment : 'environment; ds_dialogs : (int, 'dialog) Hashtbl.t; (* maps dialog IDs to dialogs *) } class type dialog_decl_type = object ('self) (* PUBLIC *) method name : string method enumeration : string -> enum_decl method variable_names : string list method variable : string -> dialog_type poly_var_decl method page_names : string list method page : string -> syntax_tree_type method page_is_declared_as_popup : string -> bool method start_page : string method default_context : syntax_tree_type dict method language_variable : string option (* RESTRICTED *) method set_name : string -> unit method set_start_page : string -> unit method add_enumeration : enum_decl -> unit method add_variable : dialog_type poly_var_decl -> unit method add_page : string -> syntax_tree_type -> unit method set_default_context : syntax_tree_type dict -> unit method set_language_variable : string -> unit end and virtual dialog_type = object ('self) method copy : dialog_type (* return a copy of the dialog *) method name : string (* return the name of the dialog *) method page_name : string (* return the name of the current page *) method page_names : string list (* returns the names of all defined pages of this dialog *) method variable : string -> dialog_type poly_var_value (* variable n: Get the variable with name n, or raise No_such_variable *) method variable_decl : string -> dialog_type poly_var_decl method string_variable : string -> string (* string_variable n: Get the contents of the string variable n. * Raise No_such_variable if the variable does not exist. Runtime_error * if the variable is not a string. *) method enum_variable : string -> string list (* enum_variable n: Get the contents of the enumerator variable n. * The enumerator must have been declared. The returned list contains * only the external values. * Raise No_such_variable if the variable does not exist. Runtime_error * if the variable is not a declared enumerator. *) method dyn_enum_variable : string -> (string * string) list (* dyn_enum_variable n: Get the contents of the enumerator variable n. * The enumerator may be dynamic or may be declared. The returned * list contains both the internal and the external values. * Raise No_such_variable if the variable does not exist. Runtime_error * if the variable is not an enumerator. *) method dialog_variable : string -> dialog_type option (* dialog_variable n: Get the contents of the dialog variable n. * Raise No_such_variable if the variable does not exist. * Runtime_error if the variable is not a dialog. *) method alist_variable : string -> (string * dialog_type poly_var_value) list (* alist_variable n: Get the contents of the associative variable n. * Raise No_such_variable if the variable does not exist. * Runtime_error if the variable is not an alist. *) method lookup_string_variable : string -> string -> string method lookup_enum_variable : string -> string -> string list method lookup_dyn_enum_variable : string -> string -> (string * string) list method lookup_dialog_variable : string -> string -> dialog_type option (* lookup_*_variable n x: Get the contents of the associative * variable n at index x. * No_such_variable if the variable does not exist. * Not_found if the variable is undefined at x. * Runtime_error if the variable has the wrong type. *) method set_variable : string -> dialog_type poly_var_value -> unit (* set_variable n v: Sets the variable n to the value v. The value v * must be compatible to the declared type of the variable. * No_such_variable if the variable does not exist. * Runtime_error if the variable is not compatible. *) method unset_variable : string -> unit (* unset_variable n: Sets the variable n to the declared default * value. * If the default is not declared, the following default values apply: * For strings: the default is the empty string. * For enumerators: the default is the empty list. * For dialogs: the default is that the value does not exist. *) method lookup_uploaded_file : string -> Netcgi.cgi_argument option (* lookup_uploaded_file name: * Checks whether the file upload box 'name' was used. If so, * 'Some arg', where 'arg' is the transporting CGI argument is returned. * If the box was not used, but the box exists, None is returned. * Raises a Runtime_error if the named box does not exist. * * IMPORTANT NOTE: Uploaded files are not persistent. This means that * they are only existent in the 'handle' phase, not during initialization * nor the 'prepare_page' phase. You get a failure * "Upload.get: not initialized" if you try to access uploaded files * in the wrong moment. *) method dump : Format.formatter -> unit (* dump f: output a textual description of the current state into * formatter f. *) method next_page : string (* Returns the name of the designated next page. *) method set_next_page : string -> unit (* Sets the name of the designated next page *) method event : event (* returns the event that just has happened *) method is_server_popup_request : bool (* Returns whether someone invoked set_server_popup_request before. * This is usually done if an dialog is restored for a server-driven * popup window (tag ui:server-popup). *) method serialize : (dialog_type,unit) Hashtbl.t -> Buffer.t -> unit (* Returns the state of this dialog as string *) method unserialize : (universe_type,dialog_type,environment) poly_ds_buf -> unit (* Sets the state of this dialog from the string which must be a * previously serialized dialog. *) method enter_session_scope : session_type -> unit method leave_session_scope : unit -> unit method session : session_type method environment : environment method declaration : dialog_decl_type method application : application_type method universe : universe_type (*********************************************************************) (* The following methods needs to be defined in subclasses: *) method virtual prepare_page : unit -> unit (* This method is invoked just before a new output page is generated. * * PRECONDITIONS: * The method 'page_name' returns the name of this page. * The method 'event' still returns the action last happened, but * the name of the page where this action happened is lost. It may * be interesting whether the last event was 'No_event' because this * indicates that the page is initially entered. * * This method should set any variables which are necessary to * generate the new page (mostly variable containing HTML fragments). * Furthermore, any state that needs to be saved should be put into * variables, too. *) method virtual handle : unit -> unit (* This method is invoked just after the user triggered an event (e.g. * pressed a button). * * PRECONDITIONS: * The method 'page_name' returns the name of the page that was visible * while the event was triggered. * The method 'event' returns the description of the event. * * POSTCONDITIONS: * The method may set the next page to display by invoking * 'set_next_page'; if it does not then the default will be used. * The default is either specified by the XML element describing * the interactor that triggered the event, or the default is * otherwise this page again. * * This method should modify the variables according to the event * that happened, and optionally set the next page to display. * * There is also an alternate way to go to another page: raising * the exception Change_page. * * By raising the exception Change_dialog this method may force to go to * another dialog. *) (*********************************************************************) (* Internal methods *) method set_event : event -> unit (* set_event e: Sets the current event to e. *) method set_server_popup_request : unit -> unit (* Sets that this dialog is restored for a server popup window *) method init : string -> unit (* MOSTLY INTERNAL USE: * init p: re-initializes the dialog in order to go to page p. *) method interactors : interactors (* ONLY INTERNAL USE *) (* -- class [dialog] also has a private method: * method private put_tree : string -> Template.tree -> unit *) end and application_type = object ('self) (* PUBLIC *) method start_dialog_name : string method dialog_names : string list method dialog_declaration : string -> dialog_decl_type method template_names : string list method template : string -> template_type method study : unit -> unit method output_encoding : string -> (string -> string) method var_function : string -> ( dialog_type -> dialog_type poly_var_value list -> dialog_type poly_var_value) method lazy_var_function : string -> ( dialog_type -> dialog_type poly_var_value Lazy.t list -> dialog_type poly_var_value) method dtd : Pxp_dtd.dtd method charset : Pxp_types.rep_encoding method debug_mode : bool method debug_mode_style : debug_mode_style method prototype_mode : bool method onstartup_call_handle : bool method add_var_function : string -> ( dialog_type -> dialog_type poly_var_value list -> dialog_type poly_var_value) -> unit method add_lazy_var_function : string -> ( dialog_type -> dialog_type poly_var_value Lazy.t list -> dialog_type poly_var_value) -> unit (* RESTRICTED *) method set_start_dialog_name : string -> unit method add_dialog_declaration : dialog_decl_type -> unit method add_template : ?lib:bool -> string -> template_type -> unit method add_output_encoding : string -> (string -> string) -> unit method set_debug_mode : bool -> debug_mode_style -> unit method set_prototype_mode : bool -> unit method set_onstartup_call_handle : bool -> unit end and template_type = object method study : application_type -> unit method instantiate : ?context: syntax_tree_type dict -> ?vars: trans_vars -> ?params: syntax_tree_type dict -> dialog_type -> syntax_tree_type (* Note: Raises Instantiation_error if something goes wrong *) end and syntax_tree_type = object ('self) (* PXP *) inherit [syntax_tree_type Pxp_document.node] Pxp_document.extension (* TEMPLATES *) inherit template_type (* SCANNERS *) (* The side-effect of the scanners is to put the result into the passed * argument. *) method scan_application : application_type -> unit method scan_dialog : application_type -> dialog_decl_type -> unit method scan_enumeration : enum_decl -> unit (* This one is functional: *) method scan_literal : unit -> dialog_type poly_var_value (* ACCESS *) method escaped_data : string (* OUTPUT *) method to_html : ?context: syntax_tree_type dict -> ?vars: trans_vars -> dialog_type -> Netchannels.out_obj_channel -> unit (* TODO: to_xhtml *) method to_text : ?context: syntax_tree_type dict -> ?vars: trans_vars -> dialog_type -> Netchannels.out_obj_channel -> unit end and universe_type = object ('self) method application : application_type method register : string -> (universe_type -> string -> environment -> dialog_type) -> unit method create : environment -> string -> dialog_type end and session_manager_type = object method create : dialog_type -> session_type method unserialize : universe_type -> environment -> string -> session_type end and session_type = object method session_id : string method dialog_name : string method dialog : dialog_type (* Returns the dialog *) method commit_changes : unit -> unit (* Causes that [serialize] returns the current state of the dialog *) method serialize : string (* Returns the state of the dialog at the time of the last [commit_change] * or the state of the initial dialog *) method change_dialog : dialog_type -> unit (* Continue with a new dialog. The methods [dialog_name] and [dialog] will * immediately reflect the change. However, you have to call [commit_changes] * to make [serialize] return the state of the new dialog. *) end type var_value = dialog_type poly_var_value type var_decl = dialog_type poly_var_decl type ds_buf = (universe_type,dialog_type,environment) poly_ds_buf exception Change_dialog of dialog_type (* The implementation of the 'handle' method of a dialog may raise * Change_dialog to drop the current dialog and continue with another * dialog. *) exception Change_page of string (* The implementation of the 'handle' method of a dialog may raise * Change_page to set the next page to display for the current dialog. *) exception Formal_user_error of string exception Runtime_error of string exception No_such_variable of string exception Instantiation_error of string (* An error generated by the [instantiate] method. The caller should * catch this exception and report the error from its own view *) let revision_types = "$Revision: 3.9 $" ;; (* Intentionally the CVS revision string! *) (* ====================================================================== * History: * * $Log: wd_types.ml,v $ * Revision 3.9 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 3.8 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 3.7 2003/02/16 23:48:15 stolpmann * Improved wd-debug-mode: there are now two styles * * Revision 3.6 2003/01/04 21:55:25 stolpmann * new record response_header * * Revision 3.5 2002/11/09 11:41:19 stolpmann * Fix: ui:select accepts dot notation. A new method * variable_decl needs to be defined for dialogs. This method * returns the var_decl record and interprets the dot notation. * * Revision 3.4 2002/10/20 19:39:17 stolpmann * New feature: The brackets $[...] can contain expressions, * not only variables * * Revision 3.3 2002/04/10 20:04:05 stolpmann * The CVS revision number is exported. * * Revision 3.2 2002/02/14 16:15:21 stolpmann * Added copyright notice. * * Revision 3.1 2002/02/12 20:29:21 stolpmann * Initial release at sourceforge. * * Revision 2.9 2002/02/11 11:14:21 gerd * Fix: It is now possible to change the dialog of sessions (required * for the Change_dialog exception) * * Revision 2.8 2002/02/07 18:49:59 gerd * Standard library * * Revision 2.7 2002/02/05 18:43:04 gerd * New: var_protected * New: page_is_declared_as_popup * New: debug_mode, prototype_mode, onstartup_call_handle * New: Session management * * Revision 2.6 2002/01/31 23:06:40 gerd * Revised conditional expansion (ui:if, ui:ifvar, ui:iflang, * ui:cond). * Added some support for internationalization (xml:lang). * * Revision 2.5 2002/01/30 15:16:10 gerd * New: charset * New argument application_type for [study] * New argument application_type for [scan_dialog] * * Revision 2.4 2002/01/28 02:12:54 gerd * Added support for output encodings. * * Revision 2.3 2002/01/26 22:38:27 gerd * Changed the type for passing parameters to templates. It is * now syntax_tree dict instead of a simple list. Furthermore, the * method [instantiate] no longer returns the new context. The new element * ui:context creates the new contexts now. * * Revision 2.2 2002/01/24 23:35:18 gerd * New exception: [Instantiation_error] * * Revision 2.1 2002/01/21 14:29:04 gerd * Initial revision. * * *)