Class: RGen::Fragment::ModelFragment

Inherits:
Object
  • Object
show all
Defined in:
lib/rgen/fragment/model_fragment.rb

Overview

A model fragment is a list of root model elements associated with a location (e.g. a file). It also stores a list of unresolved references as well as a list of unresolved references which have been resolved. Using the latter, a fragment can undo reference resolution.

Optionally, an arbitrary data object may be associated with the fragment. The data object will also be stored in the cache.

If an element within the fragment changes this must be indicated to the fragment by calling mark_changed.

Note: the fragment knows how to resolve references (resolve_local, resolve_external). However considering a fragment a data structure, this functionality might be removed in the future. Instead the fragment should be told about each resolution taking place. Use method mark_resolved for this purpose.

Defined Under Namespace

Classes: FragmentRef, ResolvedReference

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(location, options = {}) ⇒ ModelFragment

Create a model fragment

:data
  data object associated with this fragment

:identifier_provider
  identifier provider to be used when resolving references
  it must be a proc which receives a model element and must return 
  that element's identifier or nil if the element has no identifier


56
57
58
59
60
61
62
63
64
# File 'lib/rgen/fragment/model_fragment.rb', line 56

def initialize(location, options={})
  @location = location
  @fragment_ref = FragmentRef.new
  @fragment_ref.fragment = self
  @data = options[:data]
  @resolved_refs = nil 
  @changed = false
  @identifier_provider = options[:identifier_provider]
end

Instance Attribute Details

#dataObject

Returns the value of attribute data.



24
25
26
# File 'lib/rgen/fragment/model_fragment.rb', line 24

def data
  @data
end

#fragment_refObject

Returns the value of attribute fragment_ref.



24
25
26
# File 'lib/rgen/fragment/model_fragment.rb', line 24

def fragment_ref
  @fragment_ref
end

#locationObject

Returns the value of attribute location.



24
25
26
# File 'lib/rgen/fragment/model_fragment.rb', line 24

def location
  @location
end

#root_elementsObject (readonly)

Returns the value of attribute root_elements.



23
24
25
# File 'lib/rgen/fragment/model_fragment.rb', line 23

def root_elements
  @root_elements
end

Instance Method Details

#build_indexObject

Builds the index of all elements within this fragment having an identifier the index is an array of 2-element arrays holding the identifier and the element



140
141
142
143
144
145
146
# File 'lib/rgen/fragment/model_fragment.rb', line 140

def build_index
  raise "cannot build index without an identifier provider" unless @identifier_provider
  @index = elements.collect { |e|
    ident = @identifier_provider.call(e, nil)
    ident && !ident.empty? ? [ident, e] : nil 
  }.compact
end

#changed?Boolean

Indicates whether the fragment has been changed or not

Returns:

  • (Boolean)


103
104
105
# File 'lib/rgen/fragment/model_fragment.rb', line 103

def changed?
  @changed
end

#elementsObject

Returns all elements within this fragment



109
110
111
112
113
114
115
116
117
# File 'lib/rgen/fragment/model_fragment.rb', line 109

def elements
  return @elements if @elements
  @elements = []
  @root_elements.each do |e|
    @elements << e
    @elements.concat(e.eAllContents)
  end
  @elements
end

#indexObject

Returns the index of the element contained in this fragment.



121
122
123
124
# File 'lib/rgen/fragment/model_fragment.rb', line 121

def index
  build_index unless @index
  @index
end

#mark_changedObject

Must be called when any of the elements in this fragment has been changed



85
86
87
88
89
90
91
92
93
# File 'lib/rgen/fragment/model_fragment.rb', line 85

def mark_changed
  @changed = true
  @elements = nil
  @index = nil
  @unresolved_refs = nil
  # unresolved refs will be recalculated, no need to keep removed_urefs
  @removed_urefs = nil
  @resolved_refs = :dirty 
end

#mark_resolved(uref, target_fragment, target) ⇒ Object

Marks a particular unresolved reference uref as resolved to target in target_fragment.



200
201
202
203
204
205
206
207
208
209
# File 'lib/rgen/fragment/model_fragment.rb', line 200

def mark_resolved(uref, target_fragment, target)
  @resolved_refs = {} if @resolved_refs.nil? || @resolved_refs == :dirty
  target_fragment ||= :unknown
  if target_fragment != self
    @resolved_refs[target_fragment] ||= []
    @resolved_refs[target_fragment] << ResolvedReference.new(uref, target)
  end
  @removed_urefs ||= []
  @removed_urefs << uref
end

#mark_unchangedObject

Can be used to reset the change status to unchanged.



