ox 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ox might be problematic. Click here for more details.

Files changed (52) hide show
  1. data/LICENSE +27 -0
  2. data/README +153 -0
  3. data/ext/ox/base64.c +123 -0
  4. data/ext/ox/base64.h +44 -0
  5. data/ext/ox/cache.c +148 -0
  6. data/ext/ox/cache.h +43 -0
  7. data/ext/ox/cache8.c +80 -0
  8. data/ext/ox/cache8.h +43 -0
  9. data/ext/ox/cache8_test.c +69 -0
  10. data/ext/ox/cache_test.c +69 -0
  11. data/ext/ox/dump.c +901 -0
  12. data/ext/ox/extconf.rb +7 -0
  13. data/ext/ox/gen_load.c +196 -0
  14. data/ext/ox/obj_load.c +802 -0
  15. data/ext/ox/ox.c +456 -0
  16. data/ext/ox/ox.h +190 -0
  17. data/ext/ox/parse.c +629 -0
  18. data/lib/ox.rb +97 -0
  19. data/lib/ox/cdata.rb +12 -0
  20. data/lib/ox/comment.rb +13 -0
  21. data/lib/ox/doctype.rb +13 -0
  22. data/lib/ox/document.rb +20 -0
  23. data/lib/ox/element.rb +67 -0
  24. data/lib/ox/node.rb +24 -0
  25. data/test/Sample.graffle +2318 -0
  26. data/test/cache16_test.rb +17 -0
  27. data/test/cache8_test.rb +17 -0
  28. data/test/cache_test.rb +17 -0
  29. data/test/files.rb +34 -0
  30. data/test/func.rb +228 -0
  31. data/test/gen_sample.rb +22 -0
  32. data/test/obj_sample.rb +19 -0
  33. data/test/ox/change.rb +16 -0
  34. data/test/ox/dir.rb +21 -0
  35. data/test/ox/doc.rb +39 -0
  36. data/test/ox/file.rb +33 -0
  37. data/test/ox/group.rb +18 -0
  38. data/test/ox/hasprops.rb +18 -0
  39. data/test/ox/layer.rb +14 -0
  40. data/test/ox/line.rb +22 -0
  41. data/test/ox/oval.rb +12 -0
  42. data/test/ox/rect.rb +12 -0
  43. data/test/ox/shape.rb +37 -0
  44. data/test/ox/text.rb +23 -0
  45. data/test/perf_gen.rb +193 -0
  46. data/test/perf_mars.rb +97 -0
  47. data/test/perf_obj.rb +201 -0
  48. data/test/perf_pod.rb +88 -0
  49. data/test/perf_write.rb +80 -0
  50. data/test/sample.rb +62 -0
  51. data/test/test.rb +70 -0
  52. metadata +106 -0
