Class: ActiveFacts::CQL::Compiler::ValueType

Inherits:
ObjectType show all
Defined in:
lib/activefacts/cql/compiler/value_type.rb

Instance Attribute Summary

Attributes inherited from ObjectType

#name

Attributes inherited from Definition

#constellation, #tree, #vocabulary

Instance Method Summary collapse

Methods inherited from Definition

#all_bindings_in_clauses, #assert_literal_value, #build_all_steps, #build_step, #build_variables, #source

Constructor Details

#initialize(name, base, parameters, unit, value_constraint, pragmas, context_note, auto_assigned_at) ⇒ ValueType

Returns a new instance of ValueType.



90
91
92
93
94
95
96
97
98
99
# File 'lib/activefacts/cql/compiler/value_type.rb', line 90

def initialize name, base, parameters, unit, value_constraint, pragmas, context_note, auto_assigned_at
  super name
  @base_type_name = base
  @parameters = parameters
  @unit = unit
  @value_constraint = value_constraint
  @pragmas = pragmas
  @context_note = context_note
  @auto_assigned_at = auto_assigned_at
end

Instance Method Details

#compileObject



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/activefacts/cql/compiler/value_type.rb', line 101

def compile
  ordered_parameters, named_parameters = @parameters.partition{|p| Integer === p}
  length, scale = *ordered_parameters

  # Create the base type unless it already exists:
  base_type = nil
  if (@base_type_name != @name)
    unless base_type = @vocabulary.valid_value_type_name(@base_type_name)
      base_type = @constellation.ValueType(@vocabulary, @base_type_name, :concept => :new)
    end
  end

  # Create and initialise the ValueType:
  vt = @vocabulary.valid_value_type_name(@name) ||
    @constellation.ValueType(@vocabulary, @name, :concept => :new)

  # Apply independence:
  vt.is_independent = true if @pragmas.delete('independent')

  # Apply pragmas:
  @pragmas.each do |p|
    @constellation.ConceptAnnotation(:concept => vt.concept, :mapping_annotation => p)
  end if @pragmas

  # Apply supertype
  if base_type and base_type != vt
    raise "You may not change the supertype of #{vt.name} from #{vt.supertype.name} to #{base_type.name}" if vt.supertype && vt.supertype != base_type
    vt.supertype = base_type
  end

  # Apply ordered parameters:
  if length
    raise "You may not change the length of #{vt.name} from #{vt.length} to #{length}" if vt.length && vt.length != length
    vt.length = length
  end
  if scale
    raise "You may not change the scale of #{vt.name} from #{vt.scale} to #{scale}" if vt.scale && vt.scale != scale
    vt.scale = scale
  end
  vt.transaction_phase = @auto_assigned_at

  unless @unit.empty?
    unit_name, exponent = *@unit[0]
    unit = @constellation.Name[unit_name].unit ||
      @constellation.Name[unit_name].plural_named_unit
    raise "Unit #{unit_name} for value type #{@name} is not defined" unless unit
    if exponent != 1
      base_unit = unit
      unit_name = base_unit.name+"^#{exponent}"
      unless unit = @constellation.Unit.detect{|k,v| v.name == unit_name } 
        # Define a derived unit (these are skipped on output)
        unit = @constellation.Unit(:new,
              :vocabulary => @vocabulary,
              :name => unit_name,
              :is_fundamental => false
            )
        @constellation.Derivation(unit, base_unit).exponent = exponent
      end
    end
    raise "You may not change the units of #{vt.name} from #{vt.unit.describe} to #{unit.describe}" if vt.unit && vt.unit != unit
    vt.unit = unit
  end

  # Apply a value constraint:
  if @value_constraint
    @value_constraint.constellation = @constellation  # Pass constellation to the value_constraint compiler
    raise "#{vt.name} is already constrained to #{vt.value_constraint.describe} so you can't change it to #{@value_constraint.describe}" if vt.value_constraint && vt.value_constraint != value_constraint
    vt.value_constraint = @value_constraint.compile
  end

  # Apply a context note:
  if @context_note
    @context_note.compile(@constellation, vt)
  end

  # Apply named parameter (definitions, restrictions, and settings):
  trace :vtp, "Applying named parameters for #{@name}" do
    named_parameters.each do |(kind, vtp_name, *rest)|
      # Look up an existing definition of the parameter:
      vtp = nil
      vt.supertypes_transitive.detect do |st|
        vtp = st.all_value_type_parameter.detect{|evtp| evtp.name == vtp_name}
      end

      trace :vtp, "Applying #{kind} of named parameter #{vtp_name}" do
        restrictions = nil
        case kind
        when :definition
          raise "You may not redefine parameter #{vtp_name} of #{vt.name}" if vtp
          trace :vtp, "Defining parameter #{vtp_name} for #{vt.name}" do
            parameter_value_type_name = rest.shift.term
            parameter_value_type = @vocabulary.valid_value_type_name(parameter_value_type_name)
            raise "Type #{parameter_value_type_name} for parameter #{vtp_name} of #{vt.name} is not defined" unless parameter_value_type
            vtp = @constellation.ValueTypeParameter(value_type: vt, name: vtp_name, parameter_value_type: parameter_value_type)
            restrictions = rest[0]
          end

        when :restriction
          raise "parameter #{vtp_name} of #{vt.name} is not defined" unless vtp
          trace :vtp, "Restricting parameter #{vtp_name} for #{vt.name} to #{rest[0].inspect}"
          restrictions = rest[0]

        when :setting
          raise "parameter #{vtp_name} of #{vt.name} is not defined" unless vtp
          # A Setting is a single-valued restriction
          trace :vtp, "Setting parameter #{vtp_name} for #{vt.name} to #{[rest[0]].inspect}"
          restrictions = [rest[0]]
        else
          raise "AST error: unknown valuetype parameter #{kind}"
        end

        if restrictions && restrictions[0] == :value
          # This is a value restriction which perhaps sets the RestrictionStyle
          value = restrictions[1]
          vtp.restriction_style = restrictions[2]
          case restrictions[2]
          when '', nil
            restrictions = [[value, value]]
          when 'min'
            restrictions = [[value, nil]]
          when 'max'
            restrictions = [[nil, value]]   # Or should it be [0, value]? But we can get that using a range...
          end
          trace :vtp, "Setting value for parameter #{vtp_name} for #{vt.name} to #{value.inspect} with style #{vtp.restriction_style.inspect}"
        end

        if restrictions
          # Find all ValueTypeParameterRestrictions for this parameter in this type's closest restricted supertype
          vtprs = nil
          restricted_supertype = nil
          vt.supertypes_transitive.detect do |st|
            vtprs = st.all_value_type_parameter_restriction.select{|evtpr| evtpr.value_type_parameter == vtp}
            vtprs = nil if vtprs.empty?
            restricted_supertype = st
            trace :vtp, "Existing #{vtp_name} restriction on #{st.name} allows #{vt.name} to use #{vtprs.map(&:value_range).inspect}" if vtprs
            vtprs
          end

          if vtprs && vt == restricted_supertype
            raise "You can't change the existing restrictions on parameter #{vtp_name} of #{vt.name}"
          end

          trace :vtp, "Restricting parameter #{vtp_name}#{vt != vtp.value_type ? " (of #{vtp.value_type.name})" : ''} for #{vt.name} to #{restrictions.inspect}" do
            restrictions.each do |restriction|

              min, max = Array === restriction ? restriction : [restriction, restriction]
              value_range = @constellation.ValueRange(
                min && @constellation.Bound(:value => assert_literal_value(min), :is_inclusive => true),
                max && @constellation.Bound(:value => assert_literal_value(max), :is_inclusive => true)
              )

              # This restriction may not widen the supertype's restriction:
              if vtprs && !vtprs.detect{|r| r.value_range.includes?(value_range)}
                raise "#{vtp_name} of #{@name} may not be set to #{restriction} because #{vtprs[0].value_type.name} restricts it to #{vtprs.map(&:value_range).map(&:inspect)*', '}"
              end
              @constellation.ValueTypeParameterRestriction(value_type: vt, value_type_parameter: vtp, value_range: value_range)
            end
          end
        end
      end
    end
  end

  vt
end

#to_sObject



267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/activefacts/cql/compiler/value_type.rb', line 267

def to_s
  "ValueType: #{super} is written as #{
      @base_type_name
    }#{
      @parameters.size > 0 ? "(#{ @parameters.map{|p|p.to_s}*', ' })" : ''
    }#{
      @unit && @unit.length > 0 ? " in #{@unit.inspect}" : ''
    }#{
      @value_constraint ? " "+@value_constraint.to_s : ''
    }#{
      @pragmas.size > 0 ? ", pragmas [#{@pragmas*','}]" : ''
    };"
end