mu 5.7.9 → 5.7.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +0,0 @@
1
- # Copyright (C) 2008 Mu Dynamics, Inc
2
- #
3
- # This program is confidential and proprietary to Mu Dynamics, Inc and
4
- # may not be reproduced, published or disclosed to others without its
5
- # authorization.
6
-
7
- module LibXML
8
- module XML
9
- class Node
10
- # Get first element, optionally with name.
11
- def first_element name=nil
12
- each_element do |element|
13
- if not name or name == element.name
14
- return element
15
- end
16
- end
17
- return nil
18
- end
19
- end
20
- end
21
- end
@@ -1,559 +0,0 @@
1
- # Copyright (C) 2008 Mu Dynamics, Inc
2
- #
3
- # This program is confidential and proprietary to Mu Dynamics, Inc and
4
- # may not be reproduced, published or disclosed to others without its
5
- # authorization.
6
-
7
- require 'libxml'
8
- require 'mu/libxml'
9
-
10
- # Helper mixin for XML serialization.
11
- #
12
- # Example:
13
- # class MyObject
14
- # include XMLizable
15
- # xmlizable 'my-object' do
16
- # xml_element :a, Integer
17
- # xml_attribute :b, String, :required => true
18
- # end
19
- # ...
20
- module XMLizable
21
- class XMLParseError < StandardError ; end
22
-
23
- def self.included klass
24
- super
25
- klass.extend XMLizableClassMethods
26
- end
27
-
28
- module XMLizableClassMethods
29
- # Declare object to be XML serializable. If name is not given, the
30
- # object itself cannot be serialized and only XMLizable subclasses can.
31
- def xmlizable name=nil #block
32
- if defined? @xmlizable_called
33
- raise ArgumentError, "Only call xmlizable once"
34
- end
35
- @xmlizable_called = true
36
- @xmlizable_name = name
37
- @xmlizable_classes = {}
38
- if name
39
- # Add this class to ancestor's XMLizable class map.
40
- classes = ancestors.select { |ancestor| ancestor < XMLizable }
41
- classes.each do |ancestor|
42
- if ancestor.instance_variable_defined? :@xmlizable_classes
43
- classes =
44
- ancestor.instance_variable_get :@xmlizable_classes
45
- classes[name] = self
46
- end
47
- end
48
- end
49
-
50
- # Set xmlizable elements and attributes in block
51
- @xmlizable_elements = []
52
- @xmlizable_attributes = []
53
- if block_given?
54
- yield
55
- end
56
-
57
- # Re-init to merge in ancestor elements and attributes
58
- init_elements_and_attrs
59
- end
60
-
61
- # Initialize @xmlizable_elements and @xmlizable_attributes
62
- def init_elements_and_attrs
63
- # Get non-duplicate elements/attibutes from ancestors (including
64
- # current class)
65
- elems = []
66
- attrs = []
67
- attr_names = Set.new
68
- elem_names = Set.new
69
- ancestors.each do |klass|
70
- next unless klass < XMLizable
71
- if klass.instance_variable_defined? :@xmlizable_elements
72
- klass.xmlizable_elements.reverse_each do |e|
73
- elems.unshift e unless elem_names.include? e.name
74
- elem_names << e.name
75
- end
76
- end
77
- if klass.instance_variable_defined? :@xmlizable_attributes
78
- klass.xmlizable_attributes.reverse_each do |a|
79
- attrs.unshift a unless attr_names.include? a.name
80
- attr_names << a.name
81
- end
82
- end
83
- end
84
- @xmlizable_attributes = attrs.freeze
85
- @xmlizable_elements = elems.freeze
86
- end
87
-
88
- # There may be subclasses that indirectly include XMLizable or that never
89
- # call xmlizable. Use inherited hook to make sure they are initialized
90
- def inherited(klass)
91
- klass.init_elements_and_attrs
92
- end
93
-
94
- # Declare variable to be serialized as an XML element of type.
95
- def xml_element name, klass, *opts
96
- element = xml_object name, klass, *opts
97
- @xmlizable_elements << element
98
- end
99
-
100
- # Declare variable to be serialized as an array of XML element with
101
- # of type. The type must support to/from_libxml.
102
- def xml_elements name, klass, *opts
103
- element = xml_object name, klass, *opts
104
- element.array = true
105
- @xmlizable_elements << element
106
- end
107
-
108
- # Declare variable to be serialized as an XML attribute of type.
109
- def xml_attribute name, klass, *opts
110
- attribute = xml_object name, klass, *opts
111
- @xmlizable_attributes << attribute
112
- end
113
-
114
- def xmlizable_name
115
- @xmlizable_name
116
- end
117
-
118
- # Get list of elements.
119
- def xmlizable_elements
120
- @xmlizable_elements
121
- end
122
-
123
- # Get list of attributes.
124
- def xmlizable_attributes
125
- @xmlizable_attributes
126
- end
127
-
128
- # Create object to XML::Node. The object must be creatable by
129
- # calling a constructor with no arguments.
130
- def from_libxml node
131
- if node.name != @xmlizable_name
132
- xmlizable_classes = @xmlizable_classes
133
- klass = xmlizable_classes[node.name]
134
- if klass and klass != self
135
- return klass.from_libxml(node)
136
- else
137
- raise XMLParseError, "Unknown element: #{node.name}"
138
- end
139
- end
140
-
141
- object = self.new
142
- @xmlizable_attributes.each do |attribute|
143
- value = node[attribute.xml_name]
144
- if value
145
- attribute_klass = attribute.klass
146
- if attribute_klass == String
147
- # Common case with no special treatment
148
- elsif attribute_klass <= Integer
149
- value = value.to_i
150
- elsif attribute_klass <= Float
151
- value = value.to_f
152
- elsif attribute_klass <= TrueClass
153
- if value == 'true'
154
- value = true
155
- elsif value == 'false'
156
- value = false
157
- else
158
- raise XMLParseError, 'Invalid boolean value: ' +
159
- value.inspect
160
- end
161
- elsif attribute_klass <= Symbol
162
- begin
163
- value = value.to_sym
164
- rescue ArgumentError
165
- raise XMLParseError, "Invalid value: #{value.inspect} in #{node.name}"
166
- end
167
- if attribute.enum and not attribute.enum.member? value
168
- raise XMLParseError, "Invalid value: #{value.inspect} in #{node.name}"
169
- end
170
- end
171
- object.send :"#{attribute.name}=", value
172
- elsif attribute.required
173
- raise XMLParseError, "Required attribute missing: " +
174
- attribute.xml_name + ' in ' + node.name
175
- else
176
- object.send "#{attribute.name}=", attribute.default
177
- end
178
- end
179
- @xmlizable_elements.each do |element|
180
- element_node = node.first_element element.xml_name
181
- if element_node
182
- element_klass = element.klass
183
- if element_klass == String or element_klass == Mu::Scenario::Field::Reference
184
- content = element_node.content
185
- case element.escape
186
- when :default
187
- content.delete! TAB_CR_LF
188
- value = XMLizable.unescape content
189
- when :no_newline
190
- content.delete! TAB_CR
191
- value = XMLizable.unescape content
192
- when :regexp
193
- value = content
194
- else
195
- raise XMLParseError, "Unknown escape type: #{element.escape}"
196
- end
197
- elsif element_klass == Integer
198
- value = element_node.content.to_i(0)
199
- elsif element.array
200
- value = []
201
- element_node.each_element do |value_node|
202
- value << element.klass.from_libxml(value_node)
203
- end
204
- elsif element_klass == Float
205
- value = element_node.content.to_f
206
- elsif element_klass == Symbol
207
- value = element_node.content
208
- begin
209
- value = value.to_sym
210
- rescue ArgumentError
211
- raise XMLParseError, "Invalid value: #{value.inspect} in #{node.name}"
212
- end
213
- if element.enum and not element.enum.member? value
214
- raise XMLParseError, "Invalid value: #{value.inspect} in #{node.name}"
215
- end
216
- elsif element_klass == TrueClass
217
- value = element_node.content
218
- if value == 'true'
219
- value = true
220
- elsif value == 'false'
221
- value = false
222
- else
223
- raise XMLParseError, 'Invalid boolean value: ' +
224
- value.inspect
225
- end
226
- else
227
- value_node = element_node.first_element
228
- if not value_node
229
- raise XMLParseError, 'Element missing value: ' +
230
- element.xml_name + ' in ' + node.name
231
- end
232
- value = element_klass.from_libxml value_node
233
- end
234
- object.send :"#{element.name}=", value
235
- elsif element.required
236
- raise XMLParseError, 'Required element missing: ' +
237
- element.xml_name + ' in ' + node.name
238
- else
239
- object.send :"#{element.name}=", element.default
240
- end
241
- end
242
- object.post_from_libxml node
243
- return object
244
- end
245
-
246
- private
247
-
248
- # Create new XML serializable object. Internal helper method.
249
- XMLObject = ::Struct.new :name, :klass, :default, :required, :internal,
250
- :array, :enum, :xml_name, :label, :doc, :escape
251
- def xml_object name, klass, *opts
252
- # Get default value
253
- if not opts[0].is_a? Hash
254
- default = opts.shift
255
- else
256
- default = nil
257
- end
258
- array = false
259
- if opts[-1]
260
- required = opts[-1].fetch :required, false
261
- internal = opts[-1].fetch :internal, false
262
- enum = opts[-1][:enum]
263
- xml_name = opts[-1].fetch :xml_name, name.to_s
264
- label = opts[-1][:label]
265
- doc = opts[-1][:doc]
266
- escape = opts[-1].fetch :escape, :default
267
- else
268
- required = false
269
- internal = false
270
- enum = nil
271
- xml_name = name.to_s
272
- label = nil
273
- doc = nil
274
- escape = :default
275
- end
276
- return XMLObject.new(name, klass, default, required, internal,
277
- array, enum, xml_name, label, doc, escape)
278
- end
279
- end
280
-
281
- # Traverses the XMLizable tree rooted at the current node, passing
282
- # each child node to the associated block. If the block returns a
283
- # value other than nil or false the child is replaced with that
284
- # value.
285
- def xmlizable_replace_nodes &block
286
- self.class.xmlizable_attributes.each do |attribute|
287
- obj = self.send attribute.name
288
- if nobj = block.call(obj)
289
- self.send :"#{attribute.name}=", nobj
290
- end
291
- end
292
-
293
- self.class.xmlizable_elements.each do |element|
294
- obj = self.send element.name
295
- recurse = element.klass <= XMLizable
296
- if element.array
297
- obj.each_with_index do |sub_obj,i|
298
- if nsub_obj = block.call(sub_obj)
299
- obj[i] = nsub_obj
300
- elsif sub_obj and recurse
301
- sub_obj.xmlizable_replace_nodes(&block)
302
- end
303
- end
304
- else
305
- if nobj = block.call(obj)
306
- self.send :"#{element.name}=", nobj
307
- elsif obj and recurse
308
- obj.xmlizable_replace_nodes(&block)
309
- end
310
- end
311
- end
312
- end
313
-
314
- def xmlizable_map! &block
315
- self.class.xmlizable_attributes.each do |attribute|
316
- obj = self.send attribute.name
317
- if nobj = block.call(obj) and not nobj.equal?(obj)
318
- self.send :"#{attribute.name}=", nobj
319
- end
320
- end
321
-
322
- self.class.xmlizable_elements.each do |element|
323
- obj = self.send element.name
324
- if element.array
325
- obj.each_with_index do |sub_obj,i|
326
- if nsub_obj = block.call(sub_obj) and not nsub_obj.equal?(sub_obj)
327
- obj[i] = nsub_obj
328
- end
329
- end
330
- else
331
- if nobj = block.call(obj) and not nobj.equal?(obj)
332
- self.send :"#{element.name}=", nobj
333
- end
334
- end
335
- end
336
- end
337
-
338
- def xmlizable_each &block
339
- self.class.xmlizable_attributes.each do |attribute|
340
- obj = self.send attribute.name
341
- block.call(obj)
342
- end
343
-
344
- self.class.xmlizable_elements.each do |element|
345
- obj = self.send element.name
346
- if element.array
347
- obj.each do |sub_obj|
348
- block.call(sub_obj)
349
- end
350
- else
351
- block.call(obj)
352
- end
353
- end
354
- end
355
-
356
- # Recuses through xmlizable tree, yielding each element and attribute
357
- # to the supplied block. You can stop recursion at a given node
358
- # with "throw :prune" (like Find.find from stdlib)
359
- def xmlizable_find &block
360
- catch :prune do
361
- block.call(self)
362
-
363
- self.class.xmlizable_attributes.each do |attribute|
364
- catch :prune do
365
- obj = self.send attribute.name
366
- block.call(obj)
367
- end
368
- end
369
-
370
- self.class.xmlizable_elements.each do |element|
371
- obj = self.send element.name
372
- next if not obj
373
-
374
- recurse = element.klass <= XMLizable
375
- if element.array
376
- obj.each do |sub_obj|
377
- next unless sub_obj
378
- if recurse
379
- sub_obj.xmlizable_find(&block)
380
- end
381
- end
382
- else
383
- if recurse
384
- obj.xmlizable_find(&block)
385
- end
386
- end
387
- end
388
- end
389
- end
390
-
391
- # Same as xmlizable_find but only yields elements.
392
- def xmlizable_find_elements &block
393
- catch :prune do
394
- block.call(self)
395
-
396
- self.class.xmlizable_elements.each do |element|
397
- obj = self.send element.name
398
- next if not obj
399
-
400
- recurse = element.klass <= XMLizable
401
- if element.array
402
- obj.each do |sub_obj|
403
- next unless sub_obj
404
- if recurse
405
- sub_obj.xmlizable_find_elements(&block)
406
- end
407
- end
408
- else
409
- if recurse
410
- obj.xmlizable_find_elements(&block)
411
- end
412
- end
413
- end
414
- end
415
- end
416
-
417
- # Convert object to XML::Node
418
- def xmlizable_to_libxml
419
- name = self.class.instance_variable_get :@xmlizable_name
420
- if not name
421
- raise ArgumentError, 'Cannot convert nameless class to XML: ' +
422
- self.class.name
423
- end
424
- node = LibXML::XML::Node.new name
425
- self.class.xmlizable_attributes.each do |attribute|
426
- value = self.send attribute.name
427
- if value == attribute.default and not attribute.required
428
- next
429
- end
430
- node[attribute.xml_name] = value.to_s
431
- end
432
- self.class.xmlizable_elements.each do |element|
433
- value = self.send element.name
434
- element_klass = element.klass
435
- if value == element.default and not element.required
436
- next
437
- end
438
- element_node = LibXML::XML::Node.new element.xml_name
439
- node << element_node
440
- if element.array
441
- value.each do |sub_value|
442
- element_node << sub_value.to_libxml
443
- end
444
- elsif element_klass <= Integer or element_klass <= Float
445
- element_node.content = value.to_s
446
- elsif element_klass <= Symbol or element_klass <= TrueClass
447
- element_node.content = value.to_s
448
- elsif element_klass <= String
449
- value = value.to_s.dup
450
- case element.escape
451
- when :default
452
- element_node << XMLizable.escape(value, ESCAPES)
453
- when :no_newline
454
- element_node << XMLizable.escape(value, ESCAPES_NO_NEWLINE)
455
- when :regexp
456
- element_node << XMLizable.escape(value, ESCAPES_RE)
457
- else
458
- raise ArgumentError, "Unknown escape type: #{escape_table}"
459
- end
460
- else
461
- element_node << value.to_libxml
462
- end
463
- end
464
- return node
465
- end
466
-
467
- alias :to_libxml :xmlizable_to_libxml
468
-
469
- # Hook for logic after de-serializing an object.
470
- def post_from_libxml node
471
- end
472
-
473
-
474
- # Default map used for unescaping.
475
- # Backslash escapes are not substituted unless they are defined here
476
- # (or in a map explicitly passed to XMLizable.unescape). The exception
477
- # to this is "\x" sequences like "\x0a" which are always substituted.
478
- UNESCAPE_MAP = {
479
- 'n' => "\n",
480
- 't' => "\t",
481
- 'r' => "\r",
482
- '\\' => "\\"
483
- }
484
-
485
- # 1 2 3
486
- RE_ESCAPE = /\A([^\\]+)?(?:\\(?:(?:x([0-9a-fA-F]{1,2}))|(.)))?/
487
- # 1: optional text in front with no escapes
488
- # then we parse optional escape sequence:
489
- # 2: hex from \x escape
490
- # 3: char from other escapes
491
-
492
- # Returns an unescaped copy of string s.
493
- def self.unescape s, unescape_map=nil
494
- unescape_map ||= UNESCAPE_MAP
495
-
496
- input = s.dup
497
- output = []
498
- until input.empty?
499
- input.slice! RE_ESCAPE
500
- # leading text with no escape sequences
501
- output << $1 if $1
502
- if $2
503
- # We have an \x escape code. Interpret hex and get character.
504
- output << $2.hex.chr
505
- elsif $3
506
- # We have something like \t. Interpet it if we can, otherwise preserve both characters
507
- output << unescape_map.fetch($3,"\\#$3")
508
- end
509
- end
510
- output.join
511
- end
512
-
513
- # Default table used for converting ascii code to escaped representation
514
- ESCAPES = Array.new 256 do |i|
515
- case i
516
- when 9; "\\t".freeze
517
- when 13; "\\r".freeze
518
- when 92; "\\\\".freeze
519
- when 10; "\\n".freeze
520
- when 32..126; i.chr.freeze
521
- else ; ('\x%02x' % i).freeze
522
- end
523
- end
524
- ESCAPES.freeze
525
- # Text escaped with ESCAPES should not have any of these characters in it.
526
- TAB_CR_LF = "\t\r\n".freeze
527
-
528
- ESCAPES_NONE = Array.new(256) { |i| i.chr.freeze }
529
- ESCAPES_NONE.freeze
530
-
531
- # Regular expressions are escaped when writing to xml but not when
532
- # reading from XML. This is necessary because XML doesn't allow
533
- # some (all?) binary characters. Escaping doesn't change the meaning
534
- # of the regex and does not need to be reversed when deserealizing.
535
- ESCAPES_RE = Array.new 256 do |i|
536
- case i
537
- when 32..126; i.chr.freeze
538
- else ; ('\x%02x' % i).freeze
539
- end
540
- end
541
- ESCAPES_RE.freeze
542
-
543
- # Alternate escape table that doesn't escape newline characters
544
- ESCAPES_NO_NEWLINE = ESCAPES.dup
545
- ESCAPES_NO_NEWLINE[10] = "\n".freeze
546
- ESCAPES_NO_NEWLINE.freeze
547
- # Text escaped with ESCAPES_NO_NEWLINE should not have any of these characters in it.
548
- TAB_CR = "\t\r".freeze
549
-
550
- # Takes input and a table that maps ascii codes to their representation
551
- def self.escape input, escapes=nil
552
- escapes ||= ESCAPES
553
- output = []
554
- input.each_byte do |i|
555
- output << escapes[i]
556
- end
557
- output.join
558
- end
559
- end