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
|
require 'rgen/metamodel_builder'
require 'rgen/instantiator/abstract_instantiator'
require 'nokogiri'
module RGen
module Instantiator
class NodebasedXMLInstantiator < AbstractInstantiator
class << self
# The prune level is the number of parent/children associations which
# is kept when the instantiator ascents the XML tree.
# If the level is 2, information for the node's children and the childrens'
# children will be available as an XMLNodeDescriptor object.
# If the level is 0 no pruning will take place, i.e. the whole information
# is kept until the end of the instantiation process. 0 is default.
def set_prune_level(level)
@prune_level = level
end
def prune_level # :nodoc:
@prune_level ||= 0
end
end
class XMLNodeDescriptor
attr_reader :namespace, :qtag, :prefix, :tag, :parent, :attributes, :chardata
attr_accessor :object, :children
def initialize(ns, qtag, prefix, tag, parent, children, attributes)
@namespace, @qtag, @prefix, @tag, @parent, @children, @attributes =
ns, qtag, prefix, tag, parent, children, attributes
@parent.children << self if @parent
@chardata = []
end
end
class Visitor < Nokogiri::XML::SAX::Document
attr_reader :namespaces
def initialize(inst)
@instantiator = inst
@namespaces = {}
end
def start_element_namespace(tag, attributes, prefix, uri, ns)
ns.each{|n| @namespaces[n[0]] = n[1]}
attrs = {}
attributes.each{|a| attrs[a.prefix ? a.prefix+":"+a.localname : a.localname] = a.value}
qname = prefix ? prefix+":"+tag : tag
@instantiator.start_element(uri, qname, prefix, tag, attrs)
end
def end_element(name)
@instantiator.end_element
end
def characters(str)
@instantiator.on_chardata(str)
end
end
def initialize(env)
super
@env = env
@stack = []
end
def instantiate_file(file)
File.open(file) { |f| parse(f.read)}
resolve
end
def instantiate(text)
parse(text)
resolve
end
def parse(src)
@visitor = Visitor.new(self)
parser = Nokogiri::XML::SAX::Parser.new(@visitor)
parser.parse(src)
@visitor = nil
end
def start_element(ns, qtag, prefix, tag, attributes)
node = XMLNodeDescriptor.new(ns, qtag, prefix, tag, @stack[-1], [], attributes)
@stack.push node
on_descent(node)
end
def end_element
node = @stack.pop
on_ascent(node)
prune_children(node, self.class.prune_level - 1) if self.class.prune_level > 0
end
def on_chardata(str)
node = @stack.last
node.chardata << str
end
# This method is called when the XML parser goes down the tree.
# An XMLNodeDescriptor +node+ describes the current node.
# Implementing classes must overwrite this method.
def on_descent(node)
raise "Overwrite this method !"
end
# This method is called when the XML parser goes up the tree.
# An XMLNodeDescriptor +node+ describes the current node.
# Implementing classes must overwrite this method.
def on_ascent(node)
raise "Overwrite this method !"
end
def namespaces
@visitor.namespaces if @visitor
end
private
def prune_children(node, level)
if level == 0
node.children = nil
else
node.children.each { |c| prune_children(c, level-1) }
end
end
end
end
end
|