voruby2-preview 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2100 @@
1
+ require 'voruby/votable/votable'
2
+
3
+ module VORuby
4
+ module VOTable
5
+
6
+ # Classes that represent the domain objects in the VOTable 1.1 specification
7
+ # (http://www.ivoa.net/Documents/latest/VOT.html).
8
+ module V1_1
9
+
10
+ # The root element of the votable.
11
+ class VOTable < VORuby::VOTable::Base
12
+ ELEMENT_NAME = 'VOTABLE'
13
+
14
+ def self.serialization_order
15
+ [
16
+ :description, :definitions, :coordinate_systems, :params, :infos, :resources,
17
+ :version, :id
18
+ ]
19
+ end
20
+
21
+ # Create a votable.
22
+ # To parse an existing votable one would normally do this:
23
+ # votable = VOTable.from_file('my_file.xml')
24
+ # but any of the other methods described in VOTable::Base#new are also acceptable:
25
+ # votable = VOTable.new(
26
+ # :version => '1.1',
27
+ # :id => 'votable1',
28
+ # :description => Description.new(:text => 'A test votable'),
29
+ # :resources => [Resource.new(...)]
30
+ # ) # etc.
31
+ #
32
+ # or to create a blank VOTable that you'll build up later:
33
+ # votable = VOTable.new()
34
+ def initialize(defn=nil)
35
+ super(defn)
36
+ self.version = '1.1' if !self.version
37
+ end
38
+
39
+ # Retrieve the version. By default this is '1.1'.
40
+ def version
41
+ self.node['version']
42
+ end
43
+
44
+ # Set the version. Normally you should leave
45
+ # this as 1.1.
46
+ def version=(v)
47
+ @node['version'] = v.to_s
48
+ end
49
+
50
+ def id
51
+ self.node['ID']
52
+ end
53
+
54
+ def id=(i)
55
+ @node['ID'] = i.to_s
56
+ end
57
+
58
+ # Retrieve the description (Description).
59
+ def description
60
+ get_element(Description)
61
+ end
62
+
63
+ # Set the description (Description).
64
+ # votable.description = Description.new(:text => 'A fascinating votable')
65
+ def description=(d)
66
+ set_element(Description, d)
67
+ end
68
+
69
+ # Retrieve the definitions (Definitions).
70
+ # _Deprecated_.
71
+ def definitions
72
+ get_element(Definitions)
73
+ end
74
+
75
+ # Set the definitions (Definitions).
76
+ # _Deprecated_.
77
+ def definitions=(d)
78
+ set_element(Definitions, d)
79
+ end
80
+
81
+ # Retrieve the coordinate systems (Coosys).
82
+ def coordinate_systems
83
+ HomogeneousNodeList.new(self.node, xpath_for(Coosys), Coosys)
84
+ end
85
+
86
+ # Set the coordinate systems.
87
+ # Takes an array of Coosys objects.
88
+ # votable.coordinate_systems = [Coosys.new(), ...]
89
+ def coordinate_systems=(systems)
90
+ self.coordinate_systems.replace(systems)
91
+ end
92
+
93
+ # Retrieve the parameters (Param).
94
+ # Returns a HomogeneousNodeList.
95
+ def params
96
+ HomogeneousNodeList.new(self.node, xpath_for(Param), Param)
97
+ end
98
+
99
+ # Set the parameters.
100
+ # Takes an array of Param objects.
101
+ # votable.params = [Param.new(), ...]
102
+ def params=(parameters)
103
+ self.params.replace(parameters)
104
+ end
105
+
106
+ # Retrieve the information list (Info).
107
+ # Returns a HomogeneousNodeList.
108
+ def infos
109
+ HomogeneousNodeList.new(self.node, xpath_for(Info), Info)
110
+ end
111
+
112
+ # Set the information list.
113
+ # Takes an array of Info objects.
114
+ # votable.infos = [Info.new(), ...]
115
+ def infos=(is)
116
+ self.infos.replace(is)
117
+ end
118
+
119
+ # Retrieve the resources (Resource).
120
+ # Returns a HomogeneousNodeList.
121
+ def resources
122
+ HomogeneousNodeList.new(self.node, xpath_for(Resource), Resource)
123
+ end
124
+
125
+ # Set the resources.
126
+ # Takes an array of Resource objects.
127
+ # votable.resources = [Resource.new(), ...]
128
+ def resources=(res)
129
+ self.resources.replace(res)
130
+ end
131
+
132
+ # Convert a VOTable to HTML.
133
+ # Not all attributes are converted.
134
+ # See test/voruby/votable/1.1/votable.html for an example of the output.
135
+ def to_html
136
+ builder = Builder::XmlMarkup.new(:indent => 2)
137
+
138
+ votable_opts = {:class => 'votable'}
139
+ votable_opts[:id] = self.id if self.id
140
+
141
+ builder.div(votable_opts){ |votable|
142
+ votable.div(self.description.text, :class => 'description') if self.description
143
+
144
+ self.resources.each do |res|
145
+ votable << res.to_html
146
+ end
147
+ }
148
+ end
149
+
150
+ protected
151
+
152
+ def methods_to_test_for_equality
153
+ [
154
+ :version, :id, :description,
155
+ :definitions, :coordinate_systems, :params, :infos, :resources
156
+ ]
157
+ end
158
+ end
159
+
160
+ # A simple description of something.
161
+ class Description < VORuby::VOTable::Base
162
+ ELEMENT_NAME = 'DESCRIPTION'
163
+
164
+ # Create a description.
165
+ # descr = Description.new(:text => 'Hello, world!')
166
+ def initialize(defn=nil)
167
+ super(defn)
168
+ end
169
+
170
+ # Retrieve the actual text of the description.
171
+ def text
172
+ self.node.content
173
+ end
174
+
175
+ # Set the text of the description.
176
+ # descr.text = 'Hello, world!'
177
+ def text=(text)
178
+ @node.content = text
179
+ end
180
+
181
+ protected
182
+
183
+ def methods_to_test_for_equality; [:text] end
184
+ end
185
+
186
+ # Definitions of various values appropriate to the votable.
187
+ # *DEPRECATED*.
188
+ class Definitions < VORuby::VOTable::Base
189
+ ELEMENT_NAME = 'DEFINITIONS'
190
+
191
+ # Create a set of definitions.
192
+ # definitions = Definitions.new(
193
+ # :coordinate_systems => [Coosys.new]
194
+ # :params => [Param.new]
195
+ # )
196
+ def initialize(defn=nil)
197
+ super(defn)
198
+ end
199
+
200
+ # Retrieve the coordinate systems (Coosys).
201
+ # Returns a HomogeneousNodeList.
202
+ def coordinate_systems
203
+ HomogeneousNodeList.new(self.node, xpath_for(Coosys), Coosys)
204
+ end
205
+
206
+ # Set the coordinate systems.
207
+ # Takes an array of Coosys objects.
208
+ # definitions.coordinate_systems = [Coosys.new(), ...]
209
+ def coordinate_systems=(systems)
210
+ self.coordinate_systems.replace(systems)
211
+ end
212
+
213
+ # Retrieve the parameters (Param).
214
+ # Returns a HomogeneousNodeList.
215
+ def params
216
+ HomogeneousNodeList.new(self.node, xpath_for(Param), Param)
217
+ end
218
+
219
+ # Set the parameter list.
220
+ # Takes an array of Param objects.
221
+ # definitions.params = [Param.new(), ...]
222
+ def params=(parameters)
223
+ self.params.replace(parameters)
224
+ end
225
+
226
+ protected
227
+
228
+ def methods_to_test_for_equality
229
+ [:coordinate_systems, :params]
230
+ end
231
+ end
232
+
233
+ # A celestial coordinate system, to which the components of a position on the celestial sphere refer.
234
+ class Coosys < VORuby::VOTable::Base
235
+ ELEMENT_NAME = 'COOSYS'
236
+
237
+ # Create a new coordinate system.
238
+ # coosys = Coosys.new(
239
+ # :id => 'J2000',
240
+ # :equinox => 'J2000.',
241
+ # :epoch => 'J2000.',
242
+ # :system => 'eq_FK5'
243
+ # )
244
+ # The default system is eq_FK5.
245
+ def initialize(defn=nil)
246
+ super(defn)
247
+ self.system = 'eq_FK5' if !self.system
248
+ end
249
+
250
+ def id
251
+ self.node['ID']
252
+ end
253
+
254
+ def id=(i)
255
+ @node['ID'] = i.to_s
256
+ end
257
+
258
+ def equinox
259
+ self.node['equinox']
260
+ end
261
+
262
+ def equinox=(e)
263
+ @node['equinox'] = e.to_s
264
+ end
265
+
266
+ def epoch
267
+ self.node['epoch']
268
+ end
269
+
270
+ def epoch=(e)
271
+ @node['epoch'] = e.to_s
272
+ end
273
+
274
+ def system
275
+ self.node['system']
276
+ end
277
+
278
+ # Set the system.
279
+ # May be one of: eq_FK4, eq_FK5, ICRS,
280
+ # ecl_FK4, ecl_FK5, galactic, supergalactic, xy,
281
+ # barycentric or geo_app.
282
+ def system=(s)
283
+ @node['system'] = s.to_s
284
+ end
285
+
286
+ protected
287
+
288
+ def methods_to_test_for_equality
289
+ [:id, :equinox, :epoch, :system]
290
+ end
291
+ end
292
+
293
+ # A global value associated with (usually) a table.
294
+ class Param < VORuby::VOTable::Base
295
+ include Castable
296
+
297
+ ELEMENT_NAME = 'PARAM'
298
+
299
+ def self.serialization_order
300
+ [
301
+ :description, :values, :links,
302
+ :id, :unit, :datatype, :arraysize, :precision, :width, :ref, :name,
303
+ :ucd, :utype, :value
304
+ ]
305
+ end
306
+
307
+ # Create a new parameter.
308
+ # param = Param.new(
309
+ # :name => 'Telescope',
310
+ # :datatype => 'float',
311
+ # :ucd => 'phys.size;instr.tel',
312
+ # :unit => 'm',
313
+ # :value => '3.6'
314
+ # )
315
+ def initialize(defn=nil)
316
+ super(defn)
317
+ end
318
+
319
+ def id
320
+ self.node['ID']
321
+ end
322
+
323
+ def id=(i)
324
+ @node['ID'] = i.to_s
325
+ end
326
+
327
+ def unit
328
+ self.node['unit']
329
+ end
330
+
331
+ # Set the unit.
332
+ # Units should be of the form outlined by Vizier[http://vizier.u-strasbg.fr/doc/catstd-3.2.htx].
333
+ def unit=(u)
334
+ @node['unit'] = u.to_s
335
+ end
336
+
337
+ def datatype
338
+ self.node['datatype']
339
+ end
340
+
341
+ # Set the datatype.
342
+ # Should be one of: boolean, bit, unsignedByte, short, int, long,
343
+ # char, unicodeChar, float, double, floatComplex, doubleComplex.
344
+ def datatype=(d)
345
+ @node['datatype'] = d.to_s
346
+ end
347
+
348
+ def precision
349
+ self.node['precision']
350
+ end
351
+
352
+ # Set the precision of the value.
353
+ # Should match [EF]?[1-9][0-9]*
354
+ # param.precision = '1'
355
+ def precision=(p)
356
+ @node['precision'] = p.to_s
357
+ end
358
+
359
+ def width
360
+ self.node['width'].to_i
361
+ end
362
+
363
+ # Set the width.
364
+ # Should be an integer.
365
+ # param.width = 2
366
+ def width=(w)
367
+ @node['width'] = w.to_s
368
+ end
369
+
370
+ def ref
371
+ self.node['ref']
372
+ end
373
+
374
+ def ref=(r)
375
+ @node['ref'] = r.to_s
376
+ end
377
+
378
+ def name
379
+ self.node['name']
380
+ end
381
+
382
+ def name=(n)
383
+ @node['name'] = n.to_s
384
+ end
385
+
386
+ def ucd
387
+ self.node['ucd']
388
+ end
389
+
390
+ # Set the unified content descriptor or UCD[http://vizier.u-strasbg.fr/doc/UCD.htx].
391
+ def ucd=(u)
392
+ @node['ucd'] = u.to_s
393
+ end
394
+
395
+ def utype
396
+ self.node['utype']
397
+ end
398
+
399
+ # Set the utype (i.e. the role of the column in the context of an external data model).
400
+ def utype=(u)
401
+ @node['utype'] = u.to_s
402
+ end
403
+
404
+ def arraysize
405
+ self.node['arraysize']
406
+ end
407
+
408
+ # Set the arraysize (if applicable).
409
+ # i.e. *, 8x2
410
+ def arraysize=(a)
411
+ @node['arraysize'] = a.to_s
412
+ end
413
+
414
+ # Retrieve the value of the parameter.
415
+ # If _cast_ is false (default), the value is returned as a simple string.
416
+ # However, if _cast_ is true, the datatype and arraysize are used to
417
+ # determine a suitable object (or list of objects) to return.
418
+ # See Castable#as_obj for more details.
419
+ def value(cast=false)
420
+ cast ? as_obj(self.value, self.datatype, self.arraysize) : self.node['value']
421
+ end
422
+
423
+ # Set the value of the parameter.
424
+ # An attempt is made to convert native ruby types using the datatype and
425
+ # arraysize parameters.
426
+ # param.value = Complex.new(1.1, 2.2) # etc.
427
+ def value=(v)
428
+ @node['value'] = v.is_a?(String) ? v : as_string(v, self.datatype, self.arraysize)
429
+ end
430
+
431
+ # Retrieve the description (Description).
432
+ def description
433
+ get_element(Description)
434
+ end
435
+
436
+ # Set the description (Description).
437
+ def description=(d)
438
+ set_element(Description, d)
439
+ end
440
+
441
+ # Retrieve the values (Values).
442
+ def values
443
+ get_element(Values)
444
+ end
445
+
446
+ # Set the values (Values).
447
+ def values=(v)
448
+ set_element(Values, v)
449
+ end
450
+
451
+ # Retrieve the links.
452
+ # Returns a HomogeneousNodeList object.
453
+ def links
454
+ HomogeneousNodeList.new(self.node, xpath_for(Link), Link)
455
+ end
456
+
457
+ # Set the links.
458
+ # Takes an array of Link objects.
459
+ def links=(lnks)
460
+ self.links.replace(lnks)
461
+ end
462
+
463
+ protected
464
+
465
+ def methods_to_test_for_equality
466
+ [
467
+ :id, :unit, :datatype, :precision, :width, :ref, :name, :ucd, :utype,
468
+ :value, :arraysize,
469
+ :description, :values, :links
470
+ ]
471
+ end
472
+ end
473
+
474
+ # A name-value pair.
475
+ class Info < VORuby::VOTable::Base
476
+ ELEMENT_NAME = 'INFO'
477
+
478
+ # Create a new name-value pair.
479
+ # info = Info.new(:name => 'history', :value => 'my first observation')
480
+ def initialize(defn=nil)
481
+ super(defn)
482
+ end
483
+
484
+ def id
485
+ self.node['ID']
486
+ end
487
+
488
+ def id=(i)
489
+ @node['ID'] = i.to_s
490
+ end
491
+
492
+ def name
493
+ self.node['name']
494
+ end
495
+
496
+ def name=(n)
497
+ @node['name'] = n.to_s
498
+ end
499
+
500
+ def value
501
+ self.node['value']
502
+ end
503
+
504
+ def value=(v)
505
+ @node['value'] = v.to_s
506
+ end
507
+
508
+ protected
509
+
510
+ def methods_to_test_for_equality
511
+ [:id, :name, :value]
512
+ end
513
+ end
514
+
515
+ # A description and the data values of some logically independent data structure
516
+ # within the VOTable.
517
+ class Resource < VORuby::VOTable::Base
518
+ ELEMENT_NAME = 'RESOURCE'
519
+
520
+ def self.serialization_order
521
+ [
522
+ :description, :infos, :coordinate_systems, :params, :links, :tables, :resources,
523
+ :id, :name, :type, :utype
524
+ ]
525
+ end
526
+
527
+ # Create a new resource.
528
+ # res = Resource.new(
529
+ # :name => 'myFavouriteGalaxies',
530
+ # :type => 'meta',
531
+ # :params => [Param.new()],
532
+ # :links => [Link.new()],
533
+ # :tables => [Table.new()]
534
+ # )
535
+ def initialize(defn=nil)
536
+ super(defn)
537
+ end
538
+
539
+ def name
540
+ self.node['name']
541
+ end
542
+
543
+ def name=(n)
544
+ @node['name'] = n.to_s
545
+ end
546
+
547
+ def id
548
+ self.node['ID']
549
+ end
550
+
551
+ def id=(i)
552
+ @node['ID'] = i.to_s
553
+ end
554
+
555
+ # Retrieve the utype.
556
+ def utype
557
+ self.node['utype']
558
+ end
559
+
560
+ # Set the utype (i.e. the role of the column in the context of an external data model).
561
+ def utype=(u)
562
+ @node['utype'] = u.to_s
563
+ end
564
+
565
+ # Retrieve the type.
566
+ # A type of 'meta' means that the resource is descriptive only
567
+ # (does not contain any actual data in any of its sub-elements).
568
+ def type
569
+ self.node['type']
570
+ end
571
+
572
+ # Set the type.
573
+ def type=(t)
574
+ @node['type'] = t.to_s
575
+ end
576
+
577
+ # Retrieve the description (Description).
578
+ def description
579
+ get_element(Description)
580
+ end
581
+
582
+ # Set the description (Description).
583
+ # res.description = Description.new(:text => 'A fascinating resource')
584
+ def description=(d)
585
+ set_element(Description, d)
586
+ end
587
+
588
+ # Retrieve the name-value pairs (Info).
589
+ # Returns a HomogeneousNodeList.
590
+ def infos
591
+ HomogeneousNodeList.new(self.node, xpath_for(Info), Info)
592
+ end
593
+
594
+ # Set any name-value pairs.
595
+ # res.infos = [Info.new(), ...]
596
+ def infos=(is)
597
+ self.infos.replace(is)
598
+ end
599
+
600
+ # Retrieve the coordinate systems (Coosys).
601
+ # Returns a HomogeneousNodeList.
602
+ def coordinate_systems
603
+ HomogeneousNodeList.new(self.node, xpath_for(Coosys), Coosys)
604
+ end
605
+
606
+ # Set the coordinate systems (Coosys).
607
+ # res.coordinate_systems = [Coosys.new(), ...]
608
+ def coordinate_systems=(systems)
609
+ self.coordinate_systems.replace(systems)
610
+ end
611
+
612
+ # Retrieve the parameters (Param).
613
+ # Returns a HomogeneousNodeList.
614
+ def params
615
+ HomogeneousNodeList.new(self.node, xpath_for(Param), Param)
616
+ end
617
+
618
+ # Set the parameters (Param).
619
+ # res.params = [Param.new(), ...]
620
+ def params=(parameters)
621
+ self.params.clear
622
+ parameters.each do |p|
623
+ self.params << p
624
+ end
625
+ end
626
+
627
+ # Retrieve the links (Link).
628
+ # Returns a HomogeneousNodeList.
629
+ def links
630
+ HomogeneousNodeList.new(self.node, xpath_for(Link), Link)
631
+ end
632
+
633
+ # Set the links (Link).
634
+ # res.links = [Link.new(), ...]
635
+ def links=(lnks)
636
+ self.links.replace(lnks)
637
+ end
638
+
639
+ # Retrieve the tables (Table).
640
+ # Returns a HomogeneousNodeList.
641
+ def tables
642
+ HomogeneousNodeList.new(self.node, xpath_for(Table), Table)
643
+ end
644
+
645
+ # Set the tables (Table).
646
+ # res.tables = [Table.new(), ....]
647
+ def tables=(tbls)
648
+ self.tables.replace(tbls)
649
+ end
650
+
651
+ # Retrieve sub-resources (Resource).
652
+ # Returns a HomogeneousNodeList.
653
+ def resources
654
+ HomogeneousNodeList.new(self.node, xpath_for(Resource), Resource)
655
+ end
656
+
657
+ # Set sub-resources.
658
+ # res.resources = [Resource.new(), ...]
659
+ def resources=(res)
660
+ self.resources.replace(res)
661
+ end
662
+
663
+ # Convert a resource to HTML.
664
+ def to_html
665
+ builder = Builder::XmlMarkup.new(:indent => 2, :margin => 1)
666
+
667
+ resource_opts = {:class => 'resource'}
668
+ resource_opts[:id] = self.id if self.id
669
+
670
+ builder.div(resource_opts){ |resource|
671
+ resource.h3(self.name) if self.name
672
+ resource.div(self.description.text, :class => 'description') if self.description
673
+
674
+ self.tables.each do |tbl|
675
+ resource << tbl.to_html
676
+ end
677
+
678
+ self.resources.each do |res|
679
+ resource << res.to_html
680
+ end
681
+ }
682
+ end
683
+
684
+ protected
685
+
686
+ def methods_to_test_for_equality
687
+ [
688
+ :name, :id, :utype, :type,
689
+ :description, :infos, :coordinate_systems, :params,
690
+ :links, :tables, :resources
691
+ ]
692
+ end
693
+ end
694
+
695
+ # A link or pointer to other documents or data servers on the Internet through a URI.
696
+ class Link < VORuby::VOTable::Base
697
+ ELEMENT_NAME = 'LINK'
698
+
699
+ # Create a new link.
700
+ # link = Link.new(
701
+ # :href => 'http://fits.gsfc.nasa.gov/nrao_data/samples/image/swp05569slg.fits',
702
+ # :content_type => 'image/fits'
703
+ # )
704
+ def initialize(defn=nil)
705
+ super(defn)
706
+ end
707
+
708
+ def id
709
+ self.node['ID']
710
+ end
711
+
712
+ def id=(i)
713
+ @node['ID'] = i.to_s
714
+ end
715
+
716
+ def content_role
717
+ self.node['content-role']
718
+ end
719
+
720
+ # Set the content role.
721
+ # May be one of: query, hints, doc or location.
722
+ def content_role=(c)
723
+ @node['content-role'] = c.to_s
724
+ end
725
+
726
+ def content_type
727
+ self.node['content-type']
728
+ end
729
+
730
+ # Set the content type (i.e. mimetype).
731
+ def content_type=(c)
732
+ @node['content-type'] = c.to_s
733
+ end
734
+
735
+ def title
736
+ self.node['title']
737
+ end
738
+
739
+ def title=(t)
740
+ @node['title'] = t.to_s
741
+ end
742
+
743
+ def value
744
+ self.node['value']
745
+ end
746
+
747
+ def value=(v)
748
+ @node['value'] = v.to_s
749
+ end
750
+
751
+ # Retrieve the URL.
752
+ # Returns a URI object.
753
+ def href
754
+ self.node['href'] ? URI.parse(self.node['href']) : nil
755
+ end
756
+
757
+ # Set the URL.
758
+ # link.href = 'http://www.noao.edu/' # or...
759
+ # link.href = URI.parse('http://www.noao.edu/')
760
+ def href=(h)
761
+ @node['href'] = h.to_s
762
+ end
763
+
764
+ # *DEPRECATED*.
765
+ def gref
766
+ self.node['gref']
767
+ end
768
+
769
+ # *DEPRECATED*.
770
+ def gref=(g)
771
+ @node['gref'] = g.to_s
772
+ end
773
+
774
+ # Retrieve the action.
775
+ # Returns a URI object.
776
+ def action
777
+ self.node['action'] ? URI.parse(self.node['action']) : nil
778
+ end
779
+
780
+ # Set the action.
781
+ # link.action = 'http://www.retrieve-me.com/' # or...
782
+ # link.action = URI.parse('http://www.retrieve-me.com/')
783
+ def action=(a)
784
+ @node['action'] = a.to_s
785
+ end
786
+
787
+ # Retrieve the link.
788
+ # Understands any URL groked by open-uri (including file://).
789
+ # Returns a File object.
790
+ def retrieve
791
+ shref = self.href
792
+
793
+ if shref
794
+ shref.scheme == 'file' ? File.open(shref.path) : open(shref.to_s)
795
+ else
796
+ nil
797
+ end
798
+ end
799
+
800
+ protected
801
+
802
+ def methods_to_test_for_equality
803
+ [
804
+ :id, :content_role, :content_type, :title, :value,
805
+ :href, :gref, :action
806
+ ]
807
+ end
808
+ end
809
+
810
+ # The basic structure of a votable. Essentially, tablular data +
811
+ # a description of the columns of that data.
812
+ class Table < VORuby::VOTable::Base
813
+ ELEMENT_NAME= 'TABLE'
814
+
815
+ def self.serialization_order
816
+ [
817
+ :description, :params, :fields, :groups, :links, :data,
818
+ :id, :name, :ucd, :utype, :ref, :nrows
819
+ ]
820
+ end
821
+
822
+ # Create a new table.
823
+ # table = Table.new(
824
+ # :name => 'results',
825
+ # :fields => [Field.new()],
826
+ # :data => Data.new(:format => TableData.new())
827
+ # )
828
+ def initialize(defn=nil)
829
+ super(defn)
830
+ end
831
+
832
+ def id
833
+ self.node['ID']
834
+ end
835
+
836
+ def id=(i)
837
+ @node['ID'] = i.to_s
838
+ end
839
+
840
+ def name
841
+ self.node['name']
842
+ end
843
+
844
+ def name=(n)
845
+ @node['name'] = n.to_s
846
+ end
847
+
848
+ def ref
849
+ self.node['ref']
850
+ end
851
+
852
+ def ref=(r)
853
+ @node['ref'] = r.to_s
854
+ end
855
+
856
+ def ucd
857
+ self.node['ucd']
858
+ end
859
+
860
+ # Set the unified content descriptor or UCD[http://vizier.u-strasbg.fr/doc/UCD.htx].
861
+ def ucd=(u)
862
+ @node['ucd'] = u.to_s
863
+ end
864
+
865
+ def utype
866
+ self.node['utype']
867
+ end
868
+
869
+ # Set the utype (i.e. the role of the column in the context of an external data model).
870
+ def utype=(u)
871
+ @node['utype'] = u.to_s
872
+ end
873
+
874
+ # Retrieve the number of rows in the table.
875
+ def nrows
876
+ self.node['nrows'] ? self.node['nrows'].to_i : nil
877
+ end
878
+
879
+ # Set the number of rows in the table.
880
+ # _n_ should be an integer.
881
+ def nrows=(n)
882
+ @node['nrows'] = n.to_s
883
+ end
884
+
885
+ # Retrieve the description (Description).
886
+ def description
887
+ get_element(Description)
888
+ end
889
+
890
+ # Set the description (Description).
891
+ def description=(d)
892
+ set_element(Description, d)
893
+ end
894
+
895
+ # Retrieve the data (Data).
896
+ def data
897
+ get_element(Data)
898
+ end
899
+
900
+ # Set the data (Data).
901
+ def data=(d)
902
+ set_element(Data, d)
903
+ end
904
+
905
+ # Retrieve the fields (Field), a.k.a. column descriptions.
906
+ # Returns a HomogeneousNodeList.
907
+ def fields
908
+ HomogeneousNodeList.new(self.node, xpath_for(Field), Field)
909
+ end
910
+
911
+ # Set the fields.
912
+ # table.fields = [Field.new(), ...]
913
+ def fields=(flds)
914
+ self.fields.replace(flds)
915
+ end
916
+
917
+ # Retrieve the parameters (Param).
918
+ # Returns a HomogeneousNodeList.
919
+ def params
920
+ HomogeneousNodeList.new(self.node, xpath_for(Param), Param)
921
+ end
922
+
923
+ # Set the parameters list (Param).
924
+ # table.params = [Param.new(), ...]
925
+ def params=(parameters)
926
+ self.params.replace(parameters)
927
+ end
928
+
929
+ # Retrieve the groups (Group).
930
+ # Returns a HomogeneousNodeList.
931
+ def groups
932
+ HomogeneousNodeList.new(self.node, xpath_for(Group), Group)
933
+ end
934
+
935
+ # Set the groups (Group).
936
+ # table.groups = [Group.new(), ...]
937
+ def groups=(grps)
938
+ self.groups.replace(grps)
939
+ end
940
+
941
+ # Retrieve the links (Link).
942
+ # Returns a HomogeneousNodeList.
943
+ def links
944
+ HomogeneousNodeList.new(self.node, xpath_for(Link), Link)
945
+ end
946
+
947
+ # Set the links (Link).
948
+ # table.links = [Link.new(), ...]
949
+ def links=(lnks)
950
+ self.links.replace(lnks)
951
+ end
952
+
953
+ # Convert the table into HTML.
954
+ # Not all aspects of the votable are translated.
955
+ # table = Table.new(
956
+ # :id => 'table1',
957
+ # :description => Description.new(:text => 'A test table'),
958
+ # :fields => [
959
+ # Field.new(:name => 'RA', :id => 'col1', :ucd => 'pos.eq.ra;meta.main',
960
+ # :ref => 'J2000', :datatype => 'float', :width => 6, :precision => '2', :unit => 'deg'),
961
+ # Field.new(:name => 'Dec', :id => 'col2', :ucd => 'pos.eq.dec;meta.main',
962
+ # :ref => 'J2000', :datatype => 'float', :width => 6, :precision => '2', :unit => 'deg'),
963
+ # Field.new(:name => 'Name', :id => 'col3', :ucd => 'meta.id;meta.main',
964
+ # :datatype => 'char', :arraysize => '8*')
965
+ # ],
966
+ # :data => Data.new(
967
+ # :format => TableData.new(
968
+ # :trs => [
969
+ # Tr.new(:tds => [Td.new(:text => '123'), Td.new(:text => '456'), Td.new(:text => 'my_obj1')]),
970
+ # Tr.new(:tds => [Td.new(:text => '789'), Td.new(:text => '112'), Td.new(:text => 'my_obj2')]),
971
+ # Tr.new(:tds => [Td.new(:text => '145'), Td.new(:text => '178'), Td.new(:text => 'my_obj3')])
972
+ # ]
973
+ # )
974
+ # )
975
+ # )
976
+ #
977
+ # puts table.to_html # =>
978
+ # <table id="table1">
979
+ # <caption>A test table</caption>
980
+ # <thead>
981
+ # <tr>
982
+ # <th>
983
+ # <div class="field" id="col1">
984
+ # <div class="name">RA</div>
985
+ # <div class="ucd">pos.eq.ra;meta.main</div>
986
+ # <div class="unit">deg</div>
987
+ # <span class="datatype">float</span>
988
+ # </div>
989
+ # </th>
990
+ # <th>
991
+ # <div class="field" id="col2">
992
+ # <div class="name">Dec</div>
993
+ # <div class="ucd">pos.eq.dec;meta.main</div>
994
+ # <div class="unit">deg</div>
995
+ # <span class="datatype">float</span>
996
+ # </div>
997
+ # </th>
998
+ # <th>
999
+ # <div class="field" id="col3">
1000
+ # <div class="name">Name</div>
1001
+ # <div class="ucd">meta.id;meta.main</div>
1002
+ # <span class="datatype">char</span>
1003
+ # <span class="arraysize">8*</span>
1004
+ # </div>
1005
+ # </th>
1006
+ # </tr>
1007
+ # </thead>
1008
+ # <tbody>
1009
+ # <tr>
1010
+ # <td>123</td>
1011
+ # <td>456</td>
1012
+ # <td>my_obj1</td>
1013
+ # </tr>
1014
+ # <tr>
1015
+ # <td>789</td>
1016
+ # <td>112</td>
1017
+ # <td>my_obj2</td>
1018
+ # </tr>
1019
+ # <tr>
1020
+ # <td>145</td>
1021
+ # <td>178</td>
1022
+ # <td>my_obj3</td>
1023
+ # </tr>
1024
+ # </tbody>
1025
+ # </table
1026
+ def to_html
1027
+ builder = Builder::XmlMarkup.new(:indent => 2, :margin => 2)
1028
+
1029
+ tbl_options = {}
1030
+ tbl_options[:id] = self.id if self.id
1031
+
1032
+ builder.table(tbl_options) { |table|
1033
+ table.caption(self.description.text) if self.description
1034
+
1035
+ table.thead { |thead|
1036
+ if self.fields.size > 0
1037
+ thead.tr { |tr|
1038
+ self.fields.each do |field|
1039
+ tr.th { |th|
1040
+ th << field.to_html
1041
+ }
1042
+ end
1043
+ }
1044
+ end
1045
+ }
1046
+
1047
+ if self.data and self.data.format
1048
+ table.tbody { |tbody|
1049
+ self.data.format.trs.each do |tr|
1050
+ tbody << tr.to_html
1051
+ end
1052
+ }
1053
+ end
1054
+ }
1055
+ end
1056
+
1057
+ protected
1058
+
1059
+ def methods_to_test_for_equality
1060
+ [
1061
+ :id, :name, :ref, :ucd, :utype, :nrows,
1062
+ :description, :data, :fields, :params, :groups,
1063
+ :links
1064
+ ]
1065
+ end
1066
+ end
1067
+
1068
+ # The description of an actual table column.
1069
+ class Field < VORuby::VOTable::Base
1070
+ ELEMENT_NAME = 'FIELD'
1071
+
1072
+ def self.serialization_order
1073
+ [
1074
+ :description, :values, :links,
1075
+ :id, :unit, :datatype, :precision, :width, :ref,
1076
+ :name, :ucd, :utype, :arraysize, :type
1077
+ ]
1078
+ end
1079
+
1080
+ # Create a new table column description.
1081
+ # field = Field.new(
1082
+ # :name => 'RA',
1083
+ # :id => 'col1',
1084
+ # :ucd => 'pos.eq.ra;meta.main',
1085
+ # :ref => 'J2000',
1086
+ # :datatype => 'float',
1087
+ # :width => 6,
1088
+ # :precision => '2',
1089
+ # :unit => 'deg'
1090
+ # )
1091
+ def initialize(defn=nil)
1092
+ super(defn)
1093
+ end
1094
+
1095
+ def id
1096
+ self.node['ID']
1097
+ end
1098
+
1099
+ def id=(i)
1100
+ @node['ID'] = i.to_s
1101
+ end
1102
+
1103
+ def unit
1104
+ self.node['unit']
1105
+ end
1106
+
1107
+ # Set the unit.
1108
+ # Units should be of the form outlined by Vizier[http://vizier.u-strasbg.fr/doc/catstd-3.2.htx].
1109
+ def unit=(u)
1110
+ @node['unit'] = u.to_s
1111
+ end
1112
+
1113
+ def datatype
1114
+ self.node['datatype']
1115
+ end
1116
+
1117
+ # Set the datatype.
1118
+ # Should be one of: boolean, bit, unsignedByte, short, int, long,
1119
+ # char, unicodeChar, float, double, floatComplex, doubleComplex.
1120
+ def datatype=(d)
1121
+ @node['datatype'] = d.to_s
1122
+ end
1123
+
1124
+ def precision
1125
+ self.node['precision']
1126
+ end
1127
+
1128
+ # Set the precision of the value.
1129
+ # Should match [EF]?[1-9][0-9]*
1130
+ # param.precision = '1'
1131
+ def precision=(p)
1132
+ @node['precision'] = p.to_s
1133
+ end
1134
+
1135
+ def width
1136
+ self.node['width'] ? self.node['width'].to_i : nil
1137
+ end
1138
+
1139
+ # Set the width.
1140
+ # Should be an integer.
1141
+ # param.width = 2
1142
+ def width=(w)
1143
+ @node['width'] = w.to_s
1144
+ end
1145
+
1146
+ def ref
1147
+ self.node['ref']
1148
+ end
1149
+
1150
+ def ref=(r)
1151
+ @node['ref'] = r.to_s
1152
+ end
1153
+
1154
+ def name
1155
+ self.node['name']
1156
+ end
1157
+
1158
+ def name=(n)
1159
+ @node['name'] = n.to_s
1160
+ end
1161
+
1162
+ def ucd
1163
+ self.node['ucd']
1164
+ end
1165
+
1166
+ # Set the unified content descriptor or UCD[http://vizier.u-strasbg.fr/doc/UCD.htx].
1167
+ def ucd=(u)
1168
+ @node['ucd'] = u.to_s
1169
+ end
1170
+
1171
+ def utype
1172
+ self.node['utype']
1173
+ end
1174
+
1175
+ # Set the utype (i.e. the role of the column in the context of an external data model).
1176
+ def utype=(u)
1177
+ @node['utype'] = u.to_s
1178
+ end
1179
+
1180
+ def arraysize
1181
+ self.node['arraysize']
1182
+ end
1183
+
1184
+ # Set the arraysize (if applicable).
1185
+ # i.e. *, 8x2
1186
+ def arraysize=(a)
1187
+ @node['arraysize'] = a.to_s
1188
+ end
1189
+
1190
+ # *DEPRECATED*
1191
+ def type
1192
+ self.node['type']
1193
+ end
1194
+
1195
+ # May be on of: hidden, no_query, trigger or location.
1196
+ # *DEPRECATED*
1197
+ def type=(t)
1198
+ @node['type'] = t.to_s
1199
+ end
1200
+
1201
+ # Retrieve the description (Description).
1202
+ def description
1203
+ get_element(Description)
1204
+ end
1205
+
1206
+ # Set the description (Description).
1207
+ def description=(d)
1208
+ set_element(Description, d)
1209
+ end
1210
+
1211
+ # Retrieve the values (Values).
1212
+ def values
1213
+ HomogeneousNodeList.new(self.node, xpath_for(Values), Values)
1214
+ end
1215
+
1216
+ # Set the values (Values).
1217
+ def values=(vals)
1218
+ self.values.replace(vals)
1219
+ end
1220
+
1221
+ # Retrieve the links.
1222
+ # Returns a HomogeneousNodeList object.
1223
+ def links
1224
+ HomogeneousNodeList.new(self.node, xpath_for(Link), Link)
1225
+ end
1226
+
1227
+ # Set the links.
1228
+ # Takes an array of Link objects.
1229
+ def links=(lnks)
1230
+ self.links.replace(lnks)
1231
+ end
1232
+
1233
+ # Convert a field to HTML.
1234
+ def to_html
1235
+ builder = Builder::XmlMarkup.new(:indent => 2, :margin => 6)
1236
+
1237
+ field_opts = {:class => 'field'}
1238
+ field_opts[:id] = self.id if self.id
1239
+
1240
+ builder.div(field_opts) { |field|
1241
+ field.div(self.name, :class => 'name')
1242
+ field.div(self.ucd, :class => 'ucd') if self.ucd
1243
+ field.div(self.utype, :class => 'utype') if self.utype
1244
+ field.div(self.unit, :class => 'unit') if self.unit
1245
+ field.span(self.datatype, :class => 'datatype') if self.datatype
1246
+ field.span(self.arraysize, :class => 'arraysize') if self.arraysize
1247
+ }
1248
+ end
1249
+
1250
+ protected
1251
+
1252
+ def methods_to_test_for_equality
1253
+ [
1254
+ :id, :unit, :datatype, :precision, :width, :ref,
1255
+ :name, :ucd, :utype, :arraysize, :type,
1256
+ :description, :values, :links
1257
+ ]
1258
+ end
1259
+ end
1260
+
1261
+ # Holds subsidiary information about the domain of the data.
1262
+ class Values < VORuby::VOTable::Base
1263
+ ELEMENT_NAME = 'VALUES'
1264
+
1265
+ def self.serialization_order
1266
+ [
1267
+ :min, :max, :options,
1268
+ :id, :type, :null, :ref
1269
+ ]
1270
+ end
1271
+
1272
+ # Create some new subsidiary information about the domain.
1273
+ # values = Values.new(
1274
+ # :id => 'RAdomain',
1275
+ # :min => Min.new(:value => '0'),
1276
+ # :max => Max.new(:value => '360', :inclusive => false)
1277
+ # )
1278
+ def initialize(defn=nil)
1279
+ super(defn)
1280
+ self.type = 'legal' if !self.type
1281
+ end
1282
+
1283
+ def id
1284
+ self.node['ID']
1285
+ end
1286
+
1287
+ def id=(i)
1288
+ @node['ID'] = i.to_s
1289
+ end
1290
+
1291
+ def type
1292
+ self.node['type']
1293
+ end
1294
+
1295
+ # Set the scope of values present.
1296
+ # Possible values are: legal, actual.
1297
+ # Defaults to legal.
1298
+ def type=(t)
1299
+ @node['type'] = t.to_s
1300
+ end
1301
+
1302
+ def null
1303
+ self.node['null']
1304
+ end
1305
+
1306
+ # Set the value that is used to specify
1307
+ # non-existent data.
1308
+ def null=(n)
1309
+ @node['null'] = n.to_s
1310
+ end
1311
+
1312
+ def ref
1313
+ self.node['ref']
1314
+ end
1315
+
1316
+ def ref=(r)
1317
+ @node['ref'] = r.to_s
1318
+ end
1319
+
1320
+ # Retrieve the maximum of the value (Max).
1321
+ def max
1322
+ get_element(Max)
1323
+ end
1324
+
1325
+ # Set the maximum of the value (Max).
1326
+ def max=(m)
1327
+ set_element(Max, m)
1328
+ end
1329
+
1330
+ # Retrieve the minimum of the value (Min).
1331
+ def min
1332
+ get_element(Min)
1333
+ end
1334
+
1335
+ # Set the minimum of the value (Min).
1336
+ def min=(m)
1337
+ set_element(Min, m)
1338
+ end
1339
+
1340
+ # Retrieve the possible values (Option) the column can take on.
1341
+ # Returns a HomogeneousNodeList object.
1342
+ def options
1343
+ HomogeneousNodeList.new(self.node, xpath_for(Option), Option)
1344
+ end
1345
+
1346
+ # Set the possible values the column can take on.
1347
+ # values.options = [Option.new(), ...]
1348
+ def options=(opts)
1349
+ self.options.replace(opts)
1350
+ end
1351
+
1352
+ protected
1353
+
1354
+ def methods_to_test_for_equality
1355
+ [
1356
+ :id, :type, :null, :ref,
1357
+ :max, :min, :options
1358
+ ]
1359
+ end
1360
+ end
1361
+
1362
+ # A minimum value a column can take on.
1363
+ class Min < VORuby::VOTable::Base
1364
+ ELEMENT_NAME = 'MIN'
1365
+
1366
+ # Create a new minimum value for a column.
1367
+ # min = Min.new(:value => '0')
1368
+ def initialize(defn=nil)
1369
+ super(defn)
1370
+ self.inclusive = true if !self.inclusive
1371
+ end
1372
+
1373
+ def value
1374
+ self.node['value']
1375
+ end
1376
+
1377
+ def value=(v)
1378
+ @node['value'] = v.to_s
1379
+ end
1380
+
1381
+ def inclusive
1382
+ self.node['inclusive']
1383
+ end
1384
+
1385
+ # Check whether the minimum value is inclusive or not.
1386
+ # By default, the minimum is inclusive.
1387
+ def inclusive?
1388
+ self.node['inclusive'] == 'yes'
1389
+ end
1390
+
1391
+ # Set whether the minimum value is inclusive or not.
1392
+ # Takes a boolean value or a yes/no string.
1393
+ # min.inclusive = false # or...
1394
+ # min.inclusive = 'no'
1395
+ def inclusive=(i)
1396
+ if i.is_a?(TrueClass)
1397
+ @node['inclusive'] = 'yes'
1398
+ elsif i.is_a?(FalseClass)
1399
+ @node['inclusive'] = 'no'
1400
+ else
1401
+ @node['inclusive'] = i.to_s
1402
+ end
1403
+ end
1404
+
1405
+ protected
1406
+
1407
+ def methods_to_test_for_equality
1408
+ [:value, :inclusive]
1409
+ end
1410
+ end
1411
+
1412
+ # The maximum value a column may take on.
1413
+ class Max < VORuby::VOTable::Base
1414
+ ELEMENT_NAME = 'MAX'
1415
+
1416
+ # Create a new maximum value for a column.
1417
+ # max = Max.new(:value => '360', :inclusive => false)
1418
+ def initialize(defn=nil)
1419
+ super(defn)
1420
+ self.inclusive = true if !self.inclusive
1421
+ end
1422
+
1423
+ def value
1424
+ self.node['value']
1425
+ end
1426
+
1427
+ def value=(v)
1428
+ @node['value'] = v.to_s
1429
+ end
1430
+
1431
+ def inclusive
1432
+ self.node['inclusive']
1433
+ end
1434
+
1435
+ # Check whether the maximum value is inclusive or not.
1436
+ # By default, the maximum is inclusive.
1437
+ def inclusive?
1438
+ self.node['inclusive'] == 'yes'
1439
+ end
1440
+
1441
+ # Set whether the maximum value is inclusive or not.
1442
+ # Takes a boolean value or a yes/no string.
1443
+ # max.inclusive = false # or...
1444
+ # max.inclusive = 'no'
1445
+ def inclusive=(i)
1446
+ if i.is_a?(TrueClass)
1447
+ @node['inclusive'] = 'yes'
1448
+ elsif i.is_a?(FalseClass)
1449
+ @node['inclusive'] = 'no'
1450
+ else
1451
+ @node['inclusive'] = i.to_s
1452
+ end
1453
+ end
1454
+
1455
+ protected
1456
+
1457
+ def methods_to_test_for_equality
1458
+ [:value, :inclusive]
1459
+ end
1460
+ end
1461
+
1462
+ # An enumeration of possible values.
1463
+ # Essentially a hierarchy of key-value pairs.
1464
+ class Option < VORuby::VOTable::Base
1465
+ ELEMENT_NAME = 'OPTION'
1466
+
1467
+ # Create a new enumeration.
1468
+ # option = Option.new(
1469
+ # :name => 'authors',
1470
+ # :options => [
1471
+ # Option.new(:name => 'last_name', :value => 'Gasson'),
1472
+ # Option.new(:name => 'last_name', :value => 'Miller')
1473
+ # ]
1474
+ # )
1475
+ def initialize(defn=nil)
1476
+ super(defn)
1477
+ end
1478
+
1479
+ def name
1480
+ self.node['name']
1481
+ end
1482
+
1483
+ def name=(n)
1484
+ @node['name'] = n.to_s
1485
+ end
1486
+
1487
+ def value
1488
+ self.node['value']
1489
+ end
1490
+
1491
+ def value=(v)
1492
+ @node['value'] = v.to_s
1493
+ end
1494
+
1495
+ # Retrieve any sub-options (Option).
1496
+ # Returns a HomogeneousNodeList.
1497
+ def options
1498
+ HomogeneousNodeList.new(self.node, xpath_for(Option), Option)
1499
+ end
1500
+
1501
+ # Set any sub-options.
1502
+ # option.options = [Option.new(), ...]
1503
+ def options=(opts)
1504
+ self.options.replace(opts)
1505
+ end
1506
+
1507
+ protected
1508
+
1509
+ def methods_to_test_for_equality
1510
+ [:name, :value, :options]
1511
+ end
1512
+ end
1513
+
1514
+ # A way of grouping fields which are logically connected, like
1515
+ # a value and its error.
1516
+ class Group < VORuby::VOTable::Base
1517
+ ELEMENT_NAME = 'GROUP'
1518
+
1519
+ def self.serialization_order
1520
+ [
1521
+ :description, :field_refs, :params, :param_refs, :groups,
1522
+ :id, :name, :ref, :ucd, :utype
1523
+ ]
1524
+ end
1525
+
1526
+ # Create a new group.
1527
+ # group = Group.new(
1528
+ # :name => 'Velocity',
1529
+ # :description => Description.new(:text => 'Velocity and its error')
1530
+ # :field_refs => [FieldRef.new(:ref => 'col4'), FieldRef.new(:ref => 'col5')]
1531
+ # )
1532
+ def initialize(defn=nil)
1533
+ super(defn)
1534
+ end
1535
+
1536
+ def id
1537
+ self.node['ID']
1538
+ end
1539
+
1540
+ def id=(i)
1541
+ @node['ID'] = i.to_s
1542
+ end
1543
+
1544
+ def name
1545
+ self.node['name']
1546
+ end
1547
+
1548
+ def name=(n)
1549
+ @node['name'] = n.to_s
1550
+ end
1551
+
1552
+ def ref
1553
+ self.node['ref']
1554
+ end
1555
+
1556
+ def ref=(r)
1557
+ @node['ref'] = r.to_s
1558
+ end
1559
+
1560
+ def ucd
1561
+ self.node['ucd']
1562
+ end
1563
+
1564
+ # Set the unified content descriptor or UCD[http://vizier.u-strasbg.fr/doc/UCD.htx].
1565
+ def ucd=(u)
1566
+ @node['ucd'] = u.to_s
1567
+ end
1568
+
1569
+ def utype
1570
+ self.node['utype']
1571
+ end
1572
+
1573
+ # Set the utype (i.e. the role of the column in the context of an external data model).
1574
+ def utype=(u)
1575
+ @node['utype'] = u.to_s
1576
+ end
1577
+
1578
+ # Retrieve the description (Description).
1579
+ def description
1580
+ get_element(Description)
1581
+ end
1582
+
1583
+ # Set the description (Description).
1584
+ def description=(d)
1585
+ set_element(Description, d)
1586
+ end
1587
+
1588
+ # Retrieve the references to fields.
1589
+ # Returns a HomogeneousNodeList.
1590
+ def field_refs
1591
+ HomogeneousNodeList.new(self.node, xpath_for(FieldRef), FieldRef)
1592
+ end
1593
+
1594
+ # Set the references to fields.
1595
+ # group.field_refs = [FieldRef.new(), ...]
1596
+ def field_refs=(refs)
1597
+ self.field_refs.replace(refs)
1598
+ end
1599
+
1600
+ # Retrieve the references to parameters.
1601
+ # Returns a HomogeneousNodeList.
1602
+ def param_refs
1603
+ HomogeneousNodeList.new(self.node, xpath_for(ParamRef), ParamRef)
1604
+ end
1605
+
1606
+ # Set the the references to parameters.
1607
+ # group.param_refs = [ParamRef.new(), ...]
1608
+ def param_refs=(refs)
1609
+ self.param_refs.replace(refs)
1610
+ end
1611
+
1612
+ # Retrieve the list of parameters.
1613
+ # Normally one would use #param_refs instead.
1614
+ # Returns a HomogeneousNodeList.
1615
+ def params
1616
+ HomogeneousNodeList.new(self.node, xpath_for(Param), Param)
1617
+ end
1618
+
1619
+ # Set the list parameters.
1620
+ # Normally one would use #param_refs= instead.
1621
+ # group.params = [Param.new(), ...]
1622
+ def params=(parameters)
1623
+ self.params.replace(parameters)
1624
+ end
1625
+
1626
+ # Retrieve any sub-groups.
1627
+ # Returns a HomogeneousNodeList.
1628
+ def groups
1629
+ HomogeneousNodeList.new(self.node, xpath_for(Group), Group)
1630
+ end
1631
+
1632
+ # Set any sub-groups.
1633
+ # group.groups = [Group.new(), ...]
1634
+ def groups=(grps)
1635
+ self.groups.replace(grps)
1636
+ end
1637
+
1638
+ protected
1639
+
1640
+ def methods_to_test_for_equality
1641
+ [
1642
+ :id, :name, :ref, :ucd, :utype,
1643
+ :description, :field_refs, :param_refs,
1644
+ :params, :groups
1645
+ ]
1646
+ end
1647
+ end
1648
+
1649
+ # A reference to an existing Field.
1650
+ class FieldRef < VORuby::VOTable::Base
1651
+ ELEMENT_NAME = 'FIELDref'
1652
+
1653
+ # Create a new field reference.
1654
+ # fref = FieldRef.new(:ref => 'col3')
1655
+ def initialize(defn=nil)
1656
+ super(defn)
1657
+ end
1658
+
1659
+ def ref
1660
+ self.node['ref']
1661
+ end
1662
+
1663
+ def ref=(r)
1664
+ @node['ref'] = r.to_s
1665
+ end
1666
+
1667
+ # Retrieve the field (Field) associated with this reference.
1668
+ def field
1669
+ field_node = self.node.find_first("ancestor::*/FIELD[@ID='#{self.ref}']")
1670
+ field_node ? Field.new(field_node) : nil
1671
+ end
1672
+
1673
+ protected
1674
+
1675
+ def methods_to_test_for_equality; [:ref] end
1676
+ end
1677
+
1678
+ # A reference to an existing Param.
1679
+ class ParamRef < VORuby::VOTable::Base
1680
+ ELEMENT_NAME = 'PARAMref'
1681
+
1682
+ # Create an new parameter reference.
1683
+ # pref = ParamRef.new(:ref => 'param1')
1684
+ def initialize(defn=nil)
1685
+ super(defn)
1686
+ end
1687
+
1688
+ def ref
1689
+ self.node['ref']
1690
+ end
1691
+
1692
+ def ref=(r)
1693
+ @node['ref'] = r.to_s
1694
+ end
1695
+
1696
+ # Retrieve the parameter (Param) associated with the reference.
1697
+ def param
1698
+ param_node = self.node.find_first("ancestor::*/PARAM[@ID='#{self.ref}']")
1699
+ param_node ? Param.new(param_node) : nil
1700
+ end
1701
+
1702
+ protected
1703
+
1704
+ def methods_to_test_for_equality; [:ref] end
1705
+ end
1706
+
1707
+ # The actual tabular data.
1708
+ class Data < VORuby::VOTable::Base
1709
+ ELEMENT_NAME = 'DATA'
1710
+
1711
+ # Create a new body of data.
1712
+ # data = Data.new(:format => TableData.new())
1713
+ def initialize(defn=nil)
1714
+ super(defn)
1715
+ end
1716
+
1717
+ # Retrieve the formatted data. May be TableData, Binary or Fits.
1718
+ def format
1719
+ format_node = self.node.find_first("#{xpath_for(TableData)}|#{xpath_for(Binary)}|#{xpath_for(Fits)}")
1720
+ return nil if !format_node
1721
+
1722
+ case format_node.name
1723
+ when 'TABLEDATA' then TableData.new(format_node)
1724
+ when 'BINARY' then Binary.new(format_node)
1725
+ when 'FITS' then Fits.new(format_node)
1726
+ else nil
1727
+ end
1728
+ end
1729
+
1730
+ # Set the formatted data. Must be one of the allowed
1731
+ # data formats--TableData, Binary or Fits.
1732
+ def format=(f)
1733
+ raise 'Data format must be one of TableData, Binary or Fits' if ![TableData, Binary, Fits].include?(f.class)
1734
+
1735
+ self.node.each_child do |c|
1736
+ c.remove!
1737
+ end
1738
+ set_element(f.class, f)
1739
+ end
1740
+
1741
+ protected
1742
+
1743
+ def methods_to_test_for_equality; [:format] end
1744
+ end
1745
+
1746
+ # The most typical way of representing tabular data.
1747
+ class TableData < VORuby::VOTable::Base
1748
+ ELEMENT_NAME = 'TABLEDATA'
1749
+
1750
+ # Create new table data.
1751
+ # tabledata = TableData.new(:trs => [Tr.new, Tr.new, Tr.new])
1752
+ def initialize(defn=nil)
1753
+ super(defn)
1754
+ end
1755
+
1756
+ # Retrieve the rows of data.
1757
+ # Returns a HomogeneousNodeList.
1758
+ def trs
1759
+ HomogeneousNodeList.new(self.node, xpath_for(Tr), Tr)
1760
+ end
1761
+
1762
+ # Set the rows of data.
1763
+ # tabledata.rows = [Tr.new(), ...]
1764
+ def trs=(rows)
1765
+ self.trs.replace(rows)
1766
+ end
1767
+
1768
+ protected
1769
+
1770
+ def methods_to_test_for_equality; [:trs] end
1771
+ end
1772
+
1773
+ # Tabular data represented as a binary format.
1774
+ class Binary < VORuby::VOTable::Base
1775
+ ELEMENT_NAME = 'BINARY'
1776
+
1777
+ # Create a new binary table.
1778
+ # binary = Binary.new(:stream => Stream.new())
1779
+ def initialize(defn=nil)
1780
+ super(defn)
1781
+ end
1782
+
1783
+ # Retrieve the stream (Stream) representing the data.
1784
+ def stream
1785
+ get_element(Stream)
1786
+ end
1787
+
1788
+ # Set the stream representing the data.
1789
+ # binary.stream = Stream.new()
1790
+ def stream=(s)
1791
+ set_element(Stream, s)
1792
+ end
1793
+
1794
+ protected
1795
+
1796
+ def methods_to_test_for_equality; [:stream] end
1797
+ end
1798
+
1799
+ # Represents a stream of bytes, either local or remote.
1800
+ class Stream < VORuby::VOTable::Base
1801
+ ELEMENT_NAME = 'STREAM'
1802
+
1803
+ # Create a new retrieval stream.
1804
+ # stream = Stream.new(:href => 'ftp://server.com/mydata.dat')
1805
+ def initialize(defn=nil)
1806
+ super(defn)
1807
+ self.type = 'locator' if !self.type
1808
+ self.actuate = 'onRequest' if !self.actuate
1809
+ self.encoding = 'none' if !self.encoding
1810
+ end
1811
+
1812
+ def type
1813
+ self.node['type']
1814
+ end
1815
+
1816
+ # Set the type of stream. May be one of: locator or other.
1817
+ # Default is locator.
1818
+ def type=(t)
1819
+ @node['type'] = t.to_s
1820
+ end
1821
+
1822
+ # Retrieve the URL of the stream.
1823
+ # Returns a URI object.
1824
+ def href
1825
+ self.node['href'] ? URI.parse(self.node['href']) : nil
1826
+ end
1827
+
1828
+ # Set the URL of the stream.
1829
+ # link.href = 'http://www.noao.edu/my.dat' # or...
1830
+ # link.href = URI.parse('http://www.noao.edu/my.dat')
1831
+ def href=(h)
1832
+ @node['href'] = h.to_s
1833
+ end
1834
+
1835
+ def actuate
1836
+ self.node['actuate']
1837
+ end
1838
+
1839
+ # Set when the stream should be retrieved.
1840
+ # Possible values are: onLoad, onRequest, other or none.
1841
+ # Default is onRequest. In this implementation the value of this
1842
+ # parameter doesn't effect when the stream is retrieved--
1843
+ # you must always use #retrieve.
1844
+ def actuate=(a)
1845
+ @node['actuate'] = a.to_s
1846
+ end
1847
+
1848
+ def encoding
1849
+ self.node['encoding']
1850
+ end
1851
+
1852
+ # Set the encoding of the stream (i.e. base64).
1853
+ # Default is none.
1854
+ def encoding=(e)
1855
+ @node['encoding'] = e.to_s
1856
+ end
1857
+
1858
+ # Retrieve when the data in the stream expires.
1859
+ # Returns a DateTime object.
1860
+ def expires
1861
+ self.node['expires'] ? DateTime.parse(self.node['expires']) : nil
1862
+ end
1863
+
1864
+ # Set when the data in the stream expires.
1865
+ def expires=(e)
1866
+ @node['expires'] = e.to_s
1867
+ end
1868
+
1869
+ def rights
1870
+ self.node['rights']
1871
+ end
1872
+
1873
+ # Set any authentication information necessary to access
1874
+ # the remote resource (i.e. a password to an encrypted document).
1875
+ # The current implementation ignores this value when retrieving
1876
+ # the stream.
1877
+ def rights=(r)
1878
+ @node['rights'] = r.to_s
1879
+ end
1880
+
1881
+ # If the stream is text-based, it can be directly embedded
1882
+ # within the document, and can be retrieved with this method.
1883
+ def text
1884
+ self.node['content']
1885
+ end
1886
+
1887
+ # If the stream is text-based it can be directly embedded with
1888
+ # this method.
1889
+ # stream.text = 'AAAAAj/yVZiDGSSUwFZ6ypR4yGkADwAcQV0euAAIAAJBmMzNwZWZmkGle4tBR3jVQT9ocwAA'
1890
+ def text=(txt)
1891
+ @node['content'] = txt.to_s
1892
+ end
1893
+
1894
+ # Retrieve the document specified by #href.
1895
+ # Returns a File object, of if the document is
1896
+ # directly embedded in the stream a StringIO object.
1897
+ def retrieve
1898
+ shref = self.href
1899
+
1900
+ if shref
1901
+ shref.scheme == 'file' ? File.open(shref.path) : open(shref.to_s)
1902
+ else
1903
+ StringIO.new(self.text)
1904
+ end
1905
+ end
1906
+
1907
+ protected
1908
+
1909
+ def methods_to_test_for_equality
1910
+ [:type, :href, :actuate, :encoding, :expires, :rights, :text]
1911
+ end
1912
+ end
1913
+
1914
+ # Tabular data represented as a FITS[http://archive.stsci.edu/fits/fits_standard/] file.
1915
+ class Fits < VORuby::VOTable::Base
1916
+ ELEMENT_NAME = 'FITS'
1917
+
1918
+ # Create a new FITS data stream.
1919
+ # fits = Fits.new(
1920
+ # :extnum => 2,
1921
+ # :stream => Stream.new(:href => http://fits.gsfc.nasa.gov/nrao_data/samples/image/swp05569slg.fits')
1922
+ # )
1923
+ def initialize(defn=nil)
1924
+ super(defn)
1925
+ end
1926
+
1927
+ # Retrieve the FITS extension number.
1928
+ # Returns an integer.
1929
+ def extnum
1930
+ self.node['extnum'] ? self.node['extnum'].to_i : nil
1931
+ end
1932
+
1933
+ # Set the FITS extension number.
1934
+ def extnum=(e)
1935
+ @node['extnum'] = e.to_s
1936
+ end
1937
+
1938
+ # Retrieve the stream (Stream) associated with the FITS file.
1939
+ def stream
1940
+ get_element(Stream)
1941
+ end
1942
+
1943
+ # Set the stream (Stream) associated with the FITS file.
1944
+ # fits.stream = Stream.new(:href => 'http://whatever/my_fits.fits')
1945
+ def stream=(s)
1946
+ set_element(Stream, s)
1947
+ end
1948
+
1949
+ # Retrieves the FITS file locally and returns it.
1950
+ # If rfits[http://rubyforge.org/projects/rfits/] is present
1951
+ # a RFits::File object is returned (or a RFits::HDU subclass if #extnum is specified).
1952
+ # If rfits is _not_ present a File object is returned instead.
1953
+ def to_rfits
1954
+ io = self.stream.retrieve
1955
+
1956
+ io_obj = if io.is_a?(StringIO)
1957
+ tmp = Tempfile.new('fits')
1958
+ tmp.write(io.read)
1959
+ tmp
1960
+ else
1961
+ io
1962
+ end
1963
+
1964
+ # RFits isn't absolutely required, but it's nice to have.
1965
+ return io_obj if !VORuby.rfits?
1966
+
1967
+ rfits = RFits::File.new(io_obj.path)
1968
+ self.extnum ? rfits[self.extnum - 1] : rfits
1969
+ end
1970
+
1971
+ protected
1972
+
1973
+ def methods_to_test_for_equality
1974
+ [:extnum, :stream]
1975
+ end
1976
+ end
1977
+
1978
+ # A row of data in a TableData.
1979
+ class Tr < VORuby::VOTable::Base
1980
+ ELEMENT_NAME = 'TR'
1981
+
1982
+ # Create a new row of data.
1983
+ # tr = Tr.new(:tds => [Td.new()])
1984
+ def initialize(defn=nil)
1985
+ super(defn)
1986
+ end
1987
+
1988
+ # Retrieve the table data elements in the row.
1989
+ # Returns a HomogeneousNodeList.
1990
+ def tds
1991
+ HomogeneousNodeList.new(self.node, xpath_for(Td), Td)
1992
+ end
1993
+
1994
+ # Set the data elements of the row.
1995
+ # tr.tds = [Td.new(), ...]
1996
+ def tds=(columns)
1997
+ self.tds.replace(columns)
1998
+ end
1999
+
2000
+ # Convert a table row to HTML.
2001
+ # tr = Tr.new(:tds => [Td.new(:text => 'hello'), Td.new(:text => 'world')])
2002
+ # puts tr.to_html # =>
2003
+ # <tr>
2004
+ # <td>hello</td>
2005
+ # <td>world</td>
2006
+ # </tr>
2007
+ def to_html
2008
+ builder = Builder::XmlMarkup.new(:indent => 2, :margin => 4)
2009
+
2010
+ builder.tr { |tr|
2011
+ self.tds.each do |column|
2012
+ tr << column.to_html
2013
+ end
2014
+ }
2015
+ end
2016
+
2017
+ protected
2018
+
2019
+ def methods_to_test_for_equality; [:tds] end
2020
+ end
2021
+
2022
+ # A data element in a table row.
2023
+ class Td < VORuby::VOTable::Base
2024
+ include Castable
2025
+
2026
+ ELEMENT_NAME = 'TD'
2027
+
2028
+ # Create a new data element.
2029
+ # td = Td.new(:text => '123.2')
2030
+ def initialize(defn=nil)
2031
+ super(defn)
2032
+ end
2033
+
2034
+ def encoding
2035
+ self.node['encoding']
2036
+ end
2037
+
2038
+ def encoding=(e)
2039
+ @node['encoding'] = e.to_s
2040
+ end
2041
+
2042
+ # Retrieve the value of the data element as text.
2043
+ def text
2044
+ self.node.content
2045
+ end
2046
+
2047
+ # Set the value of the data element as text.
2048
+ def text=(txt)
2049
+ @node.content = txt.to_s
2050
+ end
2051
+
2052
+ # Retrieve the value of the data element as an appropriate
2053
+ # object (or list of objects), according to the datatype and arraysize of the
2054
+ # Field corresponding to this column. See Castable#as_obj
2055
+ # for more details.
2056
+ def value
2057
+ cfield = self.field
2058
+ as_obj(self.text, cfield.datatype, cfield.arraysize)
2059
+ end
2060
+
2061
+ # Set the value of the data element.
2062
+ # An attempt is made to convert native ruby types using the datatype and
2063
+ # arraysize parameters of the associated Field.
2064
+ def value=(v)
2065
+ cfield = self.field
2066
+ self.text = as_string(v, cfield.datatype, cfield.arraysize)
2067
+ end
2068
+
2069
+ # Retrieve the Field associated with this column.
2070
+ def field
2071
+ node_path = self.node.path
2072
+
2073
+ # use the xpath to determine the position
2074
+ pos = (node_path =~ /TD\[\d+\]$/) ?
2075
+ node_path.split('/').last.match(/TD\[(\d+)\]$/)[1] :
2076
+ '1'
2077
+
2078
+ field_node = self.node.find_first("../../../../*[local-name()='FIELD'][#{pos}]")
2079
+ field_node ? Field.new(field_node) : raise("Unable to associate a FIELD with this TD")
2080
+ end
2081
+
2082
+ # Convert a column to HTML.
2083
+ # td = Td.new(:text => 'hello')
2084
+ # puts td.to_html # => <td>hello</td>
2085
+ def to_html
2086
+ builder = Builder::XmlMarkup.new(:indent => 2, :margin => 5)
2087
+
2088
+ builder.td(self.text)
2089
+ end
2090
+
2091
+ protected
2092
+
2093
+ def methods_to_test_for_equality
2094
+ [:encoding, :text]
2095
+ end
2096
+ end
2097
+
2098
+ end
2099
+ end
2100
+ end