97
98
99
# File 'lib/rgen/fragment/model_fragment.rb', line 97

def mark_unchanged
  @changed = false
end

#resolve_external(external_index, options) ⇒ Object

Resolves references to external fragments using the external_index provided. The external index must be a Hash mapping identifiers uniquely to model elements.

Options:

:fragment_provider:
  If a +fragment_provider+ is given, the resolve step can be reverted later on 
  by a call to unresolve_external or unresolve_external_fragment. The fragment provider
  is a proc which receives a model element and must return the fragment in which it is
  contained.

:use_target_type:
  reference resolver uses the expected target type to narrow the set of possible targets


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/rgen/fragment/model_fragment.rb', line 178

def resolve_external(external_index, options)
  fragment_provider = options[:fragment_provider]
  resolver = RGen::Instantiator::ReferenceResolver.new(
    :identifier_resolver => proc {|ident| external_index[ident] })
  if fragment_provider
    @resolved_refs = {} if @resolved_refs.nil? || @resolved_refs == :dirty
    on_resolve = proc { |ur, target|
      target_fragment = fragment_provider.call(target)
      target_fragment ||= :unknown
      raise "can not resolve local reference in resolve_external, call resolve_local first" \
        if target_fragment == self
      @resolved_refs[target_fragment] ||= []
      @resolved_refs[target_fragment] << ResolvedReference.new(ur, target)
    } 
    @unresolved_refs = resolver.resolve(unresolved_refs, :on_resolve => on_resolve, :use_target_type => options[:use_target_type])
  else
    @unresolved_refs = resolver.resolve(unresolved_refs, :use_target_type => options[:use_target_type])
  end
end

#resolve_local(options = {}) ⇒ Object

Resolves local references (within this fragment) as far as possible

Options:

:use_target_type:
  reference resolver uses the expected target type to narrow the set of possible targets


155
156
157
158
159
160
161
# File 'lib/rgen/fragment/model_fragment.rb', line 155

def resolve_local(options={})
  resolver = RGen::Instantiator::ReferenceResolver.new
  index.each do |i|
    resolver.add_identifier(i[0], i[1])
  end
  @unresolved_refs = resolver.resolve(unresolved_refs, :use_target_type => options[:use_target_type])
end

#set_root_elements(root_elements, options = {}) ⇒ Object

Set the root elements, normally done by an instantiator.

For optimization reasons the instantiator of the fragment may provide data explicitly which is normally derived by the fragment itself. In this case it is essential that this data is consistent with the fragment.



72
73
74
75
76
77
78
79
80
81
# File 'lib/rgen/fragment/model_fragment.rb', line 72

def set_root_elements(root_elements, options={})
  @root_elements = root_elements 
  @elements = options[:elements]
  @index = options[:index]
  @unresolved_refs = options[:unresolved_refs]
  @resolved_refs = nil 
  # new unresolved refs, reset removed_urefs
  @removed_urefs = nil
  @changed = false
end

#unresolve_externalObject

Unresolve outgoing references to all external fragments, i.e. references which used to be represented by an unresolved reference from within this fragment. Note, that there may be more references to external fragments due to references which were represented by unresolved references from within other fragments.



216
217
218
219
220
221
222
# File 'lib/rgen/fragment/model_fragment.rb', line 216

def unresolve_external
  return if @resolved_refs.nil?
  raise "can not unresolve, missing fragment information" if @resolved_refs == :dirty || @resolved_refs[:unknown]
  rrefs = @resolved_refs.values.flatten
  @resolved_refs = {}
  unresolve_refs(rrefs)
end

#unresolve_external_fragment(fragment) ⇒ Object

Like unresolve_external but only unresolve references to external fragment fragment



226
227
228
229
230
231
232
# File 'lib/rgen/fragment/model_fragment.rb', line 226

def unresolve_external_fragment(fragment)
  return if @resolved_refs.nil?
  raise "can not unresolve, missing fragment information" if @resolved_refs == :dirty || @resolved_refs[:unknown]
  rrefs = @resolved_refs[fragment]
  @resolved_refs.delete(fragment)
  unresolve_refs(rrefs) if rrefs
end

#unresolved_refsObject

Returns all unresolved references within this fragment, i.e. references to MMProxy objects



128
129
130
131
132
133
134
135
# File 'lib/rgen/fragment/model_fragment.rb', line 128

def unresolved_refs
  @unresolved_refs ||= collect_unresolved_refs
  if @removed_urefs
    @unresolved_refs -= @removed_urefs
    @removed_urefs = nil
  end
  @unresolved_refs
end