aboutsummaryrefslogtreecommitdiff
path: root/src/odf/odfLoader.ml
blob: 4abe49ff1184c43f3f5044f2ff3a4bab4a3580fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
(*
This file is part of licht.

licht 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 3 of the License, or
(at your option) any later version.

licht 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 licht.  If not, see <http://www.gnu.org/licenses/>.
*)

module NS = Odf_ns

type tree =
  | Data of string
  | Cell of {repetition: int; cell_width: int; expression: Expression.t}
  | Unit

let memoization cache key f = begin
  try
    Hashtbl.find cache key
  with Not_found ->
    let value = f key in
    Hashtbl.add cache key value;
    value
end

let load_content cache content = begin function
  | "float" -> Expression.Basic (
      ScTypes.Type.number (
        DataType.Num.of_float (float_of_string content)
      ))
  | "date"  -> Expression.Basic (
      ScTypes.Type.date (
        DataType.Num.of_float (float_of_string content)
      ))
  | _ ->
    (* If the same text is present many times, use the same string instead of creating a new one *)
    memoization cache content (fun content ->
      Expression.Basic (
            ScTypes.Type.string (
              UTF8.from_utf8string content)))
end

let load_formula formula =
  let lineBuffer = Lexing.from_string formula in
  try
    Expression.Formula (
      Expression.Expression (
        Odf_ExpressionParser.value Odf_ExpressionLexer.read lineBuffer))
  with e ->
    print_endline formula;
    raise e


let build_cell cache (attributes:Xmlm.attribute list) (childs:tree list) = begin

  (* Check if the content is repeated *)
  let repetition =
    try int_of_string @@ List.assoc NS.number_columns_repeat_attr attributes
    with Not_found -> 1

  (* cell width *)
  and cell_width =
    try int_of_string @@ List.assoc NS.number_columns_spanned_attr attributes
    with Not_found -> 1

  and expression =
    try
      load_formula @@ List.assoc NS.formula_attr attributes
    with Not_found -> (
      let vtype =
        try List.assoc NS.ovalue_type_attr attributes
        with Not_found -> "" in

      try
        load_content cache (List.assoc NS.value_attr attributes) vtype
      with Not_found -> (
        (* This is not a formula, neither a value ? *)
        try
          let value = Tools.List.find_map (function | Data x -> Some x | _ -> None) childs in
          load_content cache value vtype
        with Not_found -> Expression.Undefined
      )
    ) in

  Cell {repetition; cell_width; expression}

end

let build_p (attributes:Xmlm.attribute list) = begin function
  | Data x::_ -> Data x
  | _ -> Data ""
end


let build_row (sheet:Sheet.t) (row_num:int ref) (attributes:Xmlm.attribute list) (childs:tree list) = begin

  let repetition =
    try int_of_string @@ List.assoc (NS.table, "number-rows-repeated") attributes
    with Not_found -> 1 in

  for i = 1 to repetition do
    let cell_num = ref 1 in
    List.iter (function
    | Cell cell ->
      for i = 1 to cell.repetition do
        ignore @@ Sheet.add ~history:false cell.expression (!cell_num, !row_num) sheet;
        cell_num := !cell_num + cell.cell_width
      done;
    | _ -> ()
    ) childs;
    incr row_num
  done;
  Unit
end

let data str = Data str

let load catalog source = begin

  (* Mutable datas *)
  let sheet = Sheet.create catalog in
  let cache = Hashtbl.create 10 in

  let table = String_dict.of_alist_exn [
    ((NS.text ^ "p"), build_p);
    ((NS.table ^ "table-cell"), build_cell cache);
    ((NS.table ^ "table-row"), build_row sheet (ref 1))
  ] in

  let el (((ns, name), attributes):Xmlm.tag) childs = begin
    match String_dict.find table (ns ^ name) with
    | Some f -> f attributes childs
    | None -> Unit
  end in

  match Xmlm.input_doc_tree ~el ~data source with
  | _, Unit -> sheet
  | _ -> raise Not_found
end