Sophie

Sophie

distrib > Mageia > 4 > x86_64 > by-pkgid > ce6d9aeda3f1b30d4e1e4c3682042d6c > files > 12

ocaml-data-notation-devel-0.0.10-5.mga4.x86_64.rpm

(********************************************************************************)
(*  ODN: Dump data using OCaml notation                                         *)
(*                                                                              *)
(*  Copyright (C) 2009-2011, OCamlCore SARL                                     *)
(*                                                                              *)
(*  This library is free software; you can redistribute it and/or modify it     *)
(*  under the terms of the GNU Lesser General Public License as published by    *)
(*  the Free Software Foundation; either version 2.1 of the License, or (at     *)
(*  your option) any later version, with the OCaml static compilation           *)
(*  exception.                                                                  *)
(*                                                                              *)
(*  This library 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 file COPYING for more          *)
(*  details.                                                                    *)
(*                                                                              *)
(*  You should have received a copy of the GNU Lesser General Public License    *)
(*  along with this library; if not, write to the Free Software Foundation,     *)
(*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA               *)
(********************************************************************************)

(** Property list 
    @author Sylvain Le Gall
  *)

TYPE_CONV_PATH "PropList";;

(*open CommonGettext;;*)
let s_ i = i
let f_ i = i^^""

type name_t = string
;;

let no_context f =
  fun ?context s -> f s
;;

exception Not_set of name_t;;
exception No_printer of name_t;;
exception Unknown_field of name_t * name_t;;

let string_of_exception =
  function
    | Not_set nm ->
        Printf.sprintf (f_ "Value %s is not set") nm
    | No_printer nm ->
        Printf.sprintf (f_ "No default printer for value %s") nm
    | Unknown_field (nm, schm) ->
        Printf.sprintf (f_ "Field %s is not defined in schema %s") nm schm
    | e ->
        raise e
;;

module Data =
struct

  type t = 
      (name_t, unit -> unit) Hashtbl.t

  let create () =
    Hashtbl.create 13

  let odn_of_t _ =
    ODN.STR ""

end
;;

module Schema = 
struct

  type ('ctxt, 'extra) value_t =
      {
        get:   Data.t -> string;
        set:   Data.t -> ?context:'ctxt  -> string -> unit;
        help:  (unit -> string) option;
        extra: 'extra;
      }

  type ('ctxt, 'extra) t =
      {
        name:      name_t;
        fields:    (name_t, ('ctxt, 'extra) value_t) Hashtbl.t;
        presets:   (name_t, 'ctxt option * string) Hashtbl.t;
        order:     name_t Queue.t;
        name_norm: string -> string;
      }

  let create ?(case_insensitive=false) nm = 
    {
      name      = nm;
      fields    = Hashtbl.create 13;
      presets   = Hashtbl.create 13;
      order     = Queue.create ();
      name_norm = 
        (if case_insensitive then 
           String.lowercase
         else
           fun s -> s);
    }

  let add t nm set get extra help = 
    let key = 
      t.name_norm nm
    in

    (* If available, set preset values *)
    let update_preset data =
      if Hashtbl.mem t.presets nm then
        begin
          let context, v = 
            Hashtbl.find t.presets nm
          in
            Hashtbl.remove t.presets nm;
            set data ?context v
        end
    in

    (* Set preset value before any other *)
    let set data ?context x =
      update_preset data;
      set data ?context x
    in

    (* Before get, set preset value *)
    let get data =
      update_preset data;
      get data
    in

      if Hashtbl.mem t.fields key then
        failwith 
          (Printf.sprintf 
             (f_ "Field '%s' is already defined in schema '%s'")
             nm t.name);
      Hashtbl.add 
        t.fields 
        key 
        {
          set   = set; 
          get   = get; 
          help  = help;
          extra = extra;
        };
      Queue.add nm t.order 

  let mem t nm =
    Hashtbl.mem t.fields nm 

  let find t nm = 
    try
      Hashtbl.find t.fields (t.name_norm nm)
    with Not_found ->
      raise (Unknown_field (nm, t.name))

  let get t data nm =
    (find t nm).get 
      data

  let set t data nm ?context x =
    (find t nm).set 
      data 
      ?context 
      x

  let preset t data nm ?context x = 
    Hashtbl.add t.presets nm (context, x)

  let fold f acc t =
    Queue.fold 
      (fun acc k ->
         let v =
           find t k
         in
           f acc k v.extra v.help)
      acc 
      t.order

  let iter f t =
    fold 
      (fun () -> f)
      ()
      t

end
;;

module Field =
struct

  type ('ctxt, 'value, 'extra) t =
      {
        set:    Data.t -> 'value -> unit;
        get:    Data.t -> 'value;
        sets:   Data.t -> ?context:'ctxt -> string -> unit;
        gets:   Data.t -> string;
        help:   (unit -> string) option;
        extra:  'extra;
      }

  let new_id = 
    let last_id =
      ref 0
    in
      fun () -> incr last_id; !last_id

  let create ?schema ?name ?parse ?print ?default ?update ?help extra =
    (* Default value container *)
    let v = 
      ref None 
    in

    (* If name is not given, create unique one *)
    let nm = 
      match name with 
        | Some s -> s
        | None -> Printf.sprintf "_anon_%d" (new_id ())
    in

    (* Last chance to get a value: the default *)
    let default () = 
      match default with 
        | Some d -> d
        | None -> raise (Not_set nm) 
    in

    (* Get data *)
    let get data =
      (* Get value *)
      try 
        (Hashtbl.find data nm) ();
        match !v with 
          | Some x -> x 
          | None -> default ()
      with Not_found ->
        default ()
    in

    (* Set data *)
    let set data x = 
      let x = 
        match update with 
          | Some f ->
              begin
                try 
                  f (get data) x
                with Not_set _ ->
                  x
              end
          | None ->
              x
      in
        Hashtbl.replace 
          data 
          nm 
          (fun () -> v := Some x) 
    in

    (* Parse string value, if possible *)
    let parse =
      match parse with 
        | Some f -> 
            f
        | None ->
            fun ?context s ->
              failwith 
                (Printf.sprintf 
                   (f_ "Cannot parse field '%s' when setting value %S")
                   nm
                   s)
    in

    (* Set data, from string *)
    let sets data ?context s =
      set data (parse ?context s)
    in

    (* Output value as string, if possible *)
    let print =
      match print with
        | Some f ->
            f
        | None ->
            fun _ -> raise (No_printer nm)
    in

    (* Get data, as a string *)
    let gets data =
      print (get data)
    in

      begin 
        match schema with 
          | Some t ->
              Schema.add t nm sets gets extra help
          | None ->
              ()
      end;

      {
        set   = set;
        get   = get;
        sets  = sets;
        gets  = gets;
        help  = help;
        extra = extra;
      }

  let fset data t x = 
    t.set data x

  let fget data t =
    t.get data

  let fsets data t ?context s =
    t.sets data ?context s

  let fgets data t =
    t.gets data 

end
;;

module FieldRO =
struct

  let create ?schema ?name ?parse ?print ?default ?update ?help extra =
    let fld = 
      Field.create ?schema ?name ?parse ?print ?default ?update ?help extra
    in
      fun data -> Field.fget data fld

end;;