File: ecore_to_ruby.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 (266 lines) | stat: -rwxr-xr-x 7,561 bytes parent folder | download
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
require 'set'
require 'rgen/ecore/ecore'

module RGen
  
module ECore

# ECoreToRuby can turn ECore models into their Ruby metamodel representations
class ECoreToRuby
  
  def initialize
    @modules = {}
    @classifiers = {}
    @features_added = {}
    @reserved = Set.new(Object.methods).merge(Kernel.methods)
  end

  # Create a Ruby module representing +epackage+.
  # This includes all nested modules/packages, classes and enums.
  #
  # If a parent module is provided with the "under" parameter, 
  # the new module will be nested under the parent module.
  #
  # If the parent module has a non-temporary name,
  # (more precisely: a non-temporary classpath) i.e. if it is reachable
  # via a path of constant names from the root, then the nested
  # modules and classes will also have non-temporary names.
  # In particular, this means that they will keep their names even
  # if they are assigned to new constants.
  # 
  # If no parent module is provided or the parent module has a
  # temporary name by itself, then the nested modules and classes will
  # also have temporary names. This means that their name will stay
  # 'volatile' until they are assigned to constants reachable from
  # the root and the Module#name method is called for the first time.
  #
  # While the second approach is more flexible, it can come with a major
  # performance impact. The reason is that Ruby searches the space of
  # all known non-temporary classes/modules every time the name
  # of a class/module with a temporary name is queried.
  #
  def create_module(epackage, under=Module.new)
    with_empty_constant_order_helper do
      temp = under.to_s.start_with?("#")
      mod = create_module_internal(epackage, under, temp)

      epackage.eAllClassifiers.each do |c| 
        if c.is_a?(RGen::ECore::EClass)
          create_class(c, temp)
        elsif c.is_a?(RGen::ECore::EEnum)
          create_enum(c)
        end
      end

      load_classes_with_reserved_keywords(epackage)
      mod
    end
  end

  private

  def load_classes_with_reserved_keywords(epackage)
    epackage.eAllClassifiers.each do |eclass|
      # we early load classes which have ruby reserved keywords
      if eclass.is_a?(RGen::ECore::EClass)
        reserved_used = eclass.eStructuralFeatures.any? { |f| @reserved.include?(f.name.to_sym) }
        add_features(eclass) if reserved_used
      end
    end
  end

  def create_module_internal(epackage, under, temp)
    return @modules[epackage] if @modules[epackage]
    
    if temp
      mod = Module.new do
        extend RGen::MetamodelBuilder::ModuleExtension
      end
      under.const_set(epackage.name, mod)
    else
      under.module_eval <<-END
        module #{epackage.name}
          extend RGen::MetamodelBuilder::ModuleExtension
        end
      END
      mod = under.const_get(epackage.name)
    end
    @modules[epackage] = mod

    epackage.eSubpackages.each{|p| create_module_internal(p, mod, temp)}
    mod._set_ecore_internal(epackage)

    mod
  end

  def create_class(eclass, temp)
    return @classifiers[eclass] if @classifiers[eclass]

    mod = @modules[eclass.ePackage]
    if temp
      cls = Class.new(super_class(eclass, temp)) do
        abstract if eclass.abstract
        class << self
          attr_accessor :_ecore_to_ruby
         end
      end
      mod.const_set(eclass.name, cls)
    else
      mod.module_eval <<-END
        class #{eclass.name} < #{super_class(eclass, temp)}
          #{eclass.abstract ? 'abstract' : ''}
          class << self
            attr_accessor :_ecore_to_ruby
          end
        end
      END
      cls = mod.const_get(eclass.name)
    end

    class << eclass
      attr_accessor :instanceClass
      def instanceClassName
        instanceClass.to_s
      end
    end
    eclass.instanceClass = cls

    cls::ClassModule.module_eval do
      alias _method_missing method_missing
      def method_missing(m, *args)
        if self.class._ecore_to_ruby.add_features(self.class.ecore)
          send(m, *args)
        else
          _method_missing(m, *args)
        end
      end
      alias _respond_to respond_to?
      def respond_to?(m, include_all=false)
        self.class._ecore_to_ruby.add_features(self.class.ecore)
        _respond_to(m)
      end
    end
    @classifiers[eclass] = cls
    cls._set_ecore_internal(eclass)
    cls._ecore_to_ruby = self

    cls
  end

  def create_enum(eenum)
    return @classifiers[eenum] if @classifiers[eenum]

    e = RGen::MetamodelBuilder::DataTypes::Enum.new(eenum.eLiterals.collect{|l| l.name.to_sym})
    @classifiers[eenum] = e

    @modules[eenum.ePackage].const_set(eenum.name, e)
    e
  end

  class FeatureWrapper
    def initialize(efeature, classifiers)
      @efeature = efeature
      @classifiers = classifiers
    end
    def value(prop)
      return false if prop == :containment && @efeature.is_a?(RGen::ECore::EAttribute)
      @efeature.send(prop)
    end
    def many?
      @efeature.many
    end
    def reference?
      @efeature.is_a?(RGen::ECore::EReference)
    end
    def opposite
      @efeature.eOpposite
    end
    def impl_type
      etype = @efeature.eType
      if etype.is_a?(RGen::ECore::EClass) || etype.is_a?(RGen::ECore::EEnum)
        @classifiers[etype]
      else
        ic = etype.instanceClass
        if ic
          ic
        else
          raise "unknown type: #{etype.name}" 
        end
      end
    end
  end

  def super_class(eclass, temp)
    super_types = eclass.eSuperTypes
    if temp
      case super_types.size
      when 0
        RGen::MetamodelBuilder::MMBase
      when 1
        create_class(super_types.first, temp)
      else
        RGen::MetamodelBuilder::MMMultiple(*super_types.collect{|t| create_class(t, temp)})
      end
    else
      case super_types.size
      when 0
        "RGen::MetamodelBuilder::MMBase"
      when 1
        create_class(super_types.first, temp).name
      else
        "RGen::MetamodelBuilder::MMMultiple(" + 
          super_types.collect{|t| create_class(t, temp).name}.join(",") + ")"
      end
    end
  end

  class EmptyConstantOrderHelper
    def classCreated(c); end
    def moduleCreated(m); end
    def enumCreated(e); end
  end

  def with_empty_constant_order_helper
    orig_coh = RGen::MetamodelBuilder::ConstantOrderHelper
    RGen::MetamodelBuilder.instance_eval { remove_const(:ConstantOrderHelper) }
    RGen::MetamodelBuilder.const_set(:ConstantOrderHelper, EmptyConstantOrderHelper.new)

    begin
      result = yield
    ensure
      RGen::MetamodelBuilder.instance_eval { remove_const(:ConstantOrderHelper) }
      RGen::MetamodelBuilder.const_set(:ConstantOrderHelper, orig_coh)
    end

    result
  end

  public

  def add_features(eclass)
    return false if @features_added[eclass]
    c = @classifiers[eclass]
    eclass.eStructuralFeatures.each do |f|
      w1 = FeatureWrapper.new(f, @classifiers) 
      w2 = FeatureWrapper.new(f.eOpposite, @classifiers) if f.is_a?(RGen::ECore::EReference) && f.eOpposite
      c.module_eval do
        if w1.many?
          _build_many_methods(w1, w2)
        else
          _build_one_methods(w1, w2)
        end
      end
    end
    @features_added[eclass] = true
    eclass.eSuperTypes.each do |t|
      add_features(t)
    end
    true
  end

end

end

end