File: model_serializer.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 (225 lines) | stat: -rw-r--r-- 7,878 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
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
require 'rgen/array_extensions'
require 'rgen/ecore/ecore_ext'

module RGen
  
module ModelBuilder

class ModelSerializer
  
  def initialize(writable, rootPackage)
    @writable = writable
    @currentPackage = rootPackage
    @qualifiedElementName = {}
    @internalElementName = {}
    @relativeQualifiedElementName = {}
  end
  
  def serialize(elements)
    calcQualifiedElementNames(elements)
    unifyQualifiedElementNames
    elements = [elements] unless elements.is_a?(Enumerable)
    elements.each do |e|
      serializeElement(e)
    end
  end
  
  private
  
  def serializeElement(element, viaRef=nil, namePath=[], indent=0)
    className = element.class.ecore.name
    cmd = className[0..0].downcase+className[1..-1]
    args = ["\"#{@internalElementName[element]}\""]
    namePath = namePath + [@internalElementName[element]]
    childs = [] 
    eAllStructuralFeatures(element).each do |f|
      next if f.derived
      if f.is_a?(RGen::ECore::EAttribute)
        next if f.name == "name" && element.name == @internalElementName[element]
        val = element.getGeneric(f.name)
        #puts f.defaultValue.inspect if f.name == "isRoot"
        args << ":#{f.name} => #{serializeAttribute(val)}" unless val == f.defaultValue || val.nil?
      elsif !f.containment
        next if f.eOpposite && f.eOpposite == viaRef
        val = element.getGeneric(f.name)
        refString = serializeReference(element, f, val)
        args << ":#{f.name} => #{refString}" if refString
      else
        cs = element.getGeneric(f.name)
        refString = nil
        if cs.is_a?(Array)
          cs.compact!
          rcs = cs.select{|c| serializeChild?(c, namePath)}
          childs << [f, rcs] unless rcs.empty?
          refString = serializeReference(element, f, cs-rcs)
        else
          if cs && serializeChild?(cs, namePath)
            childs << [f, [cs]]
          else
            refString = serializeReference(element, f, cs)
          end
        end
        args << ":#{f.name} => #{refString}" if refString
      end
    end

    args << ":as => :#{viaRef.name}" if viaRef && containmentRefs(viaRef.eContainingClass, element.class.ecore).size > 1
    cmd = elementPackage(element)+"."+cmd if elementPackage(element).size > 0
    @writable.write "  " * indent + cmd + " " + args.join(", ")
    if childs.size > 0
      @writable.write " do\n"
      oldPackage, @currentPackage = @currentPackage, element.class.ecore.ePackage
      childs.each do |pair|
        f, cs = pair
        cs.each {|c| serializeElement(c, f, namePath, indent+1) }
      end
      @currentPackage = oldPackage
      @writable.write "  " * indent + "end\n"
    else
      @writable.write "\n"
    end
  end
  
  def serializeChild?(child, namePath)
    @qualifiedElementName[child][0..-2] == namePath
  end

  def serializeAttribute(value)
    if value.is_a?(String)
      "\"#{value.gsub("\"","\\\"")}\""
    elsif value.is_a?(Symbol)
      ":#{value}"
    elsif value.nil?
      "nil"
    else
      value.to_s
    end
  end
  
  def serializeReference(element, ref, value)
    if value.is_a?(Array)
      value = value.compact
      value = value.select{|v| compareWithOppositeReference(ref, element, v) > 0} if ref.eOpposite
      qualNames = value.collect do |v|
        relativeQualifiedElementName(v, element).join(".")
      end
      !qualNames.empty? && ("[" + qualNames.collect { |v| "\"#{v}\"" }.join(", ") + "]")
    elsif value && (!ref.eOpposite || compareWithOppositeReference(ref, element, value) > 0)
      qualName = relativeQualifiedElementName(value, element).join(".")
      ("\"#{qualName}\"")        
    end
  end
  
  # descide which part of a bidirectional reference get serialized
  def compareWithOppositeReference(ref, element, target)
    result = 0
    # first try to make the reference from the many side to the one side
    result = -1 if ref.many && !ref.eOpposite.many
    result = 1 if !ref.many && ref.eOpposite.many
    return result if result != 0
    # for 1:1 or many:many perfer, shorter references
    result = relativeQualifiedElementName(element, target).size <=> 
      relativeQualifiedElementName(target, element).size
    return result if result != 0
    # there just needs to be a descision, use class name or object_id
    result = element.class.name <=> target.class.name
    return result if result != 0
    element.object_id <=> target.object_id    
  end

  def elementPackage(element)
    @elementPackage ||= {}
    return @elementPackage[element] if @elementPackage[element]
    eNames = element.class.ecore.ePackage.qualifiedName.split("::")
    rNames = @currentPackage.qualifiedName.split("::")
    while eNames.first == rNames.first && !eNames.first.nil?
      eNames.shift
      rNames.shift
    end
    @elementPackage[element] = eNames.join("::")
  end
  
  def relativeQualifiedElementName(element, context)
    return @relativeQualifiedElementName[[element, context]] if @relativeQualifiedElementName[[element, context]]
    # elements which are not in the @qualifiedElementName Hash are not in the scope
    # of this serialization and will be ignored
    return [] if element.nil? || @qualifiedElementName[element].nil?
    return [] if context.nil? || @qualifiedElementName[context].nil?
    eNames = @qualifiedElementName[element].dup
    cNames = @qualifiedElementName[context].dup
    while eNames.first == cNames.first && eNames.size > 1
      eNames.shift
      cNames.shift
    end
    @relativeQualifiedElementName[[element, context]] = eNames
  end

  def calcQualifiedElementNames(elements, prefix=[], takenNames=[])
    elements = [elements] unless elements.is_a?(Array)
    elements.compact!
    elements.each do |element|
      qualifiedNamePath = prefix + [calcInternalElementName(element, takenNames)]
      @qualifiedElementName[element] ||= []
      @qualifiedElementName[element] << qualifiedNamePath
      takenChildNames = []
      eAllStructuralFeatures(element).each do |f|
        if f.is_a?(RGen::ECore::EReference) && f.containment
          childs = element.getGeneric(f.name)
          calcQualifiedElementNames(childs, qualifiedNamePath, takenChildNames)
        end
      end
    end
  end
  
  def unifyQualifiedElementNames
    @qualifiedElementName.keys.each do |k|
      @qualifiedElementName[k] = @qualifiedElementName[k].sort{|a,b| a.size <=> b.size}.first    
    end
  end

  def calcInternalElementName(element, takenNames)
    return @internalElementName[element] if @internalElementName[element]
    name = if element.respond_to?(:name) && element.name && !element.name.empty?
        element.name
      else
        nextElementHelperName(element)
      end
    while takenNames.include?(name)
      name = nextElementHelperName(element)
    end
    takenNames << name
    @internalElementName[element] = name 
  end
  
  def nextElementHelperName(element)
    eClass = element.class.ecore
    @nextElementNameId ||= {}
    @nextElementNameId[eClass] ||= 1
    result = "_#{eClass.name}#{@nextElementNameId[eClass]}"
    @nextElementNameId[eClass] += 1
    result
  end

  def eAllStructuralFeatures(element)
    @eAllStructuralFeatures ||= {}
    @eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures
  end
    
  def eAllReferences(eClass)
    @eAllReferences ||= {}
    @eAllReferences[eClass] ||= eClass.eAllReferences
  end
    
  def containmentRefs(contextClass, eClass)
    @containmentRefs ||= {}
    @containmentRefs[[contextClass, eClass]] ||=
      eAllReferences(contextClass).select do |r| 
        r.containment && (eClass.eAllSuperTypes << eClass).include?(r.eType)
      end
  end

end

end

end