File: default_xml_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 (122 lines) | stat: -rw-r--r-- 3,227 bytes parent folder | download | duplicates (5)
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
require 'rgen/instantiator/nodebased_xml_instantiator'

module RGen

module Instantiator

# A default XML instantiator.
# Derive your own instantiator from this class or use it as is.
# 
class DefaultXMLInstantiator < NodebasedXMLInstantiator
  include Util::NameHelper

  NamespaceDescriptor = Struct.new(:prefix, :target)
  
  class << self
  
    def map_tag_ns(from, to, prefix="")
      tag_ns_map[from] = NamespaceDescriptor.new(prefix, to)
    end
    
    def tag_ns_map # :nodoc:
      @tag_ns_map ||={}
      @tag_ns_map
    end
    
  end  
  
  def initialize(env, default_module, create_mm=false)
    super(env)
    @default_module = default_module
    @create_mm = create_mm
  end
    
  def on_descent(node)
    obj = new_object(node)
    @env << obj unless obj.nil?
    node.object = obj
    node.attributes.each_pair { |k,v| set_attribute(node, k, v) }
  end

  def on_ascent(node)
    node.children.each { |c| assoc_p2c(node, c) }
    node.object.class.has_attr 'chardata', Object unless node.object.respond_to?(:chardata)
    set_attribute(node, "chardata", node.chardata)
  end
  
  def class_name(str)
    saneClassName(str)
  end
  
  def new_object(node)
    ns_desc = self.class.tag_ns_map[node.namespace]
    class_name = class_name(ns_desc.nil? ? node.qtag : ns_desc.prefix+node.tag)
    mod = (ns_desc && ns_desc.target) || @default_module
    build_on_error(NameError, :build_class, class_name, mod) do
      begin
        mod.const_get(class_name, false).new
      rescue ArgumentError
        # Ruby 1.8
        mod.const_get(class_name).new
      end
    end
  end
  
  def build_class(name, mod)
    mod.const_set(name, Class.new(RGen::MetamodelBuilder::MMBase))
  end
  
  def method_name(str)
    saneMethodName(str)
  end

  def assoc_p2c(parent, child)
    return unless parent.object && child.object
    method_name = method_name(className(child.object))
    build_on_error(NoMethodError, :build_p2c_assoc, parent, child, method_name) do
      parent.object.addGeneric(method_name, child.object)
      child.object.setGeneric("parent", parent.object)
    end
  end
  
  def build_p2c_assoc(parent, child, method_name)
    parent.object.class.has_many(method_name, child.object.class)
    child.object.class.has_one("parent", RGen::MetamodelBuilder::MMBase)
  end
  
  def set_attribute(node, attr, value)
    return unless node.object
      build_on_error(NoMethodError, :build_attribute, node, attr, value) do
      node.object.setGeneric(method_name(attr), value)
    end
  end
  
  def build_attribute(node, attr, value)
    node.object.class.has_attr(method_name(attr))
  end

  protected
  
  # Helper method for implementing classes.
  # This method yields the given block.
  # If the metamodel should be create automatically (see constructor)
  # rescues +error+ and calls +builder_method+ with +args+, then 
  # yields the block again.
  def build_on_error(error, builder_method, *args)
    begin
      yield
    rescue error
      if @create_mm
        send(builder_method, *args)
        yield
      else
        raise
      end
    end
  end

end

end

end