1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 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
|
require 'rgen/instantiator/resolution_helper'
module RGen
module Instantiator
# The ReferenceResolver can be used to resolve unresolved references, i.e. instances
# of class UnresolvedReference
#
# There are two ways how this can be used:
# 1. the identifiers and associated model elements are added upfront using +add_identifier+
# 2. register an :identifier_resolver with the constructor, which will be invoked
# for every unresolved identifier
#
class ReferenceResolver
# Instances of this class represent information about not yet resolved references.
# This consists of the +element+ and metamodel +feature_name+ which hold/is to hold the
# reference and the +proxy+ object which is the placeholder for the reference.
# If the reference could not be resolved because the target type does not match the
# feature type, the flag +target_type_error+ will be set.
#
class UnresolvedReference
attr_reader :feature_name, :proxy
attr_accessor :element, :target_type_error
def initialize(element, feature_name, proxy)
@element = element
@feature_name = feature_name
@proxy = proxy
end
end
# Create a reference resolver, options:
#
# :identifier_resolver:
# a proc which is called with an identifier and which should return the associated element
# in case the identifier is not uniq, the proc may return multiple values
# default: lookup element in internal map
#
def initialize(options={})
@identifier_resolver = options[:identifier_resolver]
@identifier_map = {}
end
# Add an +identifer+ / +element+ pair which will be used for looking up unresolved identifers
def add_identifier(ident, element)
map_entry = @identifier_map[ident]
if map_entry
if map_entry.is_a?(Array)
map_entry << element
else
@identifier_map[ident] = [map_entry, element]
end
else
@identifier_map[ident] = element
end
end
# Tries to resolve the given +unresolved_refs+. If resolution is successful, the proxy object
# will be removed, otherwise there will be an error description in the problems array.
# In case the resolved target element's type is not valid for the given feature, the
# +target_type_error+ flag will be set on the unresolved reference.
# Returns an array of the references which are still unresolved. Options:
#
# :problems
# an array to which problems will be appended
#
# :on_resolve
# a proc which will be called for every sucessful resolution, receives the unresolved
# reference as well as to new target element
#
# :use_target_type
# use the expected target type to narrow the set of possible targets
# (i.e. ignore targets with wrong type)
#
# :failed_resolutions
# a Hash which will receive an entry for each failed resolution for which at least one
# target element was found (wrong target type, or target not unique).
# hash key is the uref, hash value is the target element or the Array of target elements
#
def resolve(unresolved_refs, options={})
problems = options[:problems] || []
still_unresolved_refs = []
failed_resolutions = options[:failed_resolutions] || {}
unresolved_refs.each do |ur|
if @identifier_resolver
target = @identifier_resolver.call(ur.proxy.targetIdentifier)
else
target = @identifier_map[ur.proxy.targetIdentifier]
end
target = [target].compact unless target.is_a?(Array)
if options[:use_target_type]
feature = ur.element.class.ecore.eAllReferences.find{|r| r.name == ur.feature_name}
target = target.select{|e| e.is_a?(feature.eType.instanceClass)}
end
if target.size == 1
status = ResolutionHelper.set_uref_target(ur, target[0])
if status == :success
options[:on_resolve] && options[:on_resolve].call(ur, target[0])
elsif status == :type_error
ur.target_type_error = true
problems << type_error_message(target[0])
still_unresolved_refs << ur
failed_resolutions[ur] = target[0]
end
elsif target.size > 1
problems << "identifier #{ur.proxy.targetIdentifier} not uniq"
still_unresolved_refs << ur
failed_resolutions[ur] = target
else
problems << "identifier #{ur.proxy.targetIdentifier} not found"
still_unresolved_refs << ur
end
end
still_unresolved_refs
end
private
def type_error_message(target)
"invalid target type #{target.class}"
end
end
end
end
|