File: reference_resolver.rb

package info (click to toggle)
ruby-rgen 0.10.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,428 kB
  • sloc: ruby: 11,344; xml: 1,368; yacc: 72; makefile: 10
file content (128 lines) | stat: -rw-r--r-- 4,708 bytes parent folder | download | duplicates (11)
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