File: xmi11_instantiator.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 (168 lines) | stat: -rw-r--r-- 4,927 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
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
require 'rgen/ecore/ecore'
require 'rgen/instantiator/abstract_xml_instantiator'
require 'rgen/array_extensions'

class XMI11Instantiator < AbstractXMLInstantiator
  
  include RGen::ECore

  ResolverDescription = Struct.new(:object, :attribute, :value, :many)
  
  INFO = 0
  WARN = 1
  ERROR = 2
  
  def initialize(env, fix_map={}, loglevel=ERROR)
    @env = env
    @fix_map = fix_map
    @loglevel = loglevel
    @rolestack = []
    @elementstack = []
  end
   
  def add_metamodel(ns, mod)
    @ns_module_map ||={}
    @ns_module_map[ns] = mod
  end  

  def instantiate(str)
    @resolver_descs = []
    @element_by_id = {}
    super(str, 1000)
    @resolver_descs.each do |rd|
      if rd.many
        newval = rd.value.split(" ").collect{|v| @element_by_id[v]}
      else
        newval = @element_by_id[rd.value]
      end
      log WARN, "Could not resolve reference #{rd.attribute} on #{rd.object}" unless newval
      begin
        rd.object.setGeneric(rd.attribute,newval)
      rescue Exception
        log WARN, "Could not set reference #{rd.attribute} on #{rd.object}"
      end
    end
  end

  def start_tag(prefix, tag, namespaces, attributes)
    if tag =~ /\w+\.(\w+)/
      # XMI role
      role_name = map_feature_name($1) || $1
      eRef = @elementstack.last && eAllReferences(@elementstack.last).find{|r|r.name == role_name}
      log WARN, "No reference found for #{role_name} on #{@elementstack.last}" unless eRef
      @rolestack.push eRef
    elsif attributes["xmi.idref"]
      # reference
      rd = ResolverDescription.new
      rd.object = @elementstack.last
      rd.attribute = @rolestack.last.name
      rd.value = attributes["xmi.idref"]
      rd.many = @rolestack.last.many      
      @resolver_descs << rd
      @elementstack.push nil
    else
      # model element
      value = map_tag(tag, attributes) || tag
      if value.is_a?(String)
        mod = @ns_module_map[namespaces[prefix]]
        unless mod
          log WARN, "Ignoring tag #{tag}"
          return
        end
        value = mod.const_get(value).new
      end
      @env << value
      eRef = @rolestack.last
      if eRef && eRef.many
        @elementstack.last.addGeneric(eRef.name, value)
      elsif eRef
        @elementstack.last.setGeneric(eRef.name, value)
      end
      @elementstack.push value
    end
  end
  
  def end_tag(prefix, tag)
    if tag =~ /\w+\.(\w+)/
      @rolestack.pop
    else
      @elementstack.pop
    end
  end  
  
  def set_attribute(attr, value)
    return unless @elementstack.last
    if attr == "xmi.id"
      @element_by_id[value] = @elementstack.last
    else
      attr_name = map_feature_name(attr) || attr
      eFeat = eAllStructuralFeatures(@elementstack.last).find{|a| a.name == attr_name}
      unless eFeat
        log WARN, "No structural feature found for #{attr_name} on #{@elementstack.last}"
        return
      end
      if eFeat.is_a?(RGen::ECore::EReference)
        if map_feature_value(attr_name, value).is_a?(eFeat.eType.instanceClass)
          @elementstack.last.setGeneric(attr_name, map_feature_value(attr_name, value))
        else
          rd = ResolverDescription.new
          rd.object = @elementstack.last
          rd.attribute = attr_name
          rd.value = value
          rd.many = eFeat.many
          @resolver_descs << rd
        end
      else
        value = map_feature_value(attr_name, value) || value
        value = true if value == "true" && eFeat.eType == EBoolean
        value = false if value == "false" && eFeat.eType == EBoolean
        value = value.to_i if eFeat.eType == EInt || eFeat.eType == ELong
        value = value.to_f if eFeat.eType == EFloat
        value = value.to_sym if eFeat.eType.is_a?(EEnum)
        @elementstack.last.setGeneric(attr_name, value)
      end
    end
  end
  
  private
  
  def map_tag(tag, attributes)
    tag_map = @fix_map[:tags] || {}
    value = tag_map[tag]
    if value.is_a?(Proc)
      value.call(tag, attributes)
    else
      value
    end 
  end
        
  def map_feature_name(name)
    name_map = @fix_map[:feature_names] || {}
    name_map[name]
  end
  
  def map_feature_value(attr_name, value)
    value_map = @fix_map[:feature_values] || {}
    map = value_map[attr_name]
    if map.is_a?(Hash)
      map[value]
    elsif map.is_a?(Proc)
      map.call(value)
    end
  end
  
  def log(level, msg)
    puts %w(INFO WARN ERROR)[level] + ": " + msg if level >= @loglevel
  end

  def eAllReferences(element)
    @eAllReferences ||= {}
    @eAllReferences[element.class] ||= element.class.ecore.eAllReferences
  end
    
  def eAllStructuralFeatures(element)
    @eAllStructuralFeatures ||= {}
    @eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures
  end
  
end