lib/hqmf-parser/converter/pass1/precondition_converter.rb
module HQMF
# Class for converting an HQMF 1.0 representation to an HQMF 2.0 representation
class PreconditionConverter
def self.parse_preconditions(source,data_criteria_converter)
# preconditions = []
# source.each do |precondition|
# preconditions << HQMF::PreconditionConverter.parse_precondition(precondition,data_criteria_converter)
# end
#
# preconditions
parse_and_merge_preconditions(source,data_criteria_converter)
end
# converts a precondtion to a hqmf model
def self.parse_precondition(precondition,data_criteria_converter)
# grab child preconditions, and parse recursively
preconditions = parse_and_merge_preconditions(precondition[:preconditions],data_criteria_converter) if precondition[:preconditions] || []
preconditions_from_restrictions = HQMF::PreconditionExtractor.extract_preconditions_from_restrictions(precondition[:restrictions], data_criteria_converter)
# driv preconditions are preconditions that are the children of an expression
driv_preconditions = []
preconditions_from_restrictions.delete_if {|element| driv_preconditions << element if element.is_a? HQMF::Converter::SimpleRestriction and element.operator.type == 'DRIV'}
apply_restrictions_to_comparisons(preconditions, preconditions_from_restrictions) unless preconditions_from_restrictions.empty?
conjunction_code = convert_logical_conjunction(precondition[:conjunction])
if (precondition[:expression])
# this is for things like COUNT
type = precondition[:expression][:type]
operator = HQMF::Converter::SimpleOperator.new(HQMF::Converter::SimpleOperator.find_category(type), type, HQMF::Converter::SimpleOperator.parse_value(precondition[:expression][:value]))
children = []
if driv_preconditions and !driv_preconditions.empty?
children = driv_preconditions.map(&:preconditions).flatten
end
reference = nil
# take the conjunction code from the parent precondition
restriction = HQMF::Converter::SimpleRestriction.new(operator, nil, children)
comparison_precondition = HQMF::Converter::SimplePrecondition.new(nil,[restriction],reference,conjunction_code, false)
comparison_precondition.klass = HQMF::Converter::SimplePrecondition::COMPARISON
comparison_precondition.subset_comparison = true
preconditions << comparison_precondition
end
reference = nil
negation = precondition[:negation]
if (precondition[:comparison])
preconditions ||= []
comparison_precondition = HQMF::PreconditionExtractor.convert_comparison_to_precondition(precondition[:comparison],data_criteria_converter)
preconditions << comparison_precondition
end
if (precondition[:subset])
# this is for things like FIRST on preconditions
type = precondition[:subset]
operator = HQMF::Converter::SimpleOperator.new(HQMF::Converter::SimpleOperator.find_category(type), type, nil)
children = preconditions
reference = nil
# take the conjunction code from the parent precondition
restriction = HQMF::Converter::SimpleRestriction.new(operator, nil, children)
comparison_precondition = HQMF::Converter::SimplePrecondition.new(nil,[restriction],reference,conjunction_code, false)
comparison_precondition.klass = HQMF::Converter::SimplePrecondition::COMPARISON
preconditions = [comparison_precondition]
end
HQMF::Converter::SimplePrecondition.new(nil,preconditions,reference,conjunction_code, negation)
end
def self.get_comparison_preconditions(preconditions)
comparisons = []
preconditions.each do |precondition|
if (precondition.comparison? and !precondition.subset_comparison)
comparisons << precondition
elsif(precondition.has_preconditions?)
comparisons.concat(get_comparison_preconditions(precondition.preconditions))
else
raise "precondition with no comparison or children... not valid"
end
end
comparisons
end
def self.apply_restrictions_to_comparisons(preconditions, restrictions)
comparisons = get_comparison_preconditions(preconditions)
raise "no comparisons to apply restriction to" if comparisons.empty?
comparisons.each do |comparison|
comparison.preconditions.concat(restrictions)
end
end
private
def self.parse_and_merge_preconditions(source,data_criteria_converter)
return [] unless source and source.size > 0
preconditions_by_conjunction = {}
source.each do |precondition|
parsed = HQMF::PreconditionConverter.parse_precondition(precondition,data_criteria_converter)
preconditions_by_conjunction[parsed.conjunction_code] ||= []
preconditions_by_conjunction[parsed.conjunction_code] << parsed
end
merge_precondtion_conjunction_groups(preconditions_by_conjunction)
end
def self.merge_precondtion_conjunction_groups(preconditions_by_conjunction)
joined = []
preconditions_by_conjunction.each do |conjunction_code, preconditions|
sub_conditions = []
negated_conditions = []
preconditions.each do |precondition|
unless (precondition.negation)
sub_conditions.concat precondition.preconditions if precondition.preconditions
else
negated_conditions.concat precondition.preconditions if precondition.preconditions
end
end
if (!sub_conditions.empty?)
# if we have negated conditions, add them to a new precondition with an inverted conjunction on a negated precondition
# the reason we invert the conjunction is because we are turning
# AND: NOT X
# AND: NOT Y
# into
# NOT: X OR Y
# (i.e, demorgan's law)
if (!negated_conditions.empty?)
inverted_conjunction_code = HQMF::Precondition::INVERSIONS[conjunction_code]
sub_conditions << HQMF::Converter::SimplePrecondition.new(nil,negated_conditions,nil,inverted_conjunction_code, true)
end
joined << HQMF::Converter::SimplePrecondition.new(nil,sub_conditions,nil,conjunction_code, false)
elsif (!negated_conditions.empty?)
# invert conjunction based on demorgan's law
inverted_conjunction_code = HQMF::Precondition::INVERSIONS[conjunction_code]
joined << HQMF::Converter::SimplePrecondition.new(nil,negated_conditions,nil,inverted_conjunction_code, true)
end
end
joined
end
def self.convert_logical_conjunction(code)
case code
when 'OR'
HQMF::Precondition::AT_LEAST_ONE_TRUE
when 'AND'
HQMF::Precondition::ALL_TRUE
else
raise "unsupported logical conjunction code conversion: #{code}"
end
end
end
end