Class: ActiveFacts::CQL::Compiler::Reference

Inherits:
Object
  • Object
show all
Defined in:
lib/activefacts/cql/compiler/clause.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(term, leading_adjective = nil, trailing_adjective = nil, quantifier = nil, function_call = nil, role_name = nil, value_constraint = nil, literal = nil, nested_clauses = nil) ⇒ Reference

Returns a new instance of Reference.



873
874
875
876
877
878
879
880
881
882
883
# File 'lib/activefacts/cql/compiler/clause.rb', line 873

def initialize term, leading_adjective = nil, trailing_adjective = nil, quantifier = nil, function_call = nil, role_name = nil, value_constraint = nil, literal = nil, nested_clauses = nil
  @term = term
  @leading_adjective = leading_adjective
  @trailing_adjective = trailing_adjective
  @quantifier = quantifier
  # @function_call = function_call # Not used or implemented
  @role_name = role_name
  @value_constraint = value_constraint
  @literal = literal
  @nested_clauses = nested_clauses
end

Instance Attribute Details

#bindingObject

What Binding for that ObjectType



866
867
868
# File 'lib/activefacts/cql/compiler/clause.rb', line 866

def binding
  @binding
end

#clauseObject

The clause that this Reference is part of



869
870
871
# File 'lib/activefacts/cql/compiler/clause.rb', line 869

def clause
  @clause
end

#embedded_presence_constraintObject (readonly)

This refers to the ActiveFacts::Metamodel::PresenceConstraint



871
872
873
# File 'lib/activefacts/cql/compiler/clause.rb', line 871

def embedded_presence_constraint
  @embedded_presence_constraint
end

#function_callObject (readonly)

Returns the value of attribute function_call.



863
864
865
# File 'lib/activefacts/cql/compiler/clause.rb', line 863

def function_call
  @function_call
end

#leading_adjectiveObject

Returns the value of attribute leading_adjective.



864
865
866
# File 'lib/activefacts/cql/compiler/clause.rb', line 864

def leading_adjective
  @leading_adjective
end

#literalObject (readonly)

Returns the value of attribute literal.



863
864
865
# File 'lib/activefacts/cql/compiler/clause.rb', line 863

def literal
  @literal
end

#nested_clausesObject (readonly)

Returns the value of attribute nested_clauses.



863
864
865
# File 'lib/activefacts/cql/compiler/clause.rb', line 863

def nested_clauses
  @nested_clauses
end

#objectification_ofObject

If nested_clauses is set, this is the fact type it objectifies



870
871
872
# File 'lib/activefacts/cql/compiler/clause.rb', line 870

def objectification_of
  @objectification_of
end

#playerObject

What ObjectType does the Binding denote



865
866
867
# File 'lib/activefacts/cql/compiler/clause.rb', line 865

def player
  @player
end

#quantifierObject (readonly)

Returns the value of attribute quantifier.



863
864
865
# File 'lib/activefacts/cql/compiler/clause.rb', line 863

def quantifier
  @quantifier
end

#roleObject

Which Role of this ObjectType



867
868
869
# File 'lib/activefacts/cql/compiler/clause.rb', line 867

def role
  @role
end

#role_nameObject

Returns the value of attribute role_name.



864
865
866
# File 'lib/activefacts/cql/compiler/clause.rb', line 864

def role_name
  @role_name
end

#role_refObject

Which RoleRef to that Role



868
869
870
# File 'lib/activefacts/cql/compiler/clause.rb', line 868

def role_ref
  @role_ref
end

#termObject (readonly)

Returns the value of attribute term.



863
864
865
# File 'lib/activefacts/cql/compiler/clause.rb', line 863

def term
  @term
end

#trailing_adjectiveObject

Returns the value of attribute trailing_adjective.



864
865
866
# File 'lib/activefacts/cql/compiler/clause.rb', line 864

def trailing_adjective
  @trailing_adjective
end

#value_constraintObject (readonly)

Returns the value of attribute value_constraint.



863
864
865
# File 'lib/activefacts/cql/compiler/clause.rb', line 863

def value_constraint
  @value_constraint
end

Instance Method Details

#<=>(other) ⇒ Object



907
908
909
910
911
912
# File 'lib/activefacts/cql/compiler/clause.rb', line 907

def <=>(other)
  ( 4*(@term <=> other.term) +
    2*((@leading_adjective||'') <=> (other.leading_adjective||'')) +
    1*((@trailing_adjective||'') <=> (other.trailing_adjective||''))
  ) <=> 0
end

#bind(context) ⇒ Object



960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
# File 'lib/activefacts/cql/compiler/clause.rb', line 960

