voruby 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. data/LICENSE +339 -0
  2. data/REQUIREMENTS +4 -0
  3. data/Rakefile.rb +296 -0
  4. data/lib/voruby/adql/adql.rb +2735 -0
  5. data/lib/voruby/adql/ext.rb +15 -0
  6. data/lib/voruby/adql/loader.rb +5 -0
  7. data/lib/voruby/adql/operations.rb +54 -0
  8. data/lib/voruby/adql/parser.rb +160 -0
  9. data/lib/voruby/adql/transforms.rb +573 -0
  10. data/lib/voruby/ext.rb +17 -0
  11. data/lib/voruby/loader.rb +4 -0
  12. data/lib/voruby/misc/propertyfile.rb +36 -0
  13. data/lib/voruby/plastic/applications.rb +174 -0
  14. data/lib/voruby/plastic/constants.rb +30 -0
  15. data/lib/voruby/plastic/loader.rb +10 -0
  16. data/lib/voruby/plastic/plastic.rb +1 -0
  17. data/lib/voruby/resources/conesearch/conesearch.rb +9 -0
  18. data/lib/voruby/resources/conesearch/conesearch_v0_2.rb +55 -0
  19. data/lib/voruby/resources/conesearch/conesearch_v0_3.rb +50 -0
  20. data/lib/voruby/resources/conesearch/conesearch_v1_0.rb +72 -0
  21. data/lib/voruby/resources/conesearch/loader.rb +4 -0
  22. data/lib/voruby/resources/loader.rb +50 -0
  23. data/lib/voruby/resources/nodes.rb +190 -0
  24. data/lib/voruby/resources/openskynode/loader.rb +4 -0
  25. data/lib/voruby/resources/openskynode/openskynode.rb +9 -0
  26. data/lib/voruby/resources/openskynode/openskynode_v0_1.rb +54 -0
  27. data/lib/voruby/resources/sia/loader.rb +5 -0
  28. data/lib/voruby/resources/sia/sia.rb +9 -0
  29. data/lib/voruby/resources/sia/sia_v0_6.rb +90 -0
  30. data/lib/voruby/resources/sia/sia_v0_7.rb +89 -0
  31. data/lib/voruby/resources/sia/sia_v1_0.rb +122 -0
  32. data/lib/voruby/resources/stsci.rb +59 -0
  33. data/lib/voruby/resources/vodataservice/coverage_v0_2.rb +195 -0
  34. data/lib/voruby/resources/vodataservice/coverage_v0_3.rb +158 -0
  35. data/lib/voruby/resources/vodataservice/loader.rb +5 -0
  36. data/lib/voruby/resources/vodataservice/vodataservice.rb +9 -0
  37. data/lib/voruby/resources/vodataservice/vodataservice_v0_4.rb +189 -0
  38. data/lib/voruby/resources/vodataservice/vodataservice_v0_5.rb +163 -0
  39. data/lib/voruby/resources/vodataservice/vodataservice_v1_0.rb +221 -0
  40. data/lib/voruby/resources/voregistry/loader.rb +4 -0
  41. data/lib/voruby/resources/voregistry/voregistry.rb +9 -0
  42. data/lib/voruby/resources/voregistry/voregistry_v0_2.rb +40 -0
  43. data/lib/voruby/resources/voregistry/voregistry_v0_3.rb +30 -0
  44. data/lib/voruby/resources/voregistry/voregistry_v1_0.rb +86 -0
  45. data/lib/voruby/resources/voresource/loader.rb +17 -0
  46. data/lib/voruby/resources/voresource/voresource.rb +9 -0
  47. data/lib/voruby/resources/voresource/voresource_v0_10.rb +322 -0
  48. data/lib/voruby/resources/voresource/voresource_v0_9.rb +405 -0
  49. data/lib/voruby/resources/voresource/voresource_v1_0.rb +230 -0
  50. data/lib/voruby/services/ext.rb +11 -0
  51. data/lib/voruby/services/gestalt/footprint.rb +95 -0
  52. data/lib/voruby/services/gestalt/wcs_fixer.rb +105 -0
  53. data/lib/voruby/services/gestalt/wesix.rb +155 -0
  54. data/lib/voruby/services/loader.rb +7 -0
  55. data/lib/voruby/services/registry/registry.rb +53 -0
  56. data/lib/voruby/services/resolver/resolver.rb +35 -0
  57. data/lib/voruby/sesame/loader.rb +6 -0
  58. data/lib/voruby/sesame/sesame_v1_0.rb +64 -0
  59. data/lib/voruby/simple/loader.rb +6 -0
  60. data/lib/voruby/simple/parameters.rb +196 -0
  61. data/lib/voruby/simple/sap.rb +446 -0
  62. data/lib/voruby/spacetime/loader.rb +3 -0
  63. data/lib/voruby/spacetime/spacetime.rb +607 -0
  64. data/lib/voruby/stc/coords_v1_20.rb +900 -0
  65. data/lib/voruby/stc/loader.rb +55 -0
  66. data/lib/voruby/stc/region_v1_20.rb +274 -0
  67. data/lib/voruby/stc/stc_v1_20.rb +1196 -0
  68. data/lib/voruby/util.rb +27 -0
  69. data/lib/voruby/voevent/loader.rb +7 -0
  70. data/lib/voruby/voevent/voevent_v1_0.rb +213 -0
  71. data/lib/voruby/voevent/voevent_v1_1.rb +196 -0
  72. data/lib/voruby/votables/chandra.rb +410 -0
  73. data/lib/voruby/votables/cnoc.rb +393 -0
  74. data/lib/voruby/votables/data.rb +179 -0
  75. data/lib/voruby/votables/galex.rb +390 -0
  76. data/lib/voruby/votables/hst.rb +391 -0
  77. data/lib/voruby/votables/int.rb +391 -0
  78. data/lib/voruby/votables/libxml_parser.rb +411 -0
  79. data/lib/voruby/votables/libxml_votable.rb +67 -0
  80. data/lib/voruby/votables/loader.rb +10 -0
  81. data/lib/voruby/votables/meta.rb +763 -0
  82. data/lib/voruby/votables/misc.rb +51 -0
  83. data/lib/voruby/votables/nsa.rb +393 -0
  84. data/lib/voruby/votables/nsar3.rb +410 -0
  85. data/lib/voruby/votables/rexml_parser.rb +408 -0
  86. data/lib/voruby/votables/rexml_votable.rb +67 -0
  87. data/lib/voruby/votables/sdss.rb +393 -0
  88. data/lib/voruby/votables/transforms.rb +388 -0
  89. data/lib/voruby/votables/tree.rb +45 -0
  90. data/lib/voruby/votables/types.rb +391 -0
  91. data/lib/voruby/votables/votable.rb +630 -0
  92. data/lib/voruby/votables/xmm.rb +394 -0
  93. data/test/adql/test1.adql +49 -0
  94. data/test/adql/test2.adql +51 -0
  95. data/test/adql/test3.adql +81 -0
  96. data/test/adql/test4.adql +53 -0
  97. data/test/adql/test5.adql +55 -0
  98. data/test/adql/test6.adql +18 -0
  99. data/test/adql/test7.adql +48 -0
  100. data/test/adql/unittest.rb +1672 -0
  101. data/test/plastic/test.rb +44 -0
  102. data/test/plastic/test.vot +5385 -0
  103. data/test/plastic/unittest.rb +66 -0
  104. data/test/resources/conesearch/conesearch_v0_3.xml +31 -0
  105. data/test/resources/conesearch/conesearch_v1_0.xml +86 -0
  106. data/test/resources/conesearch/unittest_v0_3.rb +22 -0
  107. data/test/resources/conesearch/unittest_v1_0.rb +24 -0
  108. data/test/resources/openskynode/open_sky_node_v0_1.xml +32 -0
  109. data/test/resources/openskynode/unittest_v0_1.rb +31 -0
  110. data/test/resources/sia/simple_image_access_v0_7.xml +36 -0
  111. data/test/resources/sia/simple_image_access_v1_0.xml +122 -0
  112. data/test/resources/sia/unittest_v0_7.rb +24 -0
  113. data/test/resources/sia/unittest_v1_0.rb +29 -0
  114. data/test/resources/stsci.xml +336 -0
  115. data/test/resources/unittest_stsci.rb +25 -0
  116. data/test/resources/vodataservice/catalog_service_resource_v1_0.xml +128 -0
  117. data/test/resources/vodataservice/data_collection_resource_v0_5.xml +54 -0
  118. data/test/resources/vodataservice/data_collection_resource_v1_0.xml +117 -0
  119. data/test/resources/vodataservice/data_service_resource_v1_0.xml +115 -0
  120. data/test/resources/vodataservice/sky_service_resource_v0_10.xml +45 -0
  121. data/test/resources/vodataservice/table_service_resource_v1_0.xml +122 -0
  122. data/test/resources/vodataservice/tabular_sky_service_resource_v0_10.xml +60 -0
  123. data/test/resources/vodataservice/unittest_v0_5.rb +126 -0
  124. data/test/resources/vodataservice/unittest_v1_0.rb +151 -0
  125. data/test/resources/voregistry/authority_resource_v0_3.xml +20 -0
  126. data/test/resources/voregistry/authority_resource_v1_0.xml +82 -0
  127. data/test/resources/voregistry/registry_service_v0_3.xml +20 -0
  128. data/test/resources/voregistry/registry_service_v1_0.xml +107 -0
  129. data/test/resources/voregistry/unittest_v0_3.rb +31 -0
  130. data/test/resources/voregistry/unittest_v1_0.rb +34 -0
  131. data/test/resources/voresource/organisation_resource_v1_0.xml +90 -0
  132. data/test/resources/voresource/resource_organisation_v0_10.xml +22 -0
  133. data/test/resources/voresource/resource_service_v0_10.xml +19 -0
  134. data/test/resources/voresource/resource_v0_10.xml +19 -0
  135. data/test/resources/voresource/resource_v1_0.xml +79 -0
  136. data/test/resources/voresource/service_resource_v1_0.xml +91 -0
  137. data/test/resources/voresource/unittest_v0_10.rb +61 -0
  138. data/test/resources/voresource/unittest_v0_9.rb +4 -0
  139. data/test/resources/voresource/unittest_v1_0.rb +190 -0
  140. data/test/services/gestalt/unittest.rb +74 -0
  141. data/test/services/registry/unittest.rb +34 -0
  142. data/test/services/resolver/unittest.rb +38 -0
  143. data/test/simple/unittest.rb +46 -0
  144. data/test/spacetime/unittest.rb +39 -0
  145. data/test/stc/catalog_entry_location_v1_20.xml +112 -0
  146. data/test/stc/obs_data_location_v1_20.xml +108 -0
  147. data/test/stc/search_location_v1_20.xml +54 -0
  148. data/test/stc/stc_resource_profile_v1_20.xml +60 -0
  149. data/test/stc/unittest_v1_20.rb +620 -0
  150. data/test/voevent/unittest_v1_0.rb +79 -0
  151. data/test/voevent/unittest_v1_1.rb +70 -0
  152. data/test/voevent/voevent_v1_0.xml +96 -0
  153. data/test/voevent/voevent_v1_1.xml +76 -0
  154. data/test/votables/test.vot +366 -0
  155. data/test/votables/unittest.rb +54 -0
  156. metadata +256 -0
