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