def bind context
  @nested_clauses.each{|c| c.bind context} if @nested_clauses
  if role_name = @role_name
    # Omit these tests to see if anything evil eventuates:
    #if @leading_adjective || @trailing_adjective
    #  raise "Role reference may not have adjectives if it defines a role name or uses a subscript: #{inspect}"
    #end
  else
    if uses_role_name?
      if @leading_adjective || @trailing_adjective
        raise "Role reference may not have adjectives if it uses a role name: #{inspect}"
      end
      role_name = @term
    end
  end
	  k = key
	  @binding = context.bindings[k]
	  if !@binding
	    if !literal
	      # Find a binding that has a literal, and bind to it if it's the only one
	      candidates = context.bindings.map do |binding_key, binding|
		  binding_key[0...k.size] == k &&
		    binding_key[-2] == :literal ? binding : nil
		end.compact
	      raise "Uncertain binding reference for #{to_s}, could be any of #{candidates.inspect}" if candidates.size > 1
	      @binding = candidates[0]
	    else
	      # New binding has a literal, look for one without:
	      @binding = context.bindings[k[0...-2]]
	    end
	  end

	  if !@binding
	    @binding = Binding.new(@player, role_name)
	    context.bindings[k] = @binding
	  end
  @binding.add_ref self
  @binding
end

#find_pc_over_roles(roles) ⇒ Object



1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
# File 'lib/activefacts/cql/compiler/clause.rb', line 1038

def find_pc_over_roles(roles)
  return nil if roles.size == 0 # Safeguard; this would chuck an exception otherwise
  roles[0].all_role_ref.each do |role_ref|
    next if role_ref.role_sequence.all_role_ref.map(&:role) != roles
    pc = role_ref.role_sequence.all_presence_constraint.single  # Will return nil if there's more than one.
    #puts "Existing PresenceConstraint matches those roles!" if pc
    return pc if pc
  end
  nil
end

#identify_other_players(context) ⇒ Object



929
930
931
# File 'lib/activefacts/cql/compiler/clause.rb', line 929

def identify_other_players context
  identify_player context
end

#identify_player(context) ⇒ Object



933
934
935
936
937
938
939
940
# File 'lib/activefacts/cql/compiler/clause.rb', line 933

def identify_player context
  @player || begin
    @player = context.object_type @term
    raise "ObjectType #{@term} unrecognised" unless @player
    context.player_by_role_name[@role_name] = player if @role_name
    @player
  end
end

#identify_players_with_role_name(context) ⇒ Object



923
924
925
926
927
# File 'lib/activefacts/cql/compiler/clause.rb', line 923

def identify_players_with_role_name(context)
  identify_player(context) if role_name
  # Include players in nested clauses, if any
  nested_clauses.each{|clause| clause.identify_players_with_role_name(context)} if nested_clauses
end

#includes_literalsObject



914
915
916
# File 'lib/activefacts/cql/compiler/clause.rb', line 914

def includes_literals
  @nested_clauses && @nested_clauses.detect{|nested| nested.includes_literals}
end

#inspectObject



885
886
887
# File 'lib/activefacts/cql/compiler/clause.rb', line 885

def inspect
  to_s
end

#keyObject



946
947
948
949
950
951
952
953
954
955
956
957
958
# File 'lib/activefacts/cql/compiler/clause.rb', line 946

def key
  if @role_name
    key = [@term, @role_name]         # Defines a role name
  elsif uses_role_name?
    key = [@player.name, @term]       # Uses a role name
  else
    l = @leading_adjective
    t = @trailing_adjective
    key = [!l || l.empty? ? nil : l, @term, !t || t.empty? ? nil : t]
  end
	  key += [:literal, literal.literal] if @literal
	  key
end

#make_embedded_presence_constraint(vocabulary) ⇒ Object



1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
# File 'lib/activefacts/cql/compiler/clause.rb', line 1049

