lib/activefacts/vocabulary/extensions.rb in activefacts-0.8.8 vs lib/activefacts/vocabulary/extensions.rb in activefacts-0.8.9

- old
+ new

@@ -87,13 +87,17 @@ end end class RoleRef def describe - role_name + (join_node ? " JN#{join_node.ordinal}" : '') + role_name end + def preferred_role_ref + role.fact_type.preferred_reading.role_sequence.all_role_ref.detect{|rr| rr.role == role} + end + def role_name(joiner = "-") name_array = if role.fact_type.all_role.size == 1 if role.fact_type.is_a?(ImplicitFactType) "#{role.concept.name} phantom for #{role.fact_type.role.concept.name}" @@ -484,108 +488,132 @@ class TypeInheritance def describe(role = nil) "#{subtype.name} is a kind of #{supertype.name}" end + + def supertype_role + (roles = all_role.to_a)[0].concept == supertype ? roles[0] : roles[1] + end + + def subtype_role + (roles = all_role.to_a)[0].concept == subtype ? roles[0] : roles[1] + end end class JoinStep def describe - input_role_ref = input_join_node.all_role_ref.detect{|rr| rr.role.fact_type == fact_type} - output_role_ref = output_join_node.all_role_ref.detect{|rr| rr.role.fact_type == fact_type} - "from node #{input_join_node.ordinal} #{input_role_ref ? input_role_ref.role.concept.name : input_join_node.concept.name}"+ - " to node #{output_join_node.ordinal} #{output_role_ref ? output_role_ref.role.concept.name : output_join_node.concept.name}"+ - ": #{is_anti && 'not '}#{is_outer && 'maybe '}#{fact_type.default_reading}" + "JoinStep " + + "#{is_outer && 'maybe '}" + + (is_unary_step ? " (unary) " : "from #{input_join_role.describe} ") + + "#{is_anti && 'not '}" + + "to #{output_join_role.describe} " + + "over " + (is_objectification_step ? 'objectification ' : '') + + "'#{fact_type.default_reading}'" end def is_unary_step # Preserve this in case we have to use a real join_node for the phantom - # input_join_node.all_role_ref.detect{|rr| rr.role.fact_type.is_a?(ImplicitFactType) && rr.role.fact_type.role.fact_type.all_role.size == 1 } - input_join_node == output_join_node + input_join_role == output_join_role end def is_objectification_step fact_type.is_a?(ImplicitFactType) end + + def all_join_role + [input_join_role, output_join_role].uniq + all_incidental_join_role.to_a + end + + def external_fact_type + fact_type.is_a?(ImplicitFactType) ? fact_type.role.fact_type : fact_type + end end class JoinNode def describe - concept.name + concept.name + + (subscript ? "(#{subscript})" : '') + + " JN#{ordinal}" + +# (all_join_role.detect{|jr| jr.role_ref} ? " (projected)" : "") + + (value ? ' = '+value.describe : '') end + + def all_join_step + all_join_role.map do |jr| + jr.all_join_step_as_input_join_role.to_a + + jr.all_join_step_as_output_join_role.to_a + end. + flatten. + uniq + end end + class JoinRole + def describe + "#{role.concept.name} JN#{join_node.ordinal}" + + (role_ref ? " (projected)" : "") + end + + def all_join_step + (all_join_step_as_input_join_role.to_a + + all_join_step_as_output_join_role.to_a + + [join_step]).flatten.compact.uniq + end + end + class Join def show + steps_shown = {} debug :join, "Displaying full contents of Join #{join_id}" do all_join_node.sort_by{|jn| jn.ordinal}.each do |join_node| - debug :join, "Node #{join_node.ordinal} for #{join_node.concept.name}" do - (join_node.all_join_step_as_input_join_node.to_a + - join_node.all_join_step_as_output_join_node.to_a). - uniq. + debug :join, "#{join_node.describe}" do + join_node.all_join_step. each do |join_step| - debug :join, "#{ - join_step.is_unary_step ? 'unary ' : '' - }#{ - join_step.is_objectification_step ? 'objectification ' : '' - }step #{join_step.describe}" + next if steps_shown[join_step] + steps_shown[join_step] = true + debug :join, "#{join_step.describe}" end - join_node.all_role_ref.each do |role_ref| - debug :join, "reference #{role_ref.describe} in '#{role_ref.role.fact_type.default_reading}' over #{role_ref.role_sequence.describe}#{role_ref.role_sequence == role_sequence ? ' (projected)' : ''}" + join_node.all_join_role.each do |join_role| + debug :join, "role of #{join_role.describe} in '#{join_role.role.fact_type.default_reading}'" end end end end end + def all_join_step + all_join_node.map{|jn| jn.all_join_step.to_a}.flatten.uniq + end + + # Check all parts of this join for validity def validate show return - # Check all parts of this join for validity - jns = all_join_node.sort_by{|jn| jn.ordinal} - jns.each_with_index do |jn, i| - raise "Join node #{i} should have ordinal #{jn.ordinal}" unless jn.ordinal == i - end - # Check the join nodes: - steps = [] - jns.each_with_index do |join_node, i| + join_steps = [] + join_nodes = all_join_node.sort_by{|jn| jn.ordinal} + join_nodes.each_with_index do |join_node, i| + raise "Join node #{i} should have ordinal #{join_node.ordinal}" unless join_node.ordinal == i raise "Join Node #{i} has missing concept" unless join_node.concept - if join_node.all_role_ref.detect{|rr| rr.role.concept != join_node.concept } - raise "All role references for join node #{join_node.ordinal} should be for #{ - join_node.concept.name - } but we have #{ - (join_node.all_role_ref.map{|rr| rr.role.concept.name}-[join_node.concept.name]).uniq*', ' - }" + join_node.all_join_role do |join_role| + raise "Join Node for #{concept.name} includes role played by #{join_role.concept.name}" unless join_role.concept == concept end - steps += join_node.all_join_step_as_input_join_node.to_a - steps += join_node.all_join_step_as_output_join_node.to_a - - # REVISIT: All Role References must be in a role sequence that covers one fact type exactly (why?) - # REVISIT: All such role references must have a join node in this join. (why?) + join_steps += join_node.all_join_step end + join_steps.uniq! # Check the join steps: - steps.uniq! - steps.each_with_index do |join_step, i| - raise "Join Step #{i} has missing fact type" unless join_step.fact_type - raise "Join Step #{i} has missing input node" unless join_step.input_join_node - raise "Join Step #{i} has missing output node" unless join_step.output_join_node - debugger - p join_step.fact_type.default_reading - p join_step.input_join_node.all_role_ref.map(&:describe) - p join_step.output_join_node.all_role_ref.map(&:describe) -=begin - unless join_step.input_join_node.all_role_ref. - detect do |rr| - rr.role.fact_type == join_step.fact_type - or rr.role.fact_type.is_a?(ImplicitFactType) && rr.role.fact_type.role.fact_type == rr.join_step.concept - end - raise "Join Step #{join_step.describe} has nodes not matching its fact type" + join_steps.each do |join_step| + raise "Join Step has missing fact type" unless join_step.fact_type + raise "Join Step has missing input node" unless join_step.input_join_role + raise "Join Step has missing output node" unless join_step.output_join_role + if (role = input_join_role).role.fact_type != fact_type or + (role = output_join_role).role.fact_type != fact_type + raise "Join Step has role #{role.describe} which doesn't belong to the fact type '#{fact_type.default_reading}' it traverses" end -=end end # REVISIT: Do a connectivity check end end @@ -617,10 +645,11 @@ def initialize(role, role_sequence) @role = role @role_sequence = role_sequence end def join_node; nil; end + def join_role; nil; end def leading_adjective; nil; end def trailing_adjective; nil; end def describe @role.concept.name end @@ -703,11 +732,11 @@ objectification_role_supertypes = fact_type.entity_type.supertypes_transitive+concept.supertypes_transitive objectification_role = role.implicit_fact_type.all_role.single # Find the phantom role here else objectification_role_supertypes = counterpart_role_supertypes - objectification_role = nil + objectification_role = counterpart_role end if !d_c_o d_c_o = [proximate_role_supertypes, counterpart_role_supertypes, objectification_role_supertypes, [counterpart_role], [objectification_role]] #puts "role player supertypes starts #{d_c_o.map{|dco| dco.map(&:name).inspect}*' or '}" @@ -731,15 +760,77 @@ counterpart_sups = [] end end # Choose the first entry in the first non-empty supertypes list: - if options != :counterpart + if options != :counterpart && proximate_sups[0] [ proximate_sups[0], roles ] elsif !counterpart_sups.empty? [ counterpart_sups[0], counterpart_roles ] else [ obj_sups[0], objectification_roles ] + end + end + + class Fact + def verbalise(context = nil) + reading = fact_type.preferred_reading + reading_roles = reading.role_sequence.all_role_ref.sort_by{|rr| rr.ordinal}.map{|rr| rr.role } + role_values_in_reading_order = all_role_value.sort_by{|rv| reading_roles.index(rv.role) } + instance_verbalisations = role_values_in_reading_order.map do |rv| + if rv.instance.value + v = rv.instance.verbalise + else + if (c = rv.instance.concept).is_a?(EntityType) + if !c.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type == fact_type} + v = rv.instance.verbalise + end + end + end + next nil unless v + v.to_s.sub(/(#{rv.instance.concept.name}|\S*)\s/,'') + end + reading.expand([], false, instance_verbalisations) + end + end + + class Instance + def verbalise(context = nil) + return "#{concept.name} #{value}" if concept.is_a?(ValueType) + + return "#{concept.name} where #{fact.verbalise(context)}" if concept.fact_type + + # It's an entity that's not an objectified fact type + # REVISIT: If it has a simple identifier, there's no need to fully verbalise the identifying facts + pi = concept.preferred_identifier + identifying_role_refs = pi.role_sequence.all_role_ref_in_order + "#{concept.name}" + + " is identified by " + # REVISIT: Where the single fact type is TypeInheritance, we can shrink this + if identifying_role_refs.size == 1 && + (role = identifying_role_refs[0].role) && + (my_role = (role.fact_type.all_role.to_a-[role])[0]) && + (identifying_fact = my_role.all_role_value.detect{|rv| rv.instance == self}.fact) && + (identifying_instance = identifying_fact.all_role_value.detect{|rv| rv.role == role}.instance) + + "its #{identifying_instance.verbalise(context)}" + else + identifying_role_refs.map do |rr| + rr = rr.preferred_role_ref + [ (l = rr.leading_adjective) ? l+"-" : nil, + rr.role.role_name || rr.role.concept.name, + (t = rr.trailing_adjective) ? l+"-" : nil + ].compact*"" + end * " and " + + " where " + + identifying_role_refs.map do |rr| # Go through the identifying roles and emit the facts that define them + instance_role = concept.all_role.detect{|r| r.fact_type == rr.role.fact_type} + identifying_fact = all_role_value.detect{|rv| rv.fact.fact_type == rr.role.fact_type}.fact + #counterpart_role = (rr.role.fact_type.all_role.to_a-[instance_role])[0] + #identifying_instance = counterpart_role.all_role_value.detect{|rv| rv.fact == identifying_fact}.instance + identifying_fact.verbalise(context) + end*", " + end + end end end end