@@ -0,0 +1,2735 @@
1
+ require 'voruby/adql/loader'
2
+
3
+ # A set of classes designed to read and manipulate ADQL[http://www.ivoa.net/Documents/latest/ADQL.html].
4
+ module VORuby
5
+
6
+ module ADQL
7
+ # Acts as glue between xsi:types and their corresponding
8
+ # domain objects in the * hierarchy.
9
+ class ObjectBuilder
10
+ def self.classes
11
+ {:allSelectionItemType => AllSelectionItem,
12
+ :columnReferenceType => ColumnReference,
13
+ :tableType => ArchiveTable,
14
+ :atomType => Atom,
15
+ :stringType => StringType,
16
+ :realType => RealType,
17
+ :integerType => IntegerType,
18
+ :binaryExprType => BinaryExpr,
19
+ :unaryExprType => UnaryExpr,
20
+ :closedExprType => ClosedExpr,
21
+ :trigonometricFunctionType => TrigonometricFunction,
22
+ :trigonometricFunctionNameType => TrigonometricFunctionName,
23
+ :mathFunctionType => MathFunction,
24
+ :mathFunctionNameType => MathFunctionName,
25
+ :aggregateFunctionType => AggregateFunction,
26
+ :aggregateFunctionNameType => AggregateFunctionName,
27
+ :userDefinedFunctionType => UserDefinedFunction,
28
+ :aliasSelectionItemType => AliasSelectionItem,
29
+ :likePredType => LikePred,
30
+ :notLikePredType => NotLikePred,
31
+ :closedSearchType => ClosedSearch,
32
+ :intersectionSearchType => IntersectionSearch,
33
+ :unionSearchType => UnionSearch,
34
+ :comparisonPredType => ComparisonPred,
35
+ :betweenPredType => BetweenPred,
36
+ :notBetweenPredType => NotBetweenPred,
37
+ :includeTableType => IncludeTable,
38
+ :dropTableType => DropTable,
39
+ :xMatchType => XMatch,
40
+ :xMatchTableAliasType => XMatchTableAlias,
41
+ :comparisonType => Comparison,
42
+ :regionSearchType => RegionSearch,
43
+ :inverseSearchType => InverseSearch,
44
+ :inclusionSetType => InclusionSet,
45
+ :inclusiveSearchType => InclusiveSearch,
46
+ :exclusiveSearchType => ExclusiveSearch,
47
+ :subQuerySetType => SubQuerySet,
48
+ :joinTableType => JoinTable,
49
+ :constantListSetType => ConstantListSet,
50
+ :orderType => Order,
51
+ :circleType => Circle,
52
+ :boxType => Box,
53
+ :archiveTableType => ArchiveTable}
54
+ end
55
+
56
+ # Get the domain class associated with a particular xsi:type.
57
+ def self.get_class_for(type)
58
+ klass = self.classes()[type.to_sym] || self.classes()[type]
59
+ raise "Unable to find type #{type}" if !klass
60
+ return klass
61
+ end
62
+ end
63
+
64
+ # The abstract base type for any of items to be selected in a query.
65
+ class SelectionItem
66
+ def self.from_xml(node)
67
+ #type_s = node.elements['xsi:type']
68
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
69
+ return ObjectBuilder.get_class_for(type_s).from_xml(node)
70
+ end
71
+ end
72
+
73
+ # The base type for a scalar expression.
74
+ class ScalarExpression < SelectionItem
75
+ attr_reader :value
76
+
77
+ def initialize(val)
78
+ self.value = val
79
+ end
80
+
81
+ def is_scalar?(v)
82
+ if v.is_a?(String) or v.is_a?(Integer) or v.is_a?(Float)
83
+ return true
84
+ else
85
+ return false
86
+ end
87
+ end
88
+
89
+ def value=(v)
90
+ if self.is_scalar?(v)
91
+ @value = v
92
+ else
93
+ raise "Scalar expression must contain scalar values"
94
+ end
95
+ end
96
+
97
+ def to_s
98
+ "{value=#{self.value}}"
99
+ end
100
+
101
+ def self.from_xml(node)
102
+ #type_s = node.attributes['xsi:type']
103
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
104
+ return ObjectBuilder.get_class_for(type_s).from_xml(node)
105
+ end
106
+ end
107
+
108
+ # Represents an expression inside a bracket.
109
+ class ClosedExpr < ScalarExpression
110
+ attr_reader :value
111
+
112
+ def initialize(val)
113
+ self.value = val
114
+ end
115
+
116
+ def value=(v)
117
+ VOTables::VOTable::Misc::TypeCheck.new(v, ScalarExpression).check()
118
+ @value = v
119
+ end
120
+
121
+ def self.from_xml(node)
122
+ expr_node = REXML::XPath.first(node, 'Arg')
123
+ expr = ScalarExpression.from_xml(expr_node)
124
+ return ClosedExpr.new(expr)
125
+ end
126
+ end
127
+
128
+ # Used for expressing operations like A+B.
129
+ class BinaryOperator
130
+ attr_reader :operator
131
+
132
+ @@operators = ['+', '-', '*', '/']
133
+
134
+ def initialize(operator, op_list=nil)
135
+ @op_list = op_list || @@operators
136
+ self.operator = operator
137
+ end
138
+
139
+ def operator=(o)
140
+ if @op_list.include?(o)
141
+ @operator = o
142
+ else
143
+ raise "Binary operator is not valid. Use one of: " +
144
+ @op_list.join(', ')
145
+ end
146
+ end
147
+
148
+ def to_s
149
+ "{operator=#{self.operator}}"
150
+ end
151
+ end
152
+
153
+ # Represents a binary expression such as a+b.
154
+ class BinaryExpr < ScalarExpression
155
+ attr_reader :oper, :arg1, :arg2
156
+
157
+ def initialize(arg1, oper, arg2)
158
+ super("#{arg1} #{oper} #{arg2}")
159
+
160
+ self.arg1 = arg1
161
+ self.arg2 = arg2
162
+ self.oper = oper
163
+ end
164
+
165
+ def arg1=(a)
166
+ begin
167
+ VOTables::VOTable::Misc::TypeCheck.new(a, ScalarExpression).check()
168
+ rescue VOTables::VOTable::Misc::TypeException
169
+ @arg1 = ScalarExpression.new(a)
170
+ else
171
+ @arg1 = a
172
+ end
173
+ end
174
+
175
+ def arg2=(a)
176
+ begin
177
+ VOTables::VOTable::Misc::TypeCheck.new(a, ScalarExpression).check()
178
+ rescue VOTables::VOTable::Misc::TypeException
179
+ @arg2 = ScalarExpression.new(a)
180
+ else
181
+ @arg2 = a
182
+ end
183
+ end
184
+
185
+ def oper=(o)
186
+ begin
187
+ VOTables::VOTable::Misc::TypeCheck.new(o, BinaryOperator).check()
188
+ rescue VOTables::VOTable::Misc::TypeException
189
+ @oper = BinaryOperator.new(o)
190
+ else
191
+ @oper = o
192
+ end
193
+ end
194
+
195
+ def to_s
196
+ "{arg1=#{self.arg1},oper=#{self.oper},arg2=#{self.arg2}}"
197
+ end
198
+
199
+ def self.from_xml(node)
200
+ oper = BinaryOperator.new(node.attributes['Oper'])
201
+ arg1_node, arg2_node = node.elements.to_a('Arg')
202
+ arg1 = ScalarExpression.from_xml(arg1_node)
203
+ arg2 = ScalarExpression.from_xml(arg2_node)
204
+ return BinaryExpr.new(arg1, oper, arg2)
205
+ end
206
+ end
207
+
208
+ # Operators for expressing a single element operation.
209
+ class UnaryOperator
210
+ attr_reader :operator
211
+
212
+ @@operators = ['+', '-']
213
+
214
+ def initialize(operator, op_list=nil)
215
+ @op_list = op_list || @@operators
216
+ self.operator = operator
217
+ end
218
+
219
+ def operator=(o)
220
+ if @op_list.include?(o)
221
+ @operator = o
222
+ else
223
+ raise "Unary operator is not valid. Use one of: " +
224
+ @op_list.join(', ')
225
+ end
226
+ end
227
+
228
+ def to_s
229
+ "{operator=#{self.operator}}"
230
+ end
231
+ end
232
+
233
+ # Represents an unary expression such as -(a.ra)
234
+ class UnaryExpr < ScalarExpression
235
+ attr_reader :arg, :oper
236
+
237
+ def initialize(oper, arg)
238
+ super("#{oper}#{arg}")
239
+
240
+ self.arg = arg
241
+ self.oper = oper
242
+ end
243
+
244
+ def arg=(a)
245
+ begin
246
+ VOTables::VOTable::Misc::TypeCheck.new(a, ScalarExpression).check()
247
+ rescue VOTables::VOTable::Misc::TypeException
248
+ @arg = ScalarExpression.new(a)
249
+ else
250
+ @arg = a
251
+ end
252
+ end
253
+
254
+ def oper=(o)
255
+ begin
256
+ VOTables::VOTable::Misc::TypeCheck.new(o, UnaryOperator).check()
257
+ rescue VOTables::VOTable::Misc::TypeException
258
+ @oper = UnaryOperator.new(o)
259
+ else
260
+ @oper = o
261
+ end
262
+ end
263
+
264
+ def to_s
265
+ "{arg=#{self.oper},oper=#{self.arg}}"
266
+ end
267
+
268
+ def self.from_xml(node)
269
+ oper = UnaryOperator.new(node.attributes['Oper'])
270
+ arg_node = REXML::XPath.first(node, 'Arg')
271
+ arg = ScalarExpression.from_xml(arg_node)
272
+ return UnaryExpr.new(oper, arg)
273
+ end
274
+ end
275
+
276
+ # Represents a column.
277
+ class ColumnReference < ScalarExpression
278
+ attr_accessor :table, :name, :xpath_name
279
+
280
+ def initialize(table, name, xpath_name=nil)
281
+ super("#{table}.#{name} #{xpath_name || ''}")
282
+
283
+ self.table = table
284
+ self.name = name
285
+ self.xpath_name = xpath_name
286
+ end
287
+
288
+ def to_s
289
+ self.value
290
+ end
291
+
292
+ def self.from_xml(el)
293
+ table = el.attributes['Table']
294
+ name = el.attributes['Name']
295
+ xpath = el.attributes['xpathName']
296
+ cr = ColumnReference.new(table, name, xpath)
297
+ end
298
+ end
299
+
300
+ # Encapsulates basic literals such as Strings, Integers and Real numbers.
301
+ class Atom < ScalarExpression
302
+ attr_reader :literal
303
+ attr_accessor :unit
304
+
305
+ def initialize(literal, unit=nil)
306
+ super("#{literal}#{unit || ''}")
307
+
308
+ self.literal = literal
309
+ self.unit = unit
310
+ end
311
+
312
+ def literal=(l)
313
+ begin
314
+ VOTables::VOTable::Misc::TypeCheck.new(l, LiteralType).check()
315
+ rescue VOTables::VOTable::Misc::TypeException
316
+ if l.is_a?(Float)
317
+ @literal = RealType.new(l)
318
+ elsif l.is_a?(Integer)
319
+ @literal = IntegerType.new(l)
320
+ elsif l.is_a?(String)
321
+ @literal = StringType.new(l)
322
+ else
323
+ raise "Literal is not a real, integer or string"
324
+ end
325
+ else
326
+ @literal = l
327
+ end
328
+ end
329
+
330
+ def to_s
331
+ "{literal=#{self.literal},unit=#{self.unit}}"
332
+ end
333
+
334
+ def self.from_xml(node)
335
+ literal_node = REXML::XPath.first(node, 'Literal')
336
+ literal = LiteralType.from_xml(literal_node)
337
+ return Atom.new(literal)
338
+ end
339
+ end
340
+
341
+ # The base type for all literals.
342
+ class LiteralType
343
+ def to_s
344
+ "{value=#{self.value}}"
345
+ end
346
+
347
+ def self.from_xml(node)
348
+ #type_s = node.attributes['xsi:type']
349
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
350
+ literal = ObjectBuilder.get_class_for(type_s).from_xml(node)
351
+ return literal
352
+ end
353
+ end
354
+
355
+ # The base type for all numbers.
356
+ class NumberType < LiteralType
357
+ def self.from_xml(node)
358
+ #type_s = node.attributes['xsi:type']
359
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
360
+ number = ObjectBuilder.get_class_for(type_s).from_xml(node)
361
+ end
362
+ end
363
+
364
+ # Represents a real number.
365
+ class RealType < NumberType
366
+ attr_reader :value
367
+
368
+ def initialize(value)
369
+ self.value = value
370
+ end
371
+
372
+ def value=(v)
373
+ begin
374
+ VOTables::VOTable::Misc::TypeCheck.new(v, Float).check()
375
+ rescue VOTables::VOTable::Misc::TypeException
376
+ if v.is_a?(Integer)
377
+ @value = v.to_f
378
+ else
379
+ raise VOTables::VOTable::Misc::TypeException
380
+ end
381
+ else
382
+ @value = v
383
+ end
384
+ end
385
+
386
+ def self.from_xml(node)
387
+ return RealType.new(node.attributes['Value'].to_f)
388
+ end
389
+ end
390
+
391
+ # Represents an integer.
392
+ class IntegerType < NumberType
393
+ attr_reader :value
394
+
395
+ def initialize(value)
396
+ self.value = value
397
+ end
398
+
399
+ def value=(v)
400
+ VOTables::VOTable::Misc::TypeCheck.new(v, Integer).check()
401
+ @value = v
402
+ end
403
+
404
+ def self.from_xml(node)
405
+ return IntegerType.new(node.attributes['Value'].to_i)
406
+ end
407
+ end
408
+
409
+ # Represents a string literal.
410
+ class StringType < LiteralType
411
+ attr_reader :value
412
+
413
+ def initialize(value)
414
+ self.value = value
415
+ end
416
+
417
+ def value=(v)
418
+ VOTables::VOTable::Misc::TypeCheck.new(v, String).check()
419
+ @value = v
420
+ end
421
+
422
+ def self.from_xml(node)
423
+ return StringType.new(node.attributes['Value'])
424
+ end
425
+ end
426
+
427
+ # The base type for a function.
428
+ class FunctionType < ScalarExpression
429
+ def to_s
430
+ "{name=#{self.name}}"
431
+ end
432
+ end
433
+
434
+ # Option of selecting all or distinct elements in a query.
435
+ class SelectionOption
436
+ attr_reader :option
437
+
438
+ def initialize(option)
439
+ self.option = option
440
+ end
441
+
442
+ def option=(o)
443
+ begin
444
+ VOTables::VOTable::Misc::TypeCheck.new(o, AllOrDistinct).check()
445
+ rescue VOTables::VOTable::Misc::TypeException
446
+ @option = AllOrDistinct.new(o)
447
+ else
448
+ @option = o
449
+ end
450
+ end
451
+
452
+ def content
453
+ self.option.option
454
+ end
455
+
456
+ def to_s
457
+ "{option=#{self.option}}"
458
+ end
459
+
460
+ def self.from_xml(node)
461
+ option = AllOrDistinct.new(node.attributes['Option'])
462
+ return SelectionOption.new(option)
463
+ end
464
+ end
465
+
466
+ # Enumeration for All and Distinct options.
467
+ class AllOrDistinct
468
+ attr_reader :option, :option_list
469
+
470
+ @@options = ['ALL', 'DISTINCT']
471
+
472
+ def initialize(option, option_list=nil)
473
+ @option_list = option_list || @@options
474
+ self.option = option
475
+ end
476
+
477
+ def option=(o)
478
+ if @option_list.include?(o)
479
+ @option = o
480
+ else
481
+ raise "Option is not valid. Use one of: " +
482
+ @option_list.join(', ')
483
+ end
484
+ end
485
+
486
+ def to_s
487
+ "{option=#{self.option}}"
488
+ end
489
+ end
490
+
491
+ # Represents a trigonometric function.
492
+ class TrigonometricFunction < FunctionType
493
+ attr_reader :name, :arg, :allow
494
+
495
+ def initialize(name, arg, allow=nil)
496
+ self.name = name
497
+ self.arg = arg
498
+ self.allow = allow
499
+ end
500
+
501
+ def name=(n)
502
+ begin
503
+ VOTables::VOTable::Misc::TypeCheck.new(n, TrigonometricFunctionName).check()
504
+ rescue VOTables::VOTable::Misc::TypeException
505
+ @name = TrigonometricFunctionName.new(n)
506
+ else
507
+ @name = n
508
+ end
509
+ end
510
+
511
+ def arg=(a)
512
+ VOTables::VOTable::Misc::TypeCheck.new(a, SelectionItem).check()
513
+ @arg = a
514
+ end
515
+
516
+ def allow=(a)
517
+ VOTables::VOTable::Misc::TypeCheck.new(a, SelectionOption).check()
518
+ @allow = a
519
+ end
520
+
521
+ def to_s
522
+ "{name=#{self.name},allow=#{self.allow},arg=#{self.arg}}"
523
+ end
524
+
525
+ def self.from_xml(node)
526
+ name = TrigonometricFunctionName.from_xml(node)
527
+ arg_node = REXML::XPath.first(node, 'Arg')
528
+ arg = Arg.from_xml(arg_node)
529
+ allow_node = REXML::XPath.first(node, 'Allow')
530
+ allow = nil
531
+ if allow_node
532
+ allow = Allow.from_xml(allow_node)
533
+ end
534
+ return TrigonometricFunction.new(name, arg, allow)
535
+ end
536
+ end
537
+
538
+ # Enumeration of allowed trigonometric functions.
539
+ class TrigonometricFunctionName
540
+ attr_reader :value, :values_list
541
+
542
+ @@values = ['SIN', 'COS', 'TAN', 'COT', 'ASIN', 'ACOS', 'ATAN', 'ATAN2']
543
+
544
+ def initialize(value, values_list=nil)
545
+ @values_list = values_list || @@values
546
+ self.value = value
547
+ end
548
+
549
+ def value=(v)
550
+ if @values_list.include?(v)
551
+ @value = v
552
+ else
553
+ raise "Value is not valid. Use one of: " +
554
+ @values_list.join(', ')
555
+ end
556
+ end
557
+
558
+ def to_s
559
+ "{value=#{self.value}}"
560
+ end
561
+
562
+ def self.from_xml(node)
563
+ return TrigonometricFunctionName.new(node.attributes['Name'])
564
+ end
565
+ end
566
+
567
+ # Represents a math function.
568
+ class MathFunction < FunctionType
569
+ attr_reader :name, :arg, :allow
570
+
571
+ def initialize(name, arg, allow=nil)
572
+ self.name = name
573
+ self.arg = arg
574
+ self.allow = allow
575
+ end
576
+
577
+ def name=(n)
578
+ begin
579
+ VOTables::VOTable::Misc::TypeCheck.new(n, MathFunctionName).check()
580
+ rescue VOTables::VOTable::Misc::TypeException
581
+ @name = MathFunctionName.new(n)
582
+ else
583
+ @name = n
584
+ end
585
+ end
586
+
587
+ def arg=(a)
588
+ VOTables::VOTable::Misc::TypeCheck.new(a, SelectionItem).check()
589
+ @arg = a
590
+ end
591
+
592
+ def allow=(a)
593
+ VOTables::VOTable::Misc::TypeCheck.new(a, SelectionOption).check()
594
+ @allow = a
595
+ end
596
+
597
+ def self.from_xml(node)
598
+ name = MathFunctionName.from_xml(node)
599
+ arg_node = REXML::XPath.first(node, 'Arg')
600
+ arg = Arg.from_xml(arg_node)
601
+ allow_node = REXML::XPath.first(node, 'Allow')
602
+ allow = nil
603
+ if allow_node
604
+ allow = Allow.from_xml(allow_node)
605
+ end
606
+ return MathFunction.new(name, arg, allow)
607
+ end
608
+
609
+ def to_s
610
+ "{name=#{self.name},arg=#{self.arg},allow=#{self.allow}}"
611
+ end
612
+ end
613
+
614
+ # Enumeration of allowed math functions.
615
+ class MathFunctionName
616
+ attr_reader :value, :values_list
617
+
618
+ @@values = ['ABS', 'CEILING', 'DEGREES', 'EXP', 'FLOOR', 'LOG',
619
+ 'PI', 'POWER', 'RADIANS', 'SQRT', 'SQUARE', 'LOG10',
620
+ 'RAND', 'ROUND', 'TRUNCATE']
621
+
622
+ def initialize(value, values_list=nil)
623
+ @values_list = values_list || @@values
624
+ self.value = value
625
+ end
626
+
627
+ def value=(v)
628
+ if @values_list.include?(v)
629
+ @value = v
630
+ else
631
+ raise "Value is not valid. Use one of: " +
632
+ @values_list.join(', ')
633
+ end
634
+ end
635
+
636
+ def to_s
637
+ "{value=#{self.value}}"
638
+ end
639
+
640
+ def self.from_xml(node)
641
+ return MathFunctionName.new(node.attributes['Name'])
642
+ end
643
+ end
644
+
645
+ # Represents an aggregate function.
646
+ class AggregateFunction < FunctionType
647
+ attr_reader :name, :arg, :allow
648
+
649
+ def initialize(name, arg, allow=nil)
650
+ self.name = name
651
+ self.arg = arg
652
+ self.allow = allow
653
+ end
654
+
655
+ def name=(n)
656
+ begin
657
+ VOTables::VOTable::Misc::TypeCheck.new(n, AggregateFunctionName).check()
658
+ rescue VOTables::VOTable::Misc::TypeException
659
+ @name = AggregateFunctionName.new(n)
660
+ else
661
+ @name = n
662
+ end
663
+ end
664
+
665
+ def arg=(a)
666
+ VOTables::VOTable::Misc::TypeCheck.new(a, SelectionItem).check()
667
+ @arg = a
668
+ end
669
+
670
+ def allow=(a)
671
+ VOTables::VOTable::Misc::TypeCheck.new(a, SelectionOption).check()
672
+ @allow = a
673
+ end
674
+
675
+ def to_s
676
+ "{name=#{self.name},arg=#{self.arg},allow=#{self.allow}}"
677
+ end
678
+
679
+ def self.from_xml(node)
680
+ name = AggregateFunctionName.from_xml(node)
681
+ arg_node = REXML::XPath.first(node, 'Arg')
682
+ arg = Arg.from_xml(arg_node)
683
+ allow_node = REXML::XPath.first(node, 'Allow')
684
+ allow = nil
685
+ if allow_node
686
+ allow = Allow.from_xml(allow_node)
687
+ end
688
+ return AggregateFunction.new(name, arg, allow)
689
+ end
690
+ end
691
+
692
+ # Enumeration of allowed aggregate functions.
693
+ class AggregateFunctionName
694
+ attr_reader :value, :values_list
695
+
696
+ @@values = ['AVG', 'MIN', 'MAX', 'SUM', 'COUNT']
697
+
698
+ def initialize(value, values_list=nil)
699
+ @values_list = values_list || @@values
700
+ self.value = value
701
+ end
702
+
703
+ def value=(v)
704
+ if @values_list.include?(v)
705
+ @value = v
706
+ else
707
+ raise "Value is not valid. Use one of: " +
708
+ @values_list.join(', ')
709
+ end
710
+ end
711
+
712
+ def to_s
713
+ "{value=#{self.value}}"
714
+ end
715
+
716
+ def self.from_xml(node)
717
+ return AggregateFunctionName.new(node.attributes['Name'])
718
+ end
719
+ end
720
+
721
+ # Used to select an expression as a new alias column.
722
+ class AliasSelectionItem < SelectionItem
723
+ attr_reader :expression
724
+ attr_accessor :as
725
+
726
+ def initialize(expression, as=nil)
727
+ self.expression = expression
728
+ self.as = as
729
+ end
730
+
731
+ def expression=(e)
732
+ begin
733
+ VOTables::VOTable::Misc::TypeCheck.new(e, ScalarExpression).check()
734
+ rescue VOTables::VOTable::Misc::TypeException
735
+ @expression = ScalarExpression.new(e)
736
+ else
737
+ @expression = e
738
+ end
739
+ end
740
+
741
+ def to_s
742
+ "{expression=#{self.expression},as=#{self.as}}"
743
+ end
744
+
745
+ def self.from_xml(node)
746
+ as = node.attributes['As']
747
+ expr_node = REXML::XPath.first(node, 'Expression')
748
+ expr = ScalarExpression.from_xml(expr_node)
749
+ return AliasSelectionItem.new(expr, as)
750
+ end
751
+ end
752
+
753
+ # Represent all columns as in Select * query.
754
+ class AllSelectionItem < SelectionItem
755
+ @@value = '*'
756
+
757
+ def initialize;end
758
+
759
+ def self.value
760
+ return @@value
761
+ end
762
+
763
+ def to_s
764
+ "{expression=#{self.value}}"
765
+ end
766
+
767
+ def self.from_xml(node)
768
+ return AllSelectionItem.new()
769
+ end
770
+ end
771
+
772
+ # The comparison operators such as Less-than or More-than, etc.
773
+ class Comparison
774
+ attr_reader :value, :values_list
775
+
776
+ @@values = ['=', '<>', '>', '>=', '<', '<=']
777
+
778
+ def initialize(value, values_list=nil)
779
+ @values_list = values_list || @@values
780
+ self.value = value
781
+ end
782
+
783
+ def value=(v)
784
+ if @values_list.include?(v)
785
+ @value = v
786
+ else
787
+ raise "Value is not valid. Use one of: " +
788
+ @values_list.join(', ')
789
+ end
790
+ end
791
+
792
+ def to_s
793
+ "{value=#{self.value}}"
794
+ end
795
+
796
+ def self.from_xml(node)
797
+ return Comparison.new(node.attributes['Comparison'])
798
+ end
799
+ end
800
+
801
+ # The base type for all tables used in the From clause of the query.
802
+ class FromTable
803
+ def self.from_xml(node)
804
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
805
+ table = ObjectBuilder.get_class_for(type_s).from_xml(node)
806
+ end
807
+ end
808
+
809
+ # Same as a TableType with an additional archive name.
810
+ class ArchiveTable < FromTable
811
+ attr_accessor :archive, :name, :alias_name
812
+
813
+ def initialize(archive, name, alias_name=nil)
814
+ self.archive = archive
815
+ self.name = name
816
+ self.alias_name = alias_name
817
+ end
818
+
819
+ def to_s
820
+ "{archive=#{self.archive},name=#{self.name},alias=#{self.alias_name}}"
821
+ end
822
+
823
+ def self.from_xml(node)
824
+ archive = node.attributes['Archive'] or raise "No ArchiveTable attribute 'Archive'"
825
+ #name = CGI::escapeHTML(node.attributes['Name']) or raise "No ArchiveTable attribute 'Name'"
826
+ name = node.attributes['Name'] or raise "No ArchiveTable attribute 'Name'"
827
+ alias_name = node.attributes['Alias']
828
+
829
+ at = ArchiveTable.new(archive, name, alias_name)
830
+ end
831
+ end
832
+
833
+ # Represents a table with its name and its alias name.
834
+ class Table < FromTable
835
+ attr_accessor :name, :alias_name, :xpath_name
836
+
837
+ def initialize(name, alias_name=nil, xpath_name=nil)
838
+ self.name = name
839
+ self.alias_name = alias_name
840
+ self.xpath_name = xpath_name
841
+ end
842
+
843
+ def to_s
844
+ "{name=#{self.name},alias=#{self.alias_name},xpath=#{self.xpath_name}}"
845
+ end
846
+
847
+ def self.from_xml(node)
848
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
849
+ table = ObjectBuilder.get_class_for(type_s).from_xml(node)
850
+ end
851
+ end
852
+
853
+ # The base type for all table inclusion or drop types used in a cross match expression.
854
+ class XMatchTableAlias
855
+ def to_s
856
+ "{name=#{self.name}}"
857
+ end
858
+
859
+ def self.from_xml(node)
860
+ #type_s = node.attributes['xsi:type']
861
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
862
+ xtable = ObjectBuilder.get_class_for(type_s).from_xml(node)
863
+ end
864
+ end
865
+
866
+ # Used for adding a table for the Xmatch operation.
867
+ class IncludeTable < XMatchTableAlias
868
+ attr_accessor :name
869
+
870
+ def initialize(name)
871
+ self.name = name
872
+ end
873
+
874
+ def self.from_xml(node)
875
+ name = node.attributes['Name']
876
+ return IncludeTable.new(name)
877
+ end
878
+ end
879
+
880
+ # Used for avoiding a table in Xmatch.
881
+ class DropTable < XMatchTableAlias
882
+ attr_accessor :name
883
+
884
+ def initialize(name)
885
+ self.name = name
886
+ end
887
+
888
+ def self.from_xml(node)
889
+ name = node.attributes['Name']
890
+ return DropTable.new(name)
891
+ end
892
+ end
893
+
894
+ # The base type for searches in Where and Having clauses of the query.
895
+ class Search
896
+ def self.from_xml(node)
897
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
898
+ search = ObjectBuilder.get_class_for(type_s).from_xml(node)
899
+ end
900
+ end
901
+
902
+ # Represents expressions like a AND b.
903
+ class IntersectionSearch < Search
904
+ attr_reader :cond1, :cond2
905
+
906
+ def initialize(cond1, cond2)
907
+ self.cond1 = cond1
908
+ self.cond2 = cond2
909
+ end
910
+
911
+ def cond1=(cond)
912
+ VOTables::VOTable::Misc::TypeCheck.new(cond, Search).check()
913
+ @cond1 = cond
914
+ end
915
+
916
+ def cond2=(cond)
917
+ VOTables::VOTable::Misc::TypeCheck.new(cond, Search).check()
918
+ @cond2 = cond
919
+ end
920
+
921
+ def to_s
922
+ "{cond1=#{self.cond1},cond2=#{self.cond2}}"
923
+ end
924
+
925
+ def self.from_xml(node)
926
+ cond1_node, cond2_node = node.elements.to_a('Condition')
927
+ cond1 = Search.from_xml(cond1_node)
928
+ cond2 = Search.from_xml(cond2_node)
929
+ return IntersectionSearch.new(cond1, cond2)
930
+ end
931
+
932
+ # This method finds a condition given its attributes and it returns it
933
+ def find_condition(type, attributes)
934
+ condition = self.cond1.find_condition(type, attributes)
935
+ if condition == nil
936
+ condition = self.cond2.find_condition(type, attributes)
937
+ end
938
+
939
+ return condition
940
+ end
941
+
942
+ # This method removes a condition. -1 means that the condition must
943
+ # be removed. 1 means that the condition given its attributes wasn't
944
+ # found, so that the process must continue.
945
+ def remove_condition(type, attributes)
946
+ cond1 = self.cond1.remove_condition(type, attributes)
947
+ if cond1 == -1
948
+ return self.cond2
949
+ elsif cond1 == 1
950
+ cond2 = self.cond2.remove_condition(type, attributes)
951
+ if cond2 == -1
952
+ return self.cond1
953
+ elsif cond2 == 1
954
+ return 1
955
+ end
956
+ else
957
+ self.cond1 = cond1
958
+ return self
959
+ end
960
+ end
961
+
962
+ # This method modifies a condition given its old attributes,
963
+ # replacing the old attributes with the new attributes.
964
+ def modify_condition(type, attributes_old, attributes_new)
965
+ cond1 = self.cond1.modify_condition(type, attributes_old, attributes_new)
966
+ if cond1 != nil
967
+ self.cond1 = cond1
968
+ else
969
+ cond2 = self.cond2.modify_condition(type, attributes_old, attributes_new)
970
+ if cond2 != nil
971
+ self.cond2 = cond2
972
+ else
973
+ return nil
974
+ end
975
+ end
976
+
977
+ return self
978
+ end
979
+
980
+ # This method replaces a condition for a new condition. -1 means that
981
+ # the condition must be replaced for the new condition. 1 means that
982
+ # the process must continue.
983
+ def replace_condition(type, attributes, new_condition)
984
+ cond1 = self.cond1.replace_condition(type, attributes, new_condition)
985
+ if cond1 == -1
986
+ self.cond1 = new_condition
987
+ return self
988
+ elsif cond1 == 1
989
+ cond2 = self.cond2.replace_condition(type, attributes, new_condition)
990
+ if cond2 == -1
991
+ self.cond2 = new_condition
992
+ return self
993
+ elsif cond2 == 1
994
+ return 1
995
+ end
996
+ else
997
+ return self
998
+ end
999
+ end
1000
+ end
1001
+
1002
+ # Represents expressions like A Or B.
1003
+ class UnionSearch < Search
1004
+ attr_reader :cond1, :cond2
1005
+
1006
+ def initialize(cond1, cond2)
1007
+ self.cond1 = cond1
1008
+ self.cond2 = cond2
1009
+ end
1010
+
1011
+ def cond1=(cond)
1012
+ VOTables::VOTable::Misc::TypeCheck.new(cond, Search).check()
1013
+ @cond1 = cond
1014
+ end
1015
+
1016
+ def cond2=(cond)
1017
+ VOTables::VOTable::Misc::TypeCheck.new(cond, Search).check()
1018
+ @cond2 = cond
1019
+ end
1020
+
1021
+ def to_s
1022
+ "{cond1=#{self.cond1},cond2=#{self.cond2}}"
1023
+ end
1024
+
1025
+ def self.from_xml(node)
1026
+ cond1_node, cond2_node = node.elements.to_a('Condition')
1027
+ cond1 = Search.from_xml(cond1_node)
1028
+ cond2 = Search.from_xml(cond2_node)
1029
+ return UnionSearch.new(cond1, cond2)
1030
+ end
1031
+
1032
+ # This method finds a condition given its attributes and it returns it
1033
+ def find_condition(type, attributes)
1034
+ condition = self.cond1.find_condition(type, attributes)
1035
+ if condition == nil
1036
+ condition = self.cond2.find_condition(type, attributes)
1037
+ end
1038
+
1039
+ return condition
1040
+ end
1041
+
1042
+ # This method removes a condition. -1 means that the condition must
1043
+ # be removed. 1 means that the condition given its attributes wasn't
1044
+ # found, so that the process must continue.
1045
+ def remove_condition(type, attributes)
1046
+ cond1 = self.cond1.remove_condition(type, attributes)
1047
+ if cond1 == -1
1048
+ return self.cond2
1049
+ elsif cond1 == 1
1050
+ cond2 = self.cond2.remove_condition(type, attributes)
1051
+ if cond2 == -1
1052
+ return self.cond1
1053
+ elsif cond2 == 1
1054
+ return 1
1055
+ end
1056
+ else
1057
+ self.cond1 = cond1
1058
+ return self
1059
+ end
1060
+ end
1061
+
1062
+ # This method modifies a condition given its old attributes,
1063
+ # replacing the old attributes with the new attributes.
1064
+ def modify_condition(type, attributes_old, attributes_new)
1065
+ cond1 = self.cond1.modify_condition(type, attributes_old, attributes_new)
1066
+ if cond1 != nil
1067
+ self.cond1 = cond1
1068
+ else
1069
+ cond2 = self.cond2.modify_condition(type, attributes_old, attributes_new)
1070
+ if cond2 != nil
1071
+ self.cond2 = cond2
1072
+ else
1073
+ return nil
1074
+ end
1075
+ end
1076
+
1077
+ return self
1078
+ end
1079
+
1080
+ # This method replaces a condition for a new condition. -1 means that
1081
+ # the condition must be replaced for the new condition. 1 means that
1082
+ # the process must continue
1083
+ def replace_condition(type, attributes, new_condition)
1084
+ cond1 = self.cond1.replace_condition(type, attributes, new_condition)
1085
+ if cond1 == -1
1086
+ self.cond1 = new_condition
1087
+ return self
1088
+ elsif cond1 == 1
1089
+ cond2 = self.cond2.replace_condition(type, attributes, new_condition)
1090
+ if cond2 == -1
1091
+ self.cond2 = new_condition
1092
+ return self
1093
+ elsif cond2 == 1
1094
+ return 1
1095
+ end
1096
+ else
1097
+ return self
1098
+ end
1099
+ end
1100
+ end
1101
+
1102
+ # A cross match expression.
1103
+ class XMatch < Search
1104
+ attr_reader :tables, :nature, :sigma
1105
+
1106
+ def initialize(tables, nature, sigma)
1107
+ self.tables = tables
1108
+ self.nature = nature
1109
+ self.sigma = sigma
1110
+ end
1111
+
1112
+ def tables=(ts)
1113
+ raise "Specify at least two tables" if !ts or ts.length < 2
1114
+
1115
+ ts.each do |t|
1116
+ VOTables::VOTable::Misc::TypeCheck.new(t, XMatchTableAlias)
1117
+ end
1118
+
1119
+ @tables = ts
1120
+ end
1121
+
1122
+ def nature=(n)
1123
+ begin
1124
+ VOTables::VOTable::Misc::TypeCheck.new(n, Comparison).check()
1125
+ rescue VOTables::VOTable::Misc::TypeException
1126
+ @nature = Comparison.new(n)
1127
+ else
1128
+ @nature = n
1129
+ end
1130
+ end
1131
+
1132
+ def sigma=(s)
1133
+ begin
1134
+ VOTables::VOTable::Misc::TypeCheck.new(s, NumberType).check()
1135
+ rescue VOTables::VOTable::Misc::TypeException
1136
+ if s.is_a?(Float)
1137
+ @sigma = RealType.new(s)
1138
+ elsif s.is_a?(Integer)
1139
+ @sigma = IntegerType.new(s)
1140
+ else
1141
+ raise "Sigma must be a float or integer"
1142
+ end
1143
+ else
1144
+ @sigma = s
1145
+ end
1146
+ end
1147
+
1148
+ def to_s
1149
+ tables = self.tables.collect{|x| x.to_s}.join('|')
1150
+ "{tables=#{tables},nature=#{self.nature},sigma=#{self.sigma}}"
1151
+ end
1152
+
1153
+ def self.from_xml(node)
1154
+ tables = []
1155
+ node.elements.each('Table') do |tbl_node|
1156
+ table = XMatchTableAlias.from_xml(tbl_node)
1157
+ tables.push(table)
1158
+ end
1159
+
1160
+ nature_node = REXML::XPath.first(node, 'Nature')
1161
+ nature = Nature.from_xml(nature_node)
1162
+
1163
+ sigma_node = REXML::XPath.first(node, 'Sigma')
1164
+ sigma = Sigma.from_xml(sigma_node)
1165
+
1166
+ return XMatch.new(tables, nature, sigma)
1167
+ end
1168
+ end
1169
+
1170
+ # The Like expression of a query.
1171
+ class LikePred < Search
1172
+ attr_reader :arg, :pattern
1173
+
1174
+ def initialize(arg, pattern)
1175
+ self.arg = arg
1176
+ self.pattern = pattern
1177
+ end
1178
+
1179
+ def arg=(a)
1180
+ begin
1181
+ VOTables::VOTable::Misc::TypeCheck.new(a, ScalarExpression).check()
1182
+ rescue VOTables::VOTable::Misc::TypeException
1183
+ @arg = ScalarExpression.new(a)
1184
+ else
1185
+ @arg = a
1186
+ end
1187
+ end
1188
+
1189
+ def pattern=(p)
1190
+ begin
1191
+ VOTables::VOTable::Misc::TypeCheck.new(p, Atom).check()
1192
+ rescue VOTables::VOTable::Misc::TypeException
1193
+ @pattern = Atom.new(p)
1194
+ else
1195
+ @pattern = p
1196
+ end
1197
+ end
1198
+
1199
+ def to_s
1200
+ "{arg=#{self.arg},pattern=#{self.pattern}}"
1201
+ end
1202
+
1203
+ def self.from_xml(node)
1204
+ arg_node = REXML::XPath.first(node, 'Arg')
1205
+ arg = Arg.from_xml(arg_node)
1206
+ pattern_node = REXML::XPath.first(node, 'Pattern')
1207
+ pattern = Pattern.from_xml(pattern_node)
1208
+
1209
+ return LikePred.new(arg, pattern)
1210
+ end
1211
+
1212
+ def self.create_new_object(attributes)
1213
+ arg = ColumnReference.new(attributes['table'], attributes['name'], nil)
1214
+ pattern = StringType.new(attributes['pattern'])
1215
+ return LikePred.new(arg, pattern)
1216
+ end
1217
+
1218
+ def match_attributtes(attributes)
1219
+ return true if self.arg.table == attributes['table'] and
1220
+ self.arg.name == attributes['name'] and
1221
+ self.pattern.literal.value == attributes['pattern']
1222
+ return false
1223
+ end
1224
+
1225
+ # This method finds a condition given its attributes and it returns it
1226
+ def find_condition(type, attributes)
1227
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1228
+ return self if self.match_attributtes(attributes)
1229
+ return nil
1230
+ end
1231
+ return nil
1232
+ end
1233
+
1234
+ # This method removes a condition. -1 means that the condition must
1235
+ # be removed. 1 means that the condition given its attributes wasn't
1236
+ # found, so that the process must continue.
1237
+ def remove_condition(type, attributes)
1238
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1239
+ return -1 if self.match_attributtes(attributes)
1240
+ return 1
1241
+ end
1242
+ return 1
1243
+ end
1244
+
1245
+ # This method modifies a condition given its old attributes,
1246
+ # replacing the old attributes with the new attributes.
1247
+ def modify_condition(type, attributes_old, attributes_new)
1248
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1249
+ if self.match_attributtes(attributes_old)
1250
+ return LikePred.create_new_object(attributes_new)
1251
+ else
1252
+ return nil
1253
+ end
1254
+ end
1255
+ return nil
1256
+ end
1257
+
1258
+ # This method replaces a condition given its attributes.
1259
+ # Returns -1 if the object must be replaced, or returns 1 if
1260
+ # isn't the object that must be replaced, so the process must continue
1261
+ def replace_condition(type, attributes, new_condition)
1262
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1263
+ return -1 if self.match_attributtes(attributes)
1264
+ return 1
1265
+ end
1266
+ return 1
1267
+ end
1268
+ end
1269
+
1270
+ # The Not Like expression of a query.
1271
+ class NotLikePred < LikePred
1272
+ def self.from_xml(node)
1273
+ arg_node = REXML::XPath.first(node, 'Arg')
1274
+ arg = Arg.from_xml(arg_node)
1275
+ pattern_node = REXML::XPath.first(node, 'Pattern')
1276
+ pattern = Pattern.from_xml(pattern_node)
1277
+ return NotLikePred.new(arg, pattern)
1278
+ end
1279
+ end
1280
+
1281
+ # Represents SQL NOT IN expression.
1282
+ class ExclusiveSearch < Search
1283
+ attr_reader :expression, :set
1284
+
1285
+ def initialize(expression, set)
1286
+ self.expression = expression
1287
+ self.set = set
1288
+ end
1289
+
1290
+ def expression=(e)
1291
+ begin
1292
+ VOTables::VOTable::Misc::TypeCheck.new(e, ScalarExpression).check()
1293
+ rescue VOTables::VOTable::Misc::TypeException
1294
+ @expression = ScalarExpression.new(e)
1295
+ else
1296
+ @expression = e
1297
+ end
1298
+ end
1299
+
1300
+ def set=(s)
1301
+ VOTables::VOTable::Misc::TypeCheck.new(s, InclusionSet).check()
1302
+ @set = s
1303
+ end
1304
+
1305
+ def to_s
1306
+ "{expression=#{self.expression},set=#{self.set}}"
1307
+ end
1308
+
1309
+ def self.from_xml(node)
1310
+ expr_node = REXML::XPath.first(node, 'Expression')
1311
+ expr = Expression.from_xml(expr_node)
1312
+ set_node = REXML::XPath.first(node, 'Set')
1313
+ set = Set.from_xml(set_node)
1314
+ return ExclusiveSearch.new(expr, set)
1315
+ end
1316
+ end
1317
+
1318
+ # The base type for selection set in a SQL IN expression.
1319
+ class InclusionSet
1320
+ def self.from_xml(node)
1321
+ #type_s = node.attributes['xsi:type']
1322
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
1323
+ return ObjectBuilder.get_class_for(type_s).from_xml(node)
1324
+ end
1325
+ end
1326
+
1327
+ # Represents the subquery in a SQL IN expression.
1328
+ class SubQuerySet < InclusionSet
1329
+ attr_reader :selection
1330
+
1331
+ def initialize(selection)
1332
+ self.selection = selection
1333
+ end
1334
+
1335
+ def selection=(s)
1336
+ VOTables::VOTable::Misc::TypeCheck.new(s, Select).check()
1337
+ @selection = s
1338
+ end
1339
+
1340
+ def to_s
1341
+ "{selection=#{self.selection}}"
1342
+ end
1343
+
1344
+ def self.from_xml(node)
1345
+ select_node = REXML::XPath.first(node, 'Select')
1346
+ select = Select.from_xml(select_node)
1347
+ return SubQuerySet.new(select)
1348
+ end
1349
+ end
1350
+
1351
+ # Represents expressions like (A).
1352
+ class ClosedSearch < Search
1353
+ attr_reader :condition
1354
+
1355
+ def initialize(condition)
1356
+ self.condition = condition
1357
+ end
1358
+
1359
+ def condition=(c)
1360
+ VOTables::VOTable::Misc::TypeCheck.new(c, Search).check()
1361
+ @condition = c
1362
+ end
1363
+
1364
+ def to_s
1365
+ "{condition=#{self.condition}}"
1366
+ end
1367
+
1368
+ def self.from_xml(node)
1369
+ cond_node = REXML::XPath.first(node, 'Condition')
1370
+ cond = Condition.from_xml(cond_node)
1371
+ return ClosedSearch.new(cond)
1372
+ end
1373
+ end
1374
+
1375
+ # Represents the Comparison of two expressions.
1376
+ class ComparisonPred < Search
1377
+ attr_reader :arg1, :arg2, :comparison
1378
+
1379
+ def initialize(arg1, comparison, arg2)
1380
+ self.arg1 = arg1
1381
+ self.comparison = comparison
1382
+ self.arg2 = arg2
1383
+ end
1384
+
1385
+ def arg1=(a)
1386
+ begin
1387
+ VOTables::VOTable::Misc::TypeCheck.new(a, ScalarExpression).check()
1388
+ rescue VOTables::VOTable::Misc::TypeException
1389
+ @arg1 = ScalarExpression.new(a)
1390
+ else
1391
+ @arg1 = a
1392
+ end
1393
+ end
1394
+
1395
+ def arg2=(a)
1396
+ begin
1397
+ VOTables::VOTable::Misc::TypeCheck.new(a, ScalarExpression).check()
1398
+ rescue VOTables::VOTable::Misc::TypeException
1399
+ @arg2 = ScalarExpression.new(a)
1400
+ else
1401
+ @arg2 = a
1402
+ end
1403
+ end
1404
+
1405
+ def comparison=(c)
1406
+ begin
1407
+ VOTables::VOTable::Misc::TypeCheck.new(c, Comparison).check()
1408
+ rescue VOTables::VOTable::Misc::TypeException
1409
+ @comparison = Comparison.new(c)
1410
+ else
1411
+ @comparison = c
1412
+ end
1413
+ end
1414
+
1415
+ def to_s
1416
+ "{arg1=#{self.arg1},comparison=#{self.comparison},arg2=#{self.arg2}}"
1417
+ end
1418
+
1419
+ def self.from_xml(node)
1420
+ comparison_s = node.attributes['Comparison']
1421
+ arg1_node, arg2_node = node.elements.to_a('Arg')
1422
+ arg1 = Arg.from_xml(arg1_node)
1423
+ arg2 = Arg.from_xml(arg2_node)
1424
+
1425
+ return ComparisonPred.new(arg1, comparison_s, arg2)
1426
+ end
1427
+
1428
+ def self.create_new_object(attributes)
1429
+ arg1 = ColumnReference.new(attributes['table'], attributes['name'], nil)
1430
+
1431
+ arg2 = nil
1432
+ if attributes['value'].is_a?(Float)
1433
+ arg2 = Atom.new(RealType.new(attributes['value']))
1434
+ elsif attributes['value'].is_a?(Integer)
1435
+ arg2 = Atom.new(IntegerType.new(attributes['value']))
1436
+ elsif attributes['value'].is_a?(String)
1437
+ arg2 = Atom.new(StringType.new(attributes['value']))
1438
+ else
1439
+ raise "value is not a real, integer or string"
1440
+ end
1441
+
1442
+ return ComparisonPred.new(arg1, attributes['comparison'], arg2)
1443
+ end
1444
+
1445
+ def match_attributtes(attributes)
1446
+ return true if self.arg1.table == attributes['table'] and
1447
+ self.arg1.name == attributes['name'] and
1448
+ self.comparison.value == attributes['comparison'] and
1449
+ self.arg2.literal.value == attributes['value']
1450
+ return false
1451
+ end
1452
+
1453
+ # This method finds a condition given its attributes and it returns it
1454
+ def find_condition(type, attributes)
1455
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1456
+ return self if self.match_attributtes(attributes)
1457
+ return nil
1458
+ end
1459
+ return nil
1460
+ end
1461
+
1462
+ # This method removes a condition. -1 means that the condition must
1463
+ # be removed. 1 means that the condition given its attributes wasn't
1464
+ # found, so that the process must continue.
1465
+ def remove_condition(type, attributes)
1466
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1467
+ return -1 if self.match_attributtes(attributes)
1468
+ return 1
1469
+ end
1470
+ return 1
1471
+ end
1472
+
1473
+ # This method modifies a condition given its old attributes,
1474
+ # replacing the old attributes with the new attributes.
1475
+ def modify_condition(type, attributes_old, attributes_new)
1476
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1477
+ if self.match_attributtes(attributes_old)
1478
+ return ComparisonPred.create_new_object(attributes_new)
1479
+ else
1480
+ return nil
1481
+ end
1482
+ end
1483
+ return nil
1484
+ end
1485
+
1486
+ # This method replaces a condition given its attributes.
1487
+ # Returns -1 if the object must be replaced, or returns 1 if
1488
+ # isn't the object that must be replaced, so the process must continue
1489
+ def replace_condition(type, attributes, new_condition)
1490
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1491
+ return -1 if self.match_attributtes(attributes)
1492
+ return 1
1493
+ end
1494
+ return 1
1495
+ end
1496
+ end
1497
+
1498
+ # Represents the Between expression of a query.
1499
+ class BetweenPred < Search
1500
+ attr_reader :arg1, :arg2, :arg3
1501
+
1502
+ def initialize(arg1, arg2, arg3)
1503
+ self.arg1 = arg1
1504
+ self.arg2 = arg2
1505
+ self.arg3 = arg3
1506
+ end
1507
+
1508
+ def arg1=(a)
1509
+ begin
1510
+ VOTables::VOTable::Misc::TypeCheck.new(a, ScalarExpression).check()
1511
+ rescue VOTables::VOTable::Misc::TypeException
1512
+ @arg1 = ScalarExpression.new(a)
1513
+ else
1514
+ @arg1 = a
1515
+ end
1516
+ end
1517
+
1518
+ def arg2=(a)
1519
+ begin
1520
+ VOTables::VOTable::Misc::TypeCheck.new(a, ScalarExpression).check()
1521
+ rescue VOTables::VOTable::Misc::TypeException
1522
+ @arg2 = ScalarExpression.new(a)
1523
+ else
1524
+ @arg2 = a
1525
+ end
1526
+ end
1527
+
1528
+ def arg3=(a)
1529
+ begin
1530
+ VOTables::VOTable::Misc::TypeCheck.new(a, ScalarExpression).check()
1531
+ rescue VOTables::VOTable::Misc::TypeException
1532
+ @arg3 = ScalarExpression.new(a)
1533
+ else
1534
+ @arg3 = a
1535
+ end
1536
+ end
1537
+
1538
+ def to_s
1539
+ "{arg1=#{self.arg1},arg2=#{self.arg2},arg3=#{self.arg3}}"
1540
+ end
1541
+
1542
+ def self.from_xml(node)
1543
+ arg1_node, arg2_node, arg3_node = node.elements.to_a('Arg')
1544
+ arg1 = Arg.from_xml(arg1_node)
1545
+ arg2 = Arg.from_xml(arg2_node)
1546
+ arg3 = Arg.from_xml(arg3_node)
1547
+
1548
+ return BetweenPred.new(arg1, arg2, arg3)
1549
+ end
1550
+
1551
+ def self.create_new_object(attributes)
1552
+ arg1 = ColumnReference.new(attributes['table'], attributes['name'], nil)
1553
+
1554
+ arg2 = nil
1555
+ arg3 = nil
1556
+ if attributes['value_min'].is_a?(Float) and attributes['value_max'].is_a?(Float)
1557
+ arg2 = Atom.new(RealType.new(attributes['value_min']))
1558
+ arg3 = Atom.new(RealType.new(attributes['value_max']))
1559
+ elsif attributes['value_min'].is_a?(Integer) and attributes['value_max'].is_a?(Integer)
1560
+ arg2 = Atom.new(IntegerType.new(attributes['value_min']))
1561
+ arg3 = Atom.new(IntegerType.new(attributes['value_max']))
1562
+ else
1563
+ raise "value is not a real or integer"
1564
+ end
1565
+
1566
+ return BetweenPred.new(arg1, arg2, arg3)
1567
+ end
1568
+
1569
+ def match_attributtes(attributes)
1570
+ return true if self.arg1.table == attributes['table'] and
1571
+ self.arg1.name == attributes['name'] and
1572
+ self.arg2.literal.value == attributes['value_min'] and
1573
+ self.arg3.literal.value == attributes['value_max']
1574
+ return false
1575
+ end
1576
+
1577
+ # This method finds a condition given its attributes and it returns it
1578
+ def find_condition(type, attributes)
1579
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1580
+ return self if self.match_attributtes(attributes)
1581
+ return nil
1582
+ end
1583
+ return nil
1584
+ end
1585
+
1586
+ # This method removes a condition. -1 means that the condition must
1587
+ # be removed. 1 means that the condition given its attributes wasn't
1588
+ # found, so that the process must continue.
1589
+ def remove_condition(type, attributes)
1590
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1591
+ return -1 if self.match_attributtes(attributes)
1592
+ return 1
1593
+ end
1594
+ return 1
1595
+ end
1596
+
1597
+ # This method modifies a condition given its old attributes,
1598
+ # replacing the old attributes with the new attributes.
1599
+ def modify_condition(type, attributes_old, attributes_new)
1600
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1601
+ if self.match_attributtes(attributes_old)
1602
+ return BetweenPred.create_new_object(attributes_new)
1603
+ else
1604
+ return nil
1605
+ end
1606
+ end
1607
+ return nil
1608
+ end
1609
+
1610
+ # This method replaces a condition given its attributes.
1611
+ # Returns -1 if the object must be replaced, or returns 1 if
1612
+ # isn't the object that must be replaced, so the process must continue
1613
+ def replace_condition(type, attributes, new_condition)
1614
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1615
+ return -1 if self.match_attributtes(attributes)
1616
+ return 1
1617
+ end
1618
+ return 1
1619
+ end
1620
+ end
1621
+
1622
+ # Represents expressions like Not A.
1623
+ class NotBetweenPred < BetweenPred
1624
+ def self.from_xml(node)
1625
+ arg1_node, arg2_node, arg3_node = node.elements.to_a('Arg')
1626
+ arg1 = Arg.from_xml(arg1_node)
1627
+ arg2 = Arg.from_xml(arg2_node)
1628
+ arg3 = Arg.from_xml(arg3_node)
1629
+ return NotBetweenPred.new(arg1, arg2, arg3)
1630
+ end
1631
+ end
1632
+
1633
+ # Abstract Shape type. Shape definitions; Allsky, Circle, Polygon,
1634
+ # Box, and Sector are derived from Shape; Ellipse is derived from
1635
+ # Circle; Polygon includes also Vertex and SmallCircle
1636
+ class Shape
1637
+ def self.from_xml(node)
1638
+ type_s = node.find_attribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
1639
+ type_s = type_s.slice(type_s.index(':')+1, type_s.length)
1640
+ return ObjectBuilder.get_class_for(type_s).from_xml(node)
1641
+ end
1642
+ end
1643
+
1644
+ # Circle shape. The circle is defined by a center and a radius
1645
+ class Circle < Shape
1646
+ attr_reader :ra, :dec, :radius, :system, :shape
1647
+
1648
+ def initialize(ra, dec, radius, system='J2000')
1649
+ self.ra = ra
1650
+ self.dec = dec
1651
+ self.radius = radius
1652
+ self.system = system
1653
+ self.shape = 'CIRCLE'
1654
+ end
1655
+
1656
+ def ra=(ra)
1657
+ begin
1658
+ VOTables::VOTable::Misc::TypeCheck.new(ra, RealType).check()
1659
+ rescue VOTables::VOTable::Misc::TypeException
1660
+ @ra = RealType.new(ra)
1661
+ else
1662
+ @ra = ra
1663
+ end
1664
+ end
1665
+
1666
+ def dec=(dec)
1667
+ begin
1668
+ VOTables::VOTable::Misc::TypeCheck.new(dec, RealType).check()
1669
+ rescue VOTables::VOTable::Misc::TypeException
1670
+ @dec = RealType.new(dec)
1671
+ else
1672
+ @dec = dec
1673
+ end
1674
+ end
1675
+
1676
+ def radius=(radius)
1677
+ begin
1678
+ VOTables::VOTable::Misc::TypeCheck.new(radius, RealType).check()
1679
+ rescue VOTables::VOTable::Misc::TypeException
1680
+ @radius = RealType.new(radius)
1681
+ else
1682
+ @radius = radius
1683
+ end
1684
+ end
1685
+
1686
+ def system=(sys)
1687
+ begin
1688
+ VOTables::VOTable::Misc::TypeCheck.new(sys, StringType).check()
1689
+ rescue VOTables::VOTable::Misc::TypeException
1690
+ @system = StringType.new(sys)
1691
+ else
1692
+ @system = sys
1693
+ end
1694
+ end
1695
+
1696
+ def shape=(sh)
1697
+ begin
1698
+ VOTables::VOTable::Misc::TypeCheck.new(sh, StringType).check()
1699
+ rescue VOTables::VOTable::Misc::TypeException
1700
+ @shape = StringType.new(sh)
1701
+ else
1702
+ @shape = sh
1703
+ end
1704
+ end
1705
+
1706
+ def to_s
1707
+ "{shape=#{self.shape},system=#{self.system},ra=#{self.ra},dec=#{self.dec},radius=#{self.radius}}"
1708
+ end
1709
+
1710
+ def self.from_xml(node)
1711
+ unit = node.attributes['unit'] || 'deg'
1712
+
1713
+ center = REXML::XPath.first(node, 'reg:Center', {'reg' => 'http://www.ivoa.net/xml/STC/STCregion/v1.10'}).text
1714
+ ra_s, dec_s = center.split(/\s+/)
1715
+ ra = RealType.new(ra_s.to_f)
1716
+ dec = RealType.new(dec_s.to_f)
1717
+
1718
+ radius_s = REXML::XPath.first(node, 'reg:Radius', {'reg' => 'http://www.ivoa.net/xml/STC/STCregion/v1.10'}).text
1719
+ radius = RealType.new(radius_s.to_f)
1720
+
1721
+ return Circle.new(ra, dec, radius)
1722
+ end
1723
+
1724
+ def self.create_new_object(attributes)
1725
+ ra = RealType.new(attributes['ra'])
1726
+ dec = RealType.new(attributes['dec'])
1727
+ radius = RealType.new(attributes['radius'])
1728
+
1729
+ return Circle.new(ra, dec, radius)
1730
+ end
1731
+
1732
+ def match_attributtes(attributes)
1733
+ return true if self.ra.value == attributes['ra'] and
1734
+ self.dec.value == attributes['dec'] and
1735
+ self.radius.value == attributes['radius']
1736
+ return false
1737
+ end
1738
+ end
1739
+
1740
+ # Box shape. The box is defined by a center and a size
1741
+ class Box < Shape
1742
+ attr_reader :ra, :dec, :dra, :ddec, :system, :shape
1743
+
1744
+ def initialize(ra, dec, dra, ddec, system='J2000')
1745
+ self.ra = ra
1746
+ self.dec = dec
1747
+ self.dra = dra#deltha RA
1748
+ self.ddec = ddec#deltha DEC
1749
+ self.system = system
1750
+ self.shape = 'BOX'
1751
+ end
1752
+
1753
+ def ra=(ra)
1754
+ begin
1755
+ VOTables::VOTable::Misc::TypeCheck.new(ra, RealType).check()
1756
+ rescue VOTables::VOTable::Misc::TypeException
1757
+ @ra = RealType.new(ra)
1758
+ else
1759
+ @ra = ra
1760
+ end
1761
+ end
1762
+
1763
+ def dec=(dec)
1764
+ begin
1765
+ VOTables::VOTable::Misc::TypeCheck.new(dec, RealType).check()
1766
+ rescue VOTables::VOTable::Misc::TypeException
1767
+ @dec = RealType.new(dec)
1768
+ else
1769
+ @dec = dec
1770
+ end
1771
+ end
1772
+
1773
+ def dra=(dra)
1774
+ begin
1775
+ VOTables::VOTable::Misc::TypeCheck.new(dra, RealType).check()
1776
+ rescue VOTables::VOTable::Misc::TypeException
1777
+ @dra = RealType.new(dra)
1778
+ else
1779
+ @dra = dra
1780
+ end
1781
+ end
1782
+
1783
+ def ddec=(ddec)
1784
+ begin
1785
+ VOTables::VOTable::Misc::TypeCheck.new(ddec, RealType).check()
1786
+ rescue VOTables::VOTable::Misc::TypeException
1787
+ @ddec = RealType.new(ddec)
1788
+ else
1789
+ @ddec = ddec
1790
+ end
1791
+ end
1792
+
1793
+ def system=(sys)
1794
+ begin
1795
+ VOTables::VOTable::Misc::TypeCheck.new(sys, StringType).check()
1796
+ rescue VOTables::VOTable::Misc::TypeException
1797
+ @system = StringType.new(sys)
1798
+ else
1799
+ @system = sys
1800
+ end
1801
+ end
1802
+
1803
+ def shape=(sh)
1804
+ begin
1805
+ VOTables::VOTable::Misc::TypeCheck.new(sh, StringType).check()
1806
+ rescue VOTables::VOTable::Misc::TypeException
1807
+ @shape = StringType.new(sh)
1808
+ else
1809
+ @shape = sh
1810
+ end
1811
+ end
1812
+
1813
+ def to_s
1814
+ "{shape=#{self.shape},system=#{self.system},ra=#{self.ra}," +
1815
+ "dec=#{self.dec},dra=#{self.dra},ddec=#{self.ddec}}"
1816
+ end
1817
+
1818
+ def self.from_xml(node)
1819
+ unit = node.attributes['unit'] || 'deg'
1820
+
1821
+ center = REXML::XPath.first(node, 'reg:Center').text
1822
+ ra_s, dec_s = center.split(/\s+/)
1823
+ ra = RealType.new(ra_s.to_f)
1824
+ dec = RealType.new(dec_s.to_f)
1825
+
1826
+ size = REXML::XPath.first(node, 'reg:Size').text
1827
+ dra_s, ddec_s = size.split(/\s+/)
1828
+ dra = RealType.new(dra_s.to_f)
1829
+ ddec = RealType.new(ddec_s.to_f)
1830
+
1831
+ return Box.new(ra, dec, dra, ddec)
1832
+ end
1833
+
1834
+ def self.create_new_object(attributes)
1835
+ ra = RealType.new(attributes['ra'])
1836
+ dec = RealType.new(attributes['dec'])
1837
+ dra = RealType.new(attributes['dra'])
1838
+ ddec = RealType.new(attributes['ddec'])
1839
+
1840
+ return Box.new(ra, dec, dra, ddec)
1841
+ end
1842
+
1843
+ def match_attributtes(attributes)
1844
+ return true if self.ra.value == attributes['ra'] and
1845
+ self.dec.value == attributes['dec'] and
1846
+ self.dra.value == attributes['dra'] and
1847
+ self.ddec.value == attributes['ddec']
1848
+ return false
1849
+ end
1850
+ end
1851
+
1852
+ # Represents the Regions such as circle in Where clause.
1853
+ class RegionSearch < Search
1854
+ attr_reader :shape, :intersection
1855
+
1856
+ def initialize(shape, intersection='overlaps')
1857
+ self.shape = shape
1858
+ self.intersection = intersection
1859
+ end
1860
+
1861
+ def shape=(s)
1862
+ VOTables::VOTable::Misc::TypeCheck.new(s, Shape).check()
1863
+ @shape = s
1864
+ end
1865
+
1866
+ def intersection=(i)
1867
+ begin
1868
+ VOTables::VOTable::Misc::TypeCheck.new(i, StringType).check()
1869
+ rescue VOTables::VOTable::Misc::TypeException
1870
+ @intersection = StringType.new(i)
1871
+ else
1872
+ @intersection = i
1873
+ end
1874
+ end
1875
+
1876
+ def to_s
1877
+ "{region={intersection=#{self.intersection},shape=#{self.shape}}}"
1878
+ end
1879
+
1880
+ def self.from_xml(node)
1881
+ inter = node.attributes['intersection'].to_s
1882
+ region_node = REXML::XPath.first(node, 'Region')
1883
+ sh = Shape.from_xml(region_node)
1884
+ return RegionSearch.new(sh, inter)
1885
+ end
1886
+
1887
+ def self.create_new_object(attributes)
1888
+ sh = ObjectBuilder.get_class_for(attributes['shape_type']).create_new_object(attributes)
1889
+ return RegionSearch.new(sh, attributes['intersection'].to_s)
1890
+ end
1891
+
1892
+ def match_attributtes(attributes)
1893
+ return self.shape.match_attributtes(attributes)
1894
+ end
1895
+
1896
+ # This method finds a condition given its attributes and it returns it
1897
+ def find_condition(type, attributes)
1898
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1899
+ return self if self.match_attributtes(attributes)
1900
+ return nil
1901
+ end
1902
+ return nil
1903
+ end
1904
+
1905
+ # This method removes a condition. -1 means that the condition must
1906
+ # be removed. 1 means that the condition given its attributes wasn't
1907
+ # found, so that the process must continue.
1908
+ def remove_condition(type, attributes)
1909
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1910
+ return -1 if self.match_attributtes(attributes)
1911
+ return 1
1912
+ end
1913
+ return 1
1914
+ end
1915
+
1916
+ # This method modifies a condition given its old attributes,
1917
+ # replacing the old attributes with the new attributes.
1918
+ def modify_condition(type, attributes_old, attributes_new)
1919
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1920
+ if self.match_attributtes(attributes_old)
1921
+ return RegionSearch.create_new_object(attributes_new)
1922
+ else
1923
+ return nil
1924
+ end
1925
+ end
1926
+ return nil
1927
+ end
1928
+
1929
+ # This method replaces a condition given its attributes.
1930
+ # Returns -1 if the object must be replaced, or returns 1 if
1931
+ # isn't the object that must be replaced, so the process must continue
1932
+ def replace_condition(type, attributes, new_condition)
1933
+ if ObjectBuilder.get_class_for(type).to_s() == self.class.to_s()
1934
+ return -1 if self.match_attributtes(attributes)
1935
+ return 1
1936
+ end
1937
+ return 1
1938
+ end
1939
+ end
1940
+
1941
+ # Represents expressions like Not A.
1942
+ class InverseSearch < Search
1943
+ attr_reader :condition
1944
+
1945
+ def initialize(condition)
1946
+ self.condition = condition
1947
+ end
1948
+
1949
+ def condition=(c)
1950
+ VOTables::VOTable::Misc::TypeCheck.new(c, Search).check()
1951
+ @condition = c
1952
+ end
1953
+
1954
+ def to_s
1955
+ "{condition=#{self.condition}}"
1956
+ end
1957
+
1958
+ def self.from_xml(node)
1959
+ cond_node = REXML::XPath.first(node, 'Condition')
1960
+ cond = Condition.from_xml(cond_node)
1961
+ return InverseSearch.new(cond)
1962
+ end
1963
+ end
1964
+
1965
+ # Represents the Having expression part of a query.
1966
+ class Having
1967
+ attr_reader :condition
1968
+
1969
+ def initialize(condition)
1970
+ self.condition = condition
1971
+ end
1972
+
1973
+ def condition=(c)
1974
+ VOTables::VOTable::Misc::TypeCheck.new(c, Search).check()
1975
+ @condition = c
1976
+ end
1977
+
1978
+ def to_s
1979
+ "{condition=#{self.condition}}"
1980
+ end
1981
+
1982
+ def self.from_xml(node)
1983
+ cond_node = REXML::XPath.first(node, 'Condition')
1984
+ cond = Condition.from_xml(cond_node)
1985
+ return Having.new(cond)
1986
+ end
1987
+ end
1988
+
1989
+ # Represents the Group By expression part of a query.
1990
+ class GroupBy
1991
+ attr_reader :columns
1992
+
1993
+ def initialize(columns)
1994
+ self.columns = columns
1995
+ end
1996
+
1997
+ def columns=(c)
1998
+ raise "Provide at least one condition" if c.size < 1
1999
+
2000
+ @columns = []
2001
+ c.each do |col|
2002
+ VOTables::VOTable::Misc::TypeCheck.new(col, ColumnReference).check()
2003
+ @columns.push(col)
2004
+ end
2005
+ end
2006
+
2007
+ def to_s
2008
+ cols = self.columns.collect{|x| x.to_s}.join('|')
2009
+ "{columns=#{cols}}"
2010
+ end
2011
+
2012
+ def self.from_xml(node)
2013
+ columns = []
2014
+ node.elements.each('Column') do |col_node|
2015
+ col = Column.from_xml(col_node)
2016
+ columns.push(col)
2017
+ end
2018
+
2019
+ return GroupBy.new(columns)
2020
+ end
2021
+ end
2022
+
2023
+ # Represents the Where part of the query.
2024
+ class Where
2025
+ attr_reader :condition
2026
+
2027
+ def initialize(condition)
2028
+ self.condition = condition
2029
+ end
2030
+
2031
+ def condition=(c)
2032
+ VOTables::VOTable::Misc::TypeCheck.new(c, Search).check()
2033
+ @condition = c
2034
+ end
2035
+
2036
+ def to_s
2037
+ "{condition=#{self.condition}}"
2038
+ end
2039
+
2040
+ def self.from_xml(node)
2041
+ cond_node = REXML::XPath.first(node, 'Condition')
2042
+ cond = Condition.from_xml(cond_node)
2043
+ return Where.new(cond)
2044
+ end
2045
+
2046
+ # This method finds a condition given its attributes and it returns it
2047
+ def find_condition(type, attributes)
2048
+ return self.condition.find_condition(type, attributes)
2049
+ end
2050
+
2051
+ # This method removes a condition. -1 means that the condition must
2052
+ # be removed. 1 means that the condition given its attributes wasn't
2053
+ # found. The other case is that the condition was changed in another
2054
+ # place, for example could have been removed in the intersectionSearchType
2055
+ # called.
2056
+ def remove_condition(type, attributes)
2057
+ condition = self.condition.remove_condition(type, attributes)
2058
+ if condition == -1
2059
+ self.condition = nil
2060
+ elsif condition != 1
2061
+ self.condition = condition
2062
+ end
2063
+ end
2064
+
2065
+ # This method modify a condition given its old attributtes,
2066
+ # replacing the old attributes with the new attributes.
2067
+ def modify_condition(type, attributes_old, attributes_new)
2068
+ self.condition = self.condition.modify_condition(type, attributes_old, attributes_new)
2069
+ end
2070
+
2071
+ # This method replaces a condition for a new condition. -1 means that
2072
+ # the condition must be replaced. 1 means that the old condition in
2073
+ # this case wasn't found so that the old condition won't be replaced.
2074
+ # The other case is that the condition was changed in another place,
2075
+ # for example could have been replaced in the intersectionSearchType
2076
+ # called.
2077
+ def replace_condition(type, attributes, new_condition)
2078
+ condition = self.condition.replace_condition(type, attributes, new_condition)
2079
+ if condition == -1
2080
+ self.condition = new_condition
2081
+ elsif condition != 1
2082
+ self.condition = condition
2083
+ end
2084
+ end
2085
+ end
2086
+
2087
+ # Represents the From part of the query.
2088
+ class From
2089
+ attr_reader :tables
2090
+
2091
+ def initialize(tables)
2092
+ self.tables = tables
2093
+ end
2094
+
2095
+ def tables=(ts)
2096
+ raise "Specify at least one table" if ts.size < 1
2097
+
2098
+ @tables = []
2099
+ ts.each do |t|
2100
+ VOTables::VOTable::Misc::TypeCheck.new(t, FromTable).check()
2101
+ @tables.push(t)
2102
+ end
2103
+ end
2104
+
2105
+ def to_s
2106
+ tables = self.tables.collect{|x| x.to_s}.join('|')
2107
+ "{tables=#{tables}}"
2108
+ end
2109
+
2110
+ def self.from_xml(node)
2111
+ table_list = []
2112
+ node.elements.each("Table") do |tbl_node|
2113
+ table = Table.from_xml(tbl_node)
2114
+ table_list.push(table)
2115
+ end
2116
+
2117
+ return From.new(table_list)
2118
+ end
2119
+ end
2120
+
2121
+ # List of items to be selected in the Query.
2122
+ class SelectionList
2123
+ attr_reader :items
2124
+
2125
+ def initialize(items)
2126
+ self.items = items
2127
+ end
2128
+
2129
+ def items=(is)
2130
+ raise "Specify at least one item" if is.size < 1
2131
+
2132
+ @items = []
2133
+ is.each do |i|
2134
+ VOTables::VOTable::Misc::TypeCheck.new(i, SelectionItem).check()
2135
+ @items.push(i)
2136
+ end
2137
+ end
2138
+
2139
+ def to_s
2140
+ items = self.items.collect{|x| x.to_s}.join('|')
2141
+ return "{items=#{items}}"
2142
+ end
2143
+
2144
+ def self.from_xml(node)
2145
+ item_list = []
2146
+ node.elements.each('Item') do |item_node|
2147
+ item_list.push(Item.from_xml(item_node))
2148
+ end
2149
+
2150
+ return SelectionList.new(item_list)
2151
+ end
2152
+
2153
+ def add(attributes)
2154
+ new_item = nil
2155
+ if attributes['name'] == AllSelectionItem.value
2156
+ new_item = AllSelectionItem.new()
2157
+ else
2158
+ new_item = ColumnReference.new(attributes['table'],
2159
+ attributes['name'], attributes['xpath_name'])
2160
+ end
2161
+ self.items.push(new_item) if new_item and find(attributes) == nil
2162
+ end
2163
+
2164
+ def remove(attributes)
2165
+ remove_item = find(attributes)
2166
+ self.items.delete(remove_item) if remove_item != nil
2167
+ end
2168
+
2169
+ def find(attributes)
2170
+ self.items().each do |item|
2171
+ if !item.is_a?(AllSelectionItem)
2172
+ return item if item.name() == attributes['name'] and item.table() == attributes['table']
2173
+ elsif item.is_a?(AllSelectionItem)
2174
+ return item if AllSelectionItem.value == attributes['name']
2175
+ end
2176
+ end
2177
+ return nil
2178
+ end
2179
+
2180
+ def is_empty
2181
+ self.items().empty?
2182
+ end
2183
+ end
2184
+
2185
+ # Represents the TOP part of a query.
2186
+ class SelectionLimit
2187
+ attr_reader :top
2188
+
2189
+ def initialize(top)
2190
+ self.top = top
2191
+ end
2192
+
2193
+ def top=(t)
2194
+ raise "Provide an integer > 0" if t < 1 or !t.is_a?(Integer)
2195
+ @top = t
2196
+ end
2197
+
2198
+ def to_s
2199
+ "{top=#{self.top}}"
2200
+ end
2201
+
2202
+ def self.from_xml(node)
2203
+ top = node.attributes['Top'].to_i
2204
+ return SelectionLimit.new(top)
2205
+ end
2206
+ end
2207
+
2208
+ # Represents the SQL INTO expression.
2209
+ class Into
2210
+ attr_accessor :table_name
2211
+
2212
+ def initialize(name)
2213
+ self.table_name = name
2214
+ end
2215
+
2216
+ def to_s
2217
+ "{table_name=#{self.table_name}}"
2218
+ end
2219
+
2220
+ def self.from_xml(node)
2221
+ table_name_node = REXML::XPath.first(node, 'TableName')
2222
+ table_name = TableName.from_xml(table_name_node)
2223
+ return Into.new(table_name)
2224
+ end
2225
+ end
2226
+
2227
+ # Ascending or Descending order of an Order by term.
2228
+ class OrderDirection
2229
+ attr_reader :value
2230
+
2231
+ @@options = ['ASC', 'DESC']
2232
+
2233
+ def initialize(value, options_list=nil)
2234
+ @options_list = options_list || @@options
2235
+ self.value = value
2236
+ end
2237
+
2238
+ def value=(v)
2239
+ if @options_list.include?(v)
2240
+ @value = v
2241
+ else
2242
+ raise "Value is not valid. Use one of: " +
2243
+ @options_list.join(', ')
2244
+ end
2245
+ end
2246
+
2247
+ def to_s
2248
+ "{value=#{self.value}}"
2249
+ end
2250
+ end
2251
+
2252
+ # Option for setting the direction for Order By.
2253
+ class OrderOption
2254
+ attr_reader :direction
2255
+
2256
+ def initialize(direction)
2257
+ self.direction = direction
2258
+ end
2259
+
2260
+ def direction=(d)
2261
+ begin
2262
+ VOTables::VOTable::Misc::TypeCheck.new(d, OrderDirection).check()
2263
+ rescue VOTables::VOTable::Misc::TypeException
2264
+ @direction = OrderDirection.new(d)
2265
+ else
2266
+ @direction = d
2267
+ end
2268
+ end
2269
+
2270
+ def to_s
2271
+ "{direction=#{self.direction}}"
2272
+ end
2273
+
2274
+ def self.from_xml(node)
2275
+ return OrderOption.new(node.attributes['Direction'])
2276
+ end
2277
+ end
2278
+
2279
+ # Represents the ORDER BY part of a query.
2280
+ class Order
2281
+ attr_reader :expression, :order
2282
+
2283
+ def initialize(expression, order=nil)
2284
+ self.expression = expression
2285
+ self.order = order
2286
+ end
2287
+
2288
+ def expression=(e)
2289
+ VOTables::VOTable::Misc::TypeCheck.new(e, ScalarExpression).check()
2290
+ @expression = e
2291
+ end
2292
+
2293
+ def order=(o)
2294
+ begin
2295
+ VOTables::VOTable::Misc::TypeCheck.new(o, OrderOption).check()
2296
+ rescue VOTables::VOTable::Misc::TypeException
2297
+ @order = OrderOption.new(o)
2298
+ else
2299
+ @order = o
2300
+ end
2301
+ end
2302
+
2303
+ def to_s
2304
+ "{expression=#{self.expression},order=#{self.order}}"
2305
+ end
2306
+
2307
+ def self.from_xml(node)
2308
+ expr_node = REXML::XPath.first(node, 'Expression')
2309
+ expr = Expression.from_xml(expr_node)
2310
+ order_option_node = REXML::XPath.first(node, 'Order')
2311
+ order_option = OrderOption.from_xml(order_option_node)
2312
+ return Order.new(expr, order_option)
2313
+ end
2314
+ end
2315
+
2316
+ # List of expressions in which order the results should be provided.
2317
+ class OrderExpression
2318
+ attr_reader :items
2319
+
2320
+ def initialize(items)
2321
+ self.items = items
2322
+ end
2323
+
2324
+ def items=(is)
2325
+ raise "Specify at least one item" if is.size < 1
2326
+
2327
+ @items = []
2328
+ is.each do |i|
2329
+ VOTables::VOTable::Misc::TypeCheck.new(i, Order).check()
2330
+ @items.push(i)
2331
+ end
2332
+ end
2333
+
2334
+ def to_s
2335
+ items = self.items.collect{|x| x.to_s}.join('|')
2336
+ "{items=#{items}}"
2337
+ end
2338
+
2339
+ def self.from_xml(node)
2340
+ items = []
2341
+ node.elements.each('Item') do |item_node|
2342
+ item = Item.from_xml(item_node)
2343
+ items.push(item)
2344
+ end
2345
+
2346
+ return OrderExpression.new(items)
2347
+ end
2348
+ end
2349
+
2350
+ # Represents a list of constants provided for a SQL IN expression.
2351
+ class ConstantListSet < InclusionSet
2352
+ attr_reader :items
2353
+
2354
+ def initialize(items)
2355
+ self.items = items
2356
+ end
2357
+
2358
+ def items=(is)
2359
+ raise "Specify at least one item" if is.size < 1
2360
+
2361
+ @items = []
2362
+ is.each do |i|
2363
+ begin
2364
+ VOTables::VOTable::Misc::TypeCheck.new(i, LiteralType).check()
2365
+ rescue VOTables::VOTable::Misc::TypeException
2366
+ if i.is_a?(String)
2367
+ @items.push(StringType.new(i))
2368
+ elsif i.is_a?(Integer)
2369
+ @items.push(IntegerType.new(i))
2370
+ elsif i.is_a?(Float)
2371
+ @items.push(RealType.new(i))
2372
+ else
2373
+ raise "Item must be of type LiteralType"
2374
+ end
2375
+ else
2376
+ @items.push(i)
2377
+ end
2378
+ end
2379
+ end
2380
+
2381
+ def to_s
2382
+ items = self.items.collect{|x| x.to_s}.join('|')
2383
+ "{items=#{items}}"
2384
+ end
2385
+
2386
+ def self.from_xml(node)
2387
+ item_list = []
2388
+ node.elements.each('Item') do |item_node|
2389
+ item = Item.from_xml(item_node)
2390
+ item_list.push(Item.from_xml(item_node))
2391
+ end
2392
+
2393
+ return ConstantListSet.new(item_list)
2394
+ end
2395
+ end
2396
+
2397
+ # Represents SQL IN expression.
2398
+ class InclusiveSearch < Search
2399
+ attr_reader :expression, :set
2400
+
2401
+ def initialize(expression, set)
2402
+ self.expression = expression
2403
+ self.set = set
2404
+ end
2405
+
2406
+ def expression=(e)
2407
+ VOTables::VOTable::Misc::TypeCheck.new(e, ScalarExpression).check()
2408
+ @expression = e
2409
+ end
2410
+
2411
+ def set=(s)
2412
+ VOTables::VOTable::Misc::TypeCheck.new(s, InclusionSet).check()
2413
+ @set = s
2414
+ end
2415
+
2416
+ def to_s
2417
+ "{expression=#{self.expression},set=#{self.set}}"
2418
+ end
2419
+
2420
+ def self.from_xml(node)
2421
+ expr_node = REXML::XPath.first(node, 'Expression')
2422
+ expr = Expression.from_xml(expr_node)
2423
+ set_node = REXML::XPath.first(node, 'Set')
2424
+ set = Set.from_xml(set_node)
2425
+ return InclusiveSearch.new(expr, set)
2426
+ end
2427
+ end
2428
+
2429
+ # The SELECT part of a query.
2430
+ class Select
2431
+ attr_reader :allow, :restrict, :selection_list, :in_to, :from,
2432
+ :where, :group_by, :having, :order_by
2433
+ attr_accessor :start_comment, :end_comment
2434
+
2435
+ def initialize(selection_list, allow=nil, restrict=nil, in_to=nil,
2436
+ from=nil, where=nil, group_by=nil, having=nil, order_by=nil,
2437
+ start_comment=nil, end_comment=nil)
2438
+ self.selection_list = selection_list
2439
+ self.allow = allow
2440
+ self.restrict = restrict
2441
+ self.in_to = in_to
2442
+ self.from = from
2443
+ self.where = where
2444
+ self.group_by = group_by
2445
+ self.having = having
2446
+ self.order_by = order_by
2447
+ self.start_comment = start_comment
2448
+ self.end_comment = end_comment
2449
+ end
2450
+
2451
+ def selection_list=(sl)
2452
+ VOTables::VOTable::Misc::TypeCheck.new(sl, SelectionList).check()
2453
+ @selection_list = sl
2454
+ end
2455
+
2456
+ def allow=(a)
2457
+ begin
2458
+ VOTables::VOTable::Misc::TypeCheck.new(a, SelectionOption).check()
2459
+ rescue VOTables::VOTable::Misc::TypeException
2460
+ @allow = SelectionOption.new(a)
2461
+ else
2462
+ @allow = a
2463
+ end
2464
+ end
2465
+
2466
+ def restrict=(r)
2467
+ begin
2468
+ VOTables::VOTable::Misc::TypeCheck.new(r, SelectionLimit).check()
2469
+ rescue VOTables::VOTable::Misc::TypeException
2470
+ @restrict = SelectionLimit.new(r)
2471
+ else
2472
+ @restrict = r
2473
+ end
2474
+ end
2475
+
2476
+ def in_to=(it)
2477
+ begin
2478
+ VOTables::VOTable::Misc::TypeCheck.new(it, Into).check()
2479
+ rescue VOTables::VOTable::Misc::TypeException
2480
+ @in_to = Into.new(it)
2481
+ else
2482
+ @in_to = it
2483
+ end
2484
+ end
2485
+
2486
+ def from=(f)
2487
+ VOTables::VOTable::Misc::TypeCheck.new(f, From).check()
2488
+ @from = f
2489
+ end
2490
+
2491
+ def where=(w)
2492
+ VOTables::VOTable::Misc::TypeCheck.new(w, Where).check()
2493
+ @where = w
2494
+ end
2495
+
2496
+ def group_by=(gb)
2497
+ VOTables::VOTable::Misc::TypeCheck.new(gb, GroupBy).check()
2498
+ @group_by = gb
2499
+ end
2500
+
2501
+ def having=(h)
2502
+ VOTables::VOTable::Misc::TypeCheck.new(h, Having).check()
2503
+ @having = h
2504
+ end
2505
+
2506
+ def order_by=(ob)
2507
+ VOTables::VOTable::Misc::TypeCheck.new(ob, OrderExpression).check()
2508
+ @order_by = ob
2509
+ end
2510
+
2511
+ def to_s
2512
+ "{allow=#{self.allow},restrict=#{self.restrict},selection_list=#{self.selection_list}," +
2513
+ "in_to=#{self.in_to},from=#{self.from},where=#{self.where},group_by=#{self.group_by}," +
2514
+ "having=#{self.having},order_by=#{self.order_by},start_comment=#{self.start_comment}," +
2515
+ "end_comment=#{self.end_comment}}"
2516
+ end
2517
+
2518
+ def to_file(file_path)
2519
+ file = File.new(file_path, 'w')
2520
+ file.syswrite(self.to_adqlx)
2521
+ file.close()
2522
+ end
2523
+
2524
+ def self.from_xml(node)
2525
+ # SelectionList
2526
+ sl_node = REXML::XPath.first(node, 'SelectionList') or raise "No SelectionList element"
2527
+ selection_list = SelectionList.from_xml(sl_node)
2528
+
2529
+ # Allow
2530
+ allow_node = REXML::XPath.first(node, 'Allow')
2531
+ allow = nil
2532
+ allow = Allow.from_xml(allow_node) if allow_node
2533
+
2534
+ # Restrict
2535
+ restrict_node = REXML::XPath.first(node, 'Restrict')
2536
+ restrict = nil
2537
+ restrict = Restrict.from_xml(restrict_node) if restrict_node
2538
+
2539
+ # InTo
2540
+ into_node = REXML::XPath.first(node, 'InTo')
2541
+ into = nil
2542
+ into = InTo.from_xml(into_node) if into_node
2543
+
2544
+ # From
2545
+ from_node = REXML::XPath.first(node, 'From')
2546
+ from = nil
2547
+ from = From.from_xml(from_node) if from_node
2548
+
2549
+ # Where
2550
+ where_node = REXML::XPath.first(node, 'Where')
2551
+ where = nil
2552
+ where = Where.from_xml(where_node) if where_node
2553
+
2554
+ # GroupBy
2555
+ groupby_node = REXML::XPath.first(node, 'GroupBy')
2556
+ groupby = nil
2557
+ groupby = GroupBy.from_xml(groupby_node) if groupby_node
2558
+
2559
+ # Having
2560
+ having_node = REXML::XPath.first(node, 'Having')
2561
+ having = nil
2562
+ having = Having.from_xml(having_node) if having_node
2563
+
2564
+ # OrderBy
2565
+ orderby_node = REXML::XPath.first(node, 'OrderBy')
2566
+ orderby = nil
2567
+ orderby = OrderBy.from_xml(orderby_node) if orderby_node
2568
+
2569
+ # StartComment
2570
+ start_comment_node = REXML::XPath.first(node, 'StartComment')
2571
+ start_comment = nil
2572
+ start_comment = StartComment.from_xml(start_comment_node) if start_comment_node
2573
+
2574
+ # EndComment
2575
+ end_comment_node = REXML::XPath.first(node, 'EndComment')
2576
+ end_comment = nil
2577
+ end_comment = EndComment.from_xml(end_comment_node) if end_comment_node
2578
+
2579
+ return Select.new(selection_list, allow, restrict, into,
2580
+ from, where, groupby, having, orderby,
2581
+ start_comment, end_comment)
2582
+ end
2583
+ end
2584
+
2585
+ # Represents user defined function expressions.
2586
+ class UserDefinedFunction < ScalarExpression
2587
+ attr_accessor :name
2588
+ attr_reader :params
2589
+
2590
+ def initialize(name, params=nil)
2591
+ self.name = name
2592
+ self.params = params
2593
+ end
2594
+
2595
+ def params=(ps)
2596
+ if ps
2597
+ raise "Provide at least 1 parameter" if ps.size < 1
2598
+
2599
+ @params = []
2600
+ ps.each do |p|
2601
+ VOTables::VOTable::Misc::TypeCheck.new(p, ScalarExpression)
2602
+ @params.push(p)
2603
+ end
2604
+ end
2605
+ end
2606
+
2607
+ def to_s
2608
+ params = self.params.collect{|x| x.to_s}.join('|')
2609
+ "{name=#{self.name},params=#{params}}"
2610
+ end
2611
+
2612
+ def self.from_xml(node)
2613
+ name = REXML::XPath.first(node, 'Name').text
2614
+ params = []
2615
+ node.elements.each('Params') do |param_node|
2616
+ params.push(ScalarExpression.from_xml(param_node))
2617
+ end
2618
+ params = nil if params.size < 1
2619
+ return UserDefinedFunction.new(name, params)
2620
+ end
2621
+ end
2622
+
2623
+ # Denotes the type of a Join operation.
2624
+ class JointTableQualifier
2625
+ attr_reader :value
2626
+
2627
+ @@joins = ['LEFT_OUTER', 'RIGHT_OUTER', 'FULL_OUTER',
2628
+ 'INNER', 'CROSS']
2629
+
2630
+ def initialize(value, join_list=nil)
2631
+ @join_list = join_list || @@joins
2632
+ self.value = value
2633
+ end
2634
+
2635
+ def value=(v)
2636
+ if @join_list.include?(v)
2637
+ @value = v
2638
+ else
2639
+ raise "Join type is not valid. Use one of: " +
2640
+ @join_list.join(', ')
2641
+ end
2642
+ end
2643
+
2644
+ def to_s
2645
+ "{value=#{self.value}}"
2646
+ end
2647
+
2648
+ def self.from_xml(node)
2649
+ return JointTableQualifier.new(node.text)
2650
+ end
2651
+ end
2652
+
2653
+ # Represents SQL JOIN expression.
2654
+ class JoinTable < FromTable
2655
+ attr_reader :qualifier, :tables, :condition
2656
+
2657
+ def initialize(qualifier, tables, condition)
2658
+ self.qualifier = qualifier
2659
+ self.tables = tables
2660
+ self.condition = condition
2661
+ end
2662
+
2663
+ def qualifier=(q)
2664
+ begin
2665
+ VOTables::VOTable::Misc::TypeCheck.new(q, JointTableQualifier).check()
2666
+ rescue VOTables::VOTable::Misc::TypeException
2667
+ @qualifier = JointTableQualifier.new(q)
2668
+ else
2669
+ @qualifier = q
2670
+ end
2671
+ end
2672
+
2673
+ def tables=(ts)
2674
+ VOTables::VOTable::Misc::TypeCheck.new(ts, ArrayOfFromTable).check()
2675
+ @tables = ts
2676
+ end
2677
+
2678
+ def condition=(c)
2679
+ VOTables::VOTable::Misc::TypeCheck.new(c, ComparisonPred).check()
2680
+ @condition = c
2681
+ end
2682
+
2683
+ def to_s
2684
+ "{qualifier=#{self.qualifier},tables=#{self.tables},condition=#{self.condition}}"
2685
+ end
2686
+
2687
+ def self.from_xml(node)
2688
+ qual_node = REXML::XPath.first(node, 'Qualifier')
2689
+ qual = Qualifier.from_xml(qual_node)
2690
+ tables_node = REXML::XPath.first(node, 'Tables')
2691
+ tables = Tables.from_xml(tables_node)
2692
+ cond_node = REXML::XPath.first(node, 'Condition')
2693
+ cond = Condition.from_xml(cond_node)
2694
+ return JoinTable.new(qual, tables, cond)
2695
+ end
2696
+ end
2697
+
2698
+ # Represents an array of tables in the from expression.def expression.
2699
+ class ArrayOfFromTable
2700
+ attr_reader :from_tables
2701
+
2702
+ def initialize(tables)
2703
+ self.from_tables = tables
2704
+ end
2705
+
2706
+ def from_tables=(fts)
2707
+ raise "Provide at least one table" if fts.size < 1
2708
+
2709
+ @from_tables = []
2710
+ if fts
2711
+ fts.each do |ft|
2712
+ VOTables::VOTable::Misc::TypeCheck.new(ft, FromTable)
2713
+ @from_tables.push(ft)
2714
+ end
2715
+ end
2716
+ end
2717
+
2718
+ def to_s
2719
+ tables = self.from_tables.collect{|x| x.to_s}.join('|')
2720
+ "{from_tables=#{tables}}"
2721
+ end
2722
+
2723
+ def self.from_xml(node)
2724
+ from_tables = []
2725
+ node.elements.to_a('Table').each do |ftbl|
2726
+ from_tables.push(FromTable.from_xml(ftbl))
2727
+ end
2728
+
2729
+ return ArrayOfFromTable.new(from_tables)
2730
+ end
2731
+ end
2732
+
2733
+ end
2734
+
2735
+ end