def make_embedded_presence_constraint vocabulary
  raise "No Role for embedded_presence_constraint" unless @role_ref
  fact_type = @role_ref.role.fact_type
  constellation = vocabulary.constellation

  trace :constraint, "Processing embedded constraint #{@quantifier.inspect} on #{@role_ref.role.object_type.name} in #{fact_type.describe}" do
    # Preserve the role order of the clause, excluding this role:
    constrained_roles = (@clause.refs-[self]).map{|vr| vr.role_ref.role}
    if constrained_roles.empty?
      trace :constraint, "Quantifier over unary role has no effect"
      return
    end
    constraint = find_pc_over_roles(constrained_roles)
    if constraint
      raise "Conflicting maximum frequency for constraint" if constraint.max_frequency && constraint.max_frequency != @quantifier.max
      trace :constraint, "Setting max frequency to #{@quantifier.max} for existing constraint #{constraint.object_id} over #{constraint.role_sequence.describe} in #{fact_type.describe}" unless constraint.max_frequency
      constraint.max_frequency = @quantifier.max
      raise "Conflicting minimum frequency for constraint" if constraint.min_frequency && constraint.min_frequency != @quantifier.min
      trace :constraint, "Setting min frequency to #{@quantifier.min} for existing constraint #{constraint.object_id} over #{constraint.role_sequence.describe} in #{fact_type.describe}" unless constraint.min_frequency
      constraint.min_frequency = @quantifier.min
    else
      role_sequence = constellation.RoleSequence(:new)
      constrained_roles.each_with_index do |constrained_role, i|
        role_ref = constellation.RoleRef(role_sequence, i, :role => constrained_role)
      end
      constraint = constellation.PresenceConstraint(
          :new,
          :vocabulary => vocabulary,
          :role_sequence => role_sequence,
          :is_mandatory => @quantifier.min && @quantifier.min > 0,  # REVISIT: Check "maybe" qualifier?
          :max_frequency => @quantifier.max,
          :min_frequency => @quantifier.min
        )
	      if @quantifier.pragmas
		@quantifier.pragmas.each do |p|
		  constellation.ConceptAnnotation(:concept => constraint.concept, :mapping_annotation => p)
		end
	      end
      trace :constraint, "Made new embedded PC GUID=#{constraint.concept.guid} min=#{@quantifier.min.inspect} max=#{@quantifier.max.inspect} over #{(e = fact_type.entity_type) ? e.name : role_sequence.describe} in #{fact_type.describe}"
      @quantifier.enforcement.compile(constellation, constraint) if @quantifier.enforcement
      @embedded_presence_constraint = constraint
    end
    constraint
  end

end

#rebind(context) ⇒ Object



1010
1011
1012
1013
# File 'lib/activefacts/cql/compiler/clause.rb', line 1010

def rebind(context)
  unbind context
  bind context
end

#rebind_to(context, other_ref) ⇒ Object



1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
# File 'lib/activefacts/cql/compiler/clause.rb', line 1015

def rebind_to(context, other_ref)
  trace :binding, "Rebinding #{inspect} to #{other_ref.inspect}"

  old_binding = binding   # Remember to move all refs across
  unbind(context)

  new_binding = other_ref.binding
  [self, *old_binding.refs].each do |ref|
    ref.binding = new_binding
    new_binding.add_ref ref
  end
  old_binding.rebound_to = new_binding
end

#result(context = nil) ⇒ Object



1096
1097
1098
# File 'lib/activefacts/cql/compiler/clause.rb', line 1096

def result(context = nil)
  self
end

#to_sObject



889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
# File 'lib/activefacts/cql/compiler/clause.rb', line 889

def to_s
  "{#{
    @quantifier && @quantifier.inspect+' '
  }#{
    @leading_adjective && @leading_adjective.sub(/ |$/,'- ').sub(/ *$/,' ')
  }#{
    @term
  }#{
    @trailing_adjective && ' '+@trailing_adjective.sub(/(.* |^)/, '\1-')
  }#{
    @role_name and @role_name.is_a?(Integer) ? "(#{@role_name})" : " (as #{@role_name})"
  }#{
    @literal && ' '+@literal.inspect
  }#{
    @value_constraint && ' '+@value_constraint.to_s
  }}"
end

#unbind(context) ⇒ Object



1000
1001
1002
1003
1004
1005
1006
1007
1008
# File 'lib/activefacts/cql/compiler/clause.rb', line 1000

def unbind context
  # The key has changed.
  @binding.delete_ref self
  if @binding.refs.empty?
    # Remove the binding from the context if this was the last reference
    context.bindings.delete_if {|k,v| v == @binding }
  end
  @binding = nil
end

#uses_role_name?Boolean

Returns:

  • (Boolean)


942
943
944
# File 'lib/activefacts/cql/compiler/clause.rb', line 942

def uses_role_name?
  @term != @player.name
end

#wipe_leading_adjectiveObject

These are called when we successfully match a fact type reading that has relevant adjectives:



1030
1031
1032
# File 'lib/activefacts/cql/compiler/clause.rb', line 1030

def wipe_leading_adjective
  @leading_adjective = nil
end

#wipe_trailing_adjectiveObject



1034
1035
1036
# File 'lib/activefacts/cql/compiler/clause.rb', line 1034

def wipe_trailing_adjective
  @trailing_adjective = nil
end