data/ext/ox/ox.c ADDED
@@ -0,0 +1,456 @@
1
+ /* ox.c
2
+ * Copyright (c) 2011, Peter Ohler
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * - Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * - Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * - Neither the name of Peter Ohler nor the names of its contributors may be
16
+ * used to endorse or promote products derived from this software without
17
+ * specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #include <stdlib.h>
32
+ #include <errno.h>
33
+ #include <stdio.h>
34
+ #include <string.h>
35
+
36
+ #include "ruby.h"
37
+ #include "ox.h"
38
+
39
+ void Init_ox();
40
+
41
+ VALUE Ox = Qnil;
42
+
43
+ ID at_id;
44
+ ID attributes_id;
45
+ ID beg_id;
46
+ ID den_id;
47
+ ID end_id;
48
+ ID excl_id;
49
+ ID inspect_id;
50
+ ID keys_id;
51
+ ID local_id;
52
+ ID nodes_id;
53
+ ID num_id;
54
+ ID parse_id;
55
+ ID to_c_id;
56
+ ID to_s_id;
57
+ ID tv_sec_id;
58
+ ID tv_usec_id;
59
+ ID value_id;
60
+
61
+ VALUE version_sym;
62
+ VALUE standalone_sym;
63
+ VALUE encoding_sym;
64
+ VALUE indent_sym;
65
+ VALUE xsd_date_sym;
66
+ VALUE opt_format_sym;
67
+ VALUE circular_sym;
68
+ VALUE mode_sym;
69
+ VALUE auto_sym;
70
+ VALUE optimized_sym;
71
+ VALUE object_sym;
72
+ VALUE generic_sym;
73
+ VALUE limited_sym;
74
+ VALUE best_effort_sym;
75
+ VALUE trace_sym;
76
+ VALUE empty_string;
77
+ VALUE zero_fixnum;
78
+
79
+ VALUE ox_cdata_clas;
80
+ VALUE ox_comment_clas;
81
+ VALUE ox_doctype_clas;
82
+ VALUE ox_document_clas;
83
+ VALUE ox_element_clas;
84
+ VALUE ox_text_clas;
85
+ VALUE struct_class;
86
+ VALUE time_class;
87
+
88
+ Cache symbol_cache = 0;
89
+ Cache class_cache = 0;
90
+ Cache attr_cache = 0;
91
+
92
+ extern ParseCallbacks ox_obj_callbacks;
93
+ extern ParseCallbacks ox_gen_callbacks;
94
+ extern ParseCallbacks ox_limited_callbacks;
95
+
96
+ static void parse_dump_options(VALUE options, int *indent, int *xsd_date, int *circular);
97
+
98
+ /* call-seq: parse_obj(xml) => Object
99
+ *
100
+ * Parses an XML document String that is in the object format and returns an
101
+ * Object of the type represented by the XML. This function expects an
102
+ * optimized XML formated String. For other formats use the more generic
103
+ * Ox.load() method. Raises an exception if the XML is malformed or the
104
+ * classes specified in the file are not valid.
105
+ * [xml] XML String in optimized Object format.
106
+ */
107
+ static VALUE
108
+ to_obj(VALUE self, VALUE ruby_xml) {
109
+ VALUE obj;
110
+ char *xml;
111
+
112
+ Check_Type(ruby_xml, T_STRING);
113
+ // the xml string gets modified so make a copy of it
114
+ xml = strdup(StringValuePtr(ruby_xml));
115
+ obj = parse(xml, ox_obj_callbacks, 0, 0, 0);
116
+ free(xml);
117
+ return obj;
118
+ }
119
+
120
+ /* call-seq: parse(xml) => Ox::Document or Ox::Element
121
+ *
122
+ * Parses and XML document String into an Ox::Document or Ox::Element.
123
+ * Raises an exception if the XML is malformed.
124
+ * [xml] XML String
125
+ */
126
+ static VALUE
127
+ to_gen(VALUE self, VALUE ruby_xml) {
128
+ VALUE obj;
129
+ char *xml;
130
+
131
+ Check_Type(ruby_xml, T_STRING);
132
+ // the xml string gets modified so make a copy of it
133
+ xml = strdup(StringValuePtr(ruby_xml));
134
+ obj = parse(xml, ox_gen_callbacks, 0, 0, 0);
135
+ free(xml);
136
+ return obj;
137
+ }
138
+
139
+ typedef enum {
140
+ AutoMode = 0, // not supported
141
+ ObjMode = 1,
142
+ GenMode = 2,
143
+ LimMode = 3,
144
+ } LoadMode;
145
+
146
+ static VALUE
147
+ load(char *xml, int argc, VALUE *argv, VALUE self) {
148
+ VALUE obj;
149
+ int mode = AutoMode;
150
+ int trace = 0;
151
+ int best_effort = 0;
152
+
153
+ if (1 == argc && rb_cHash == rb_obj_class(*argv)) {
154
+ VALUE h = *argv;
155
+ VALUE v;
156
+
157
+ if (Qnil != (v = rb_hash_lookup(h, mode_sym))) {
158
+ if (auto_sym == v) {
159
+ mode = AutoMode;
160
+ } else if (object_sym == v) {
161
+ mode = ObjMode;
162
+ } else if (optimized_sym == v) {
163
+ mode = ObjMode;
164
+ } else if (generic_sym == v) {
165
+ mode = GenMode;
166
+ } else if (limited_sym == v) {
167
+ mode = LimMode;
168
+ } else {
169
+ rb_raise(rb_eArgError, ":mode must be :generic, :object, or :limited.\n");
170
+ }
171
+ }
172
+ if (Qnil != (v = rb_hash_lookup(h, trace_sym))) {
173
+ Check_Type(v, T_FIXNUM);
174
+ trace = FIX2INT(v);
175
+ }
176
+ if (Qnil != (v = rb_hash_lookup(h, best_effort_sym))) {
177
+ best_effort = (Qfalse != v);
178
+ }
179
+ }
180
+ switch (mode) {
181
+ case ObjMode:
182
+ obj = parse(xml, ox_obj_callbacks, 0, trace, best_effort);
183
+ break;
184
+ case GenMode:
185
+ obj = parse(xml, ox_gen_callbacks, 0, trace, 0);
186
+ break;
187
+ case LimMode:
188
+ obj = parse(xml, ox_limited_callbacks, 0, trace, best_effort);
189
+ break;
190
+ case AutoMode:
191
+ default:
192
+ obj = parse(xml, ox_gen_callbacks, 0, trace, 0);
193
+ break;
194
+ }
195
+ free(xml);
196
+
197
+ return obj;
198
+ }
199
+
200
+ /* call-seq: load(xml, options) => Ox::Document or Ox::Element or Object
201
+ *
202
+ * Parses and XML document String into an Ox::Document, or Ox::Element, or
203
+ * Object depending on the options. Raises an exception if the XML is
204
+ * malformed or the classes specified are not valid.
205
+ * [xml] XML String
206
+ * [options] load options
207
+ * [:mode] format expected
208
+ * [:object] object format
209
+ * [:generic] read as a generic XML file
210
+ * [:limited] read as a generic XML file but with callbacks on text and elements events only
211
+ * [:trace] trace level as a Fixnum, default: 0 (silent)
212
+ * [:best_effort] use best effort to create Objects using nil if undefined Class, default: 0
213
+ */
214
+ static VALUE
215
+ load_str(int argc, VALUE *argv, VALUE self) {
216
+ char *xml;
217
+
218
+ Check_Type(*argv, T_STRING);
219
+ // the xml string gets modified so make a copy of it
220
+ xml = strdup(StringValuePtr(*argv));
221
+
222
+ return load(xml, argc - 1, argv + 1, self);
223
+ }
224
+
225
+ /* call-seq: load_file(file_path, xml, options) => Ox::Document or Ox::Element or Object
226
+ *
227
+ * Parses and XML document from a file into an Ox::Document, or Ox::Element,
228
+ * or Object depending on the options. Raises an exception if the XML is
229
+ * malformed or the classes specified are not valid.
230
+ * [file_path] file path to read the XML document from
231
+ * [xml] XML String
232
+ * [options] load options
233
+ * [:mode] format expected
234
+ * [:object] object format
235
+ * [:generic] read as a generic XML file
236
+ * [:limited] read as a generic XML file but with callbacks on text and elements events only
237
+ * [:trace] trace level as a Fixnum, default: 0 (silent)
238
+ * [:best_effort] use best effort to create Objects using nil if undefined Class, default: 0
239
+ */
240
+ static VALUE
241
+ load_file(int argc, VALUE *argv, VALUE self) {
242
+ char *path;
243
+ char *xml;
244
+ FILE *f;
245
+ unsigned long len;
246
+
247
+ Check_Type(*argv, T_STRING);
248
+ path = StringValuePtr(*argv);
249
+ if (0 == (f = fopen(path, "r"))) {
250
+ rb_raise(rb_eIOError, "%s\n", strerror(errno));
251
+ }
252
+ fseek(f, 0, SEEK_END);
253
+ len = ftell(f);
254
+ if (0 == (xml = malloc(len + 1))) {
255
+ fclose(f);
256
+ rb_raise(rb_eNoMemError, "Could not allocate memory for %ld byte file.\n", len);
257
+ }
258
+ fseek(f, 0, SEEK_SET);
259
+ if (len != fread(xml, 1, len, f)) {
260
+ fclose(f);
261
+ rb_raise(rb_eLoadError, "Failed to read %ld bytes from %s.\n", len, path);
262
+ }
263
+ fclose(f);
264
+ xml[len] = '\0';
265
+
266
+ return load(xml, argc - 1, argv + 1, self);
267
+ }
268
+
269
+ static void
270
+ parse_dump_options(VALUE options, int *indent, int *xsd_date, int *circular) {
271
+ if (rb_cHash == rb_obj_class(options)) {
272
+ VALUE v;
273
+
274
+ if (Qnil != (v = rb_hash_lookup(options, indent_sym))) {
275
+ if (rb_cFixnum != rb_obj_class(v)) {
276
+ rb_raise(rb_eArgError, ":indent must be a Fixnum.\n");
277
+ }
278
+ *indent = NUM2INT(v);
279
+ }
280
+ if (Qnil != (v = rb_hash_lookup(options, xsd_date_sym))) {
281
+ VALUE c = rb_obj_class(v);
282
+
283
+ if (rb_cTrueClass == c) {
284
+ *xsd_date = 1;
285
+ } else if (rb_cFalseClass == c) {
286
+ *xsd_date = 0;
287
+ } else {
288
+ rb_raise(rb_eArgError, ":xsd_date must be true or false.\n");
289
+ }
290
+ }
291
+ if (Qnil != (v = rb_hash_lookup(options, circular_sym))) {
292
+ VALUE c = rb_obj_class(v);
293
+
294
+ if (rb_cTrueClass == c) {
295
+ *circular = 1;
296
+ } else if (rb_cFalseClass == c) {
297
+ *circular = 0;
298
+ } else {
299
+ rb_raise(rb_eArgError, ":circular must be true or false.\n");
300
+ }
301
+ }
302
+ }
303
+ }
304
+
305
+ /* call-seq: dump(obj, options) => xml-string
306
+ *
307
+ * Dumps an Object (obj) to a string.
308
+ * [obj] Object to serialize as an XML document String
309
+ * [options] formating options
310
+ * [:indent] number of spaces to use as the standard indention, default: 2
311
+ * [:xsd_date] use XSD date format if true, default: false
312
+ * [:circular] allow circular references, default: false
313
+ */
314
+ static VALUE
315
+ dump(int argc, VALUE *argv, VALUE self) {
316
+ char *xml;
317
+ int indent = 2;
318
+ int xsd_date = 0;
319
+ int circular = 0;
320
+ VALUE rstr;
321
+
322
+ if (2 == argc) {
323
+ parse_dump_options(argv[1], &indent, &xsd_date, &circular);
324
+ }
325
+ if (0 == (xml = write_obj_to_str(*argv, indent, xsd_date, circular))) {
326
+ rb_raise(rb_eNoMemError, "Not enough memory.\n");
327
+ }
328
+ rstr = rb_str_new2(xml);
329
+ free(xml);
330
+
331
+ return rstr;
332
+ }
333
+
334
+ /* call-seq: to_file(file_path, obj, options)
335
+ *
336
+ * Dumps and Object to the specified file.
337
+ * [file_path] file path to write the XML document to
338
+ * [obj] Object to serialize as an XML document String
339
+ * [options] formating options
340
+ * [:indent] number of spaces to use as the standard indention, default: 2
341
+ * [:xsd_date] use XSD date format if true, default: false
342
+ * [:circular] allow circular references, default: false
343
+ */
344
+ static VALUE
345
+ to_file(int argc, VALUE *argv, VALUE self) {
346
+ int indent = 2;
347
+ int xsd_date = 0;
348
+ int circular = 0;
349
+
350
+ if (3 == argc) {
351
+ parse_dump_options(argv[2], &indent, &xsd_date, &circular);
352
+ }
353
+ Check_Type(*argv, T_STRING);
354
+ write_obj_to_file(argv[1], StringValuePtr(*argv), indent, xsd_date, circular);
355
+
356
+ return Qnil;
357
+ }
358
+
359
+ extern void ox_cache_test(void);
360
+
361
+ static VALUE
362
+ cache_test(VALUE self) {
363
+ ox_cache_test();
364
+ return Qnil;
365
+ }
366
+
367
+ extern void ox_cache8_test(void);
368
+
369
+ static VALUE
370
+ cache8_test(VALUE self) {
371
+ ox_cache8_test();
372
+ return Qnil;
373
+ }
374
+
375
+ void Init_ox() {
376
+ Ox = rb_define_module("Ox");
377
+ rb_define_module_function(Ox, "parse_obj", to_obj, 1);
378
+ rb_define_module_function(Ox, "parse", to_gen, 1);
379
+ rb_define_module_function(Ox, "load", load_str, -1);
380
+
381
+ rb_define_module_function(Ox, "to_xml", dump, -1);
382
+ rb_define_module_function(Ox, "dump", dump, -1);
383
+
384
+ rb_define_module_function(Ox, "load_file", load_file, -1);
385
+ rb_define_module_function(Ox, "to_file", to_file, -1);
386
+
387
+ rb_require("time");
388
+ parse_id = rb_intern("parse");
389
+ local_id = rb_intern("local");
390
+ at_id = rb_intern("at");
391
+ inspect_id = rb_intern("inspect");
392
+ beg_id = rb_intern("@beg");
393
+ end_id = rb_intern("@end");
394
+ den_id = rb_intern("@den");
395
+ excl_id = rb_intern("@excl");
396
+ value_id = rb_intern("@value");
397
+ nodes_id = rb_intern("@nodes");
398
+ num_id = rb_intern("@num");
399
+ attributes_id = rb_intern("@attributes");
400
+ keys_id = rb_intern("keys");
401
+ tv_sec_id = rb_intern("tv_sec");
402
+ tv_usec_id = rb_intern("tv_usec");
403
+ to_c_id = rb_intern("to_c");
404
+ to_s_id = rb_intern("to_s");
405
+
406
+ time_class = rb_const_get(rb_cObject, rb_intern("Time"));
407
+ struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
408
+
409
+ version_sym = ID2SYM(rb_intern("version"));
410
+ standalone_sym = ID2SYM(rb_intern("standalone"));
411
+ encoding_sym = ID2SYM(rb_intern("encoding"));
412
+ indent_sym = ID2SYM(rb_intern("indent"));
413
+ xsd_date_sym = ID2SYM(rb_intern("xsd_date"));
414
+ opt_format_sym = ID2SYM(rb_intern("opt_format"));
415
+ mode_sym = ID2SYM(rb_intern("mode"));
416
+ auto_sym = ID2SYM(rb_intern("auto"));
417
+ optimized_sym = ID2SYM(rb_intern("optimized"));
418
+ object_sym = ID2SYM(rb_intern("object"));
419
+ circular_sym = ID2SYM(rb_intern("circular"));
420
+ generic_sym = ID2SYM(rb_intern("generic"));
421
+ limited_sym = ID2SYM(rb_intern("limited"));
422
+ trace_sym = ID2SYM(rb_intern("trace"));
423
+ best_effort_sym = ID2SYM(rb_intern("best_effort"));
424
+ empty_string = rb_str_new2("");
425
+ zero_fixnum = INT2NUM(0);
426
+
427
+ //rb_require("node"); // generic xml node classes
428
+ ox_document_clas = rb_const_get(Ox, rb_intern("Document"));
429
+ ox_element_clas = rb_const_get(Ox, rb_intern("Element"));
430
+ ox_comment_clas = rb_const_get(Ox, rb_intern("Comment"));
431
+ ox_doctype_clas = rb_const_get(Ox, rb_intern("DocType"));
432
+ ox_cdata_clas = rb_const_get(Ox, rb_intern("CData"));
433
+
434
+ ox_cache_new(&symbol_cache);
435
+ ox_cache_new(&class_cache);
436
+ ox_cache_new(&attr_cache);
437
+
438
+ rb_define_module_function(Ox, "cache_test", cache_test, 0);
439
+ rb_define_module_function(Ox, "cache8_test", cache8_test, 0);
440
+ }
441
+
442
+ void
443
+ _raise_error(const char *msg, const char *xml, const char *current, const char* file, int line) {
444
+ int xline = 1;
445
+ int col = 1;
446
+
447
+ for (; xml < current && '\n' != *current; current--) {
448
+ col++;
449
+ }
450
+ for (; xml < current; current--) {
451
+ if ('\n' == *current) {
452
+ xline++;
453
+ }
454
+ }
455
+ rb_raise(rb_eEncodingError, "%s at line %d, column %d [%s:%d]\n", msg, xline, col, file, line);
456
+ }