duxml 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/validate_xml +19 -0
- data/lib/duxml/doc/element.rb +157 -0
- data/lib/duxml/doc/lazy_ox.rb +153 -0
- data/lib/duxml/doc/node_set.rb +39 -0
- data/lib/duxml/doc.rb +26 -0
- data/lib/duxml/meta/grammar/pattern/attr_name_pattern.rb +36 -0
- data/lib/duxml/meta/grammar/pattern/attr_val_pattern.rb +36 -0
- data/lib/duxml/meta/grammar/pattern/child_pattern.rb +72 -0
- data/lib/duxml/meta/grammar/pattern/text_pattern.rb +30 -0
- data/lib/duxml/meta/grammar/pattern.rb +98 -0
- data/lib/duxml/meta/grammar/pattern_maker.rb +69 -0
- data/lib/duxml/meta/grammar/relax_ng/attrs_rule.rb +58 -0
- data/lib/duxml/meta/grammar/relax_ng/children_rule.rb +83 -0
- data/lib/duxml/meta/grammar/relax_ng/value_rule.rb +44 -0
- data/lib/duxml/meta/grammar/relax_ng.rb +40 -0
- data/lib/duxml/meta/grammar/rule/attrs_rule.rb +78 -0
- data/lib/duxml/meta/grammar/rule/children_rule.rb +137 -0
- data/lib/duxml/meta/grammar/rule/text_rule.rb +40 -0
- data/lib/duxml/meta/grammar/rule/value_rule.rb +111 -0
- data/lib/duxml/meta/grammar/rule.rb +59 -0
- data/lib/duxml/meta/grammar/spreadsheet.rb +35 -0
- data/lib/duxml/meta/grammar.rb +134 -0
- data/lib/duxml/meta/history/add.rb +36 -0
- data/lib/duxml/meta/history/change.rb +71 -0
- data/lib/duxml/meta/history/change_attr.rb +34 -0
- data/lib/duxml/meta/history/change_text.rb +33 -0
- data/lib/duxml/meta/history/error.rb +25 -0
- data/lib/duxml/meta/history/new_attr.rb +33 -0
- data/lib/duxml/meta/history/new_text.rb +37 -0
- data/lib/duxml/meta/history/qualify_error.rb +22 -0
- data/lib/duxml/meta/history/remove.rb +28 -0
- data/lib/duxml/meta/history/undo.rb +24 -0
- data/lib/duxml/meta/history/validate_error.rb +21 -0
- data/lib/duxml/meta/history.rb +88 -0
- data/lib/duxml/meta.rb +51 -0
- data/lib/duxml/reportable.rb +27 -0
- data/lib/duxml/ruby_ext/fixnum.rb +56 -0
- data/lib/duxml/ruby_ext/module.rb +12 -0
- data/lib/duxml/ruby_ext/object.rb +14 -0
- data/lib/duxml/ruby_ext/regexp.rb +20 -0
- data/lib/duxml/ruby_ext/string.rb +29 -0
- data/lib/duxml/saxer.rb +71 -0
- data/lib/duxml.rb +97 -0
- data/xml/dita_grammar.xml +2133 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a443f0b41070e85e05a9042711af460f1a9de0ce
|
4
|
+
data.tar.gz: c25d6801574a1730e3a38a5ccab2ed91b482d37b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2804f997a7fa48f9bd6c24ddef5e8b84df058b4bfbc1f6a41cbda340646378ef0d94a95a32b89cc1160819bb2d9bf37926c8196e2e8f5d9d70950a5baa973f3b
|
7
|
+
data.tar.gz: d2cac9f2b2c16c07b754f98224eea4da660857a73345215bed10824818cd837de15ce2fe98bb06a2100eb69171537fd270ae27dd6199710c1e740c88fd3d2711
|
data/bin/validate_xml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/duxml')
|
5
|
+
|
6
|
+
include Duxml
|
7
|
+
|
8
|
+
DITA_GRAMMAR_FILE = File.expand_path(File.dirname(__FILE__) + '/../xml/test_grammar.xml')
|
9
|
+
xml_file = ARGV.first
|
10
|
+
log_file = ARGV[1] || 'log.txt'
|
11
|
+
|
12
|
+
load xml_file
|
13
|
+
puts "loaded XML file: #{xml_file}"
|
14
|
+
meta.grammar = DITA_GRAMMAR_FILE
|
15
|
+
puts "loaded grammar file: #{DITA_GRAMMAR_FILE}"
|
16
|
+
validate
|
17
|
+
puts "validation complete"
|
18
|
+
log log_file
|
19
|
+
puts "logged errors to #{log_file}"
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
2
|
+
|
3
|
+
require 'ox'
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/lazy_ox')
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + '/node_set')
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + '/../reportable')
|
7
|
+
|
8
|
+
module Duxml
|
9
|
+
# contains actual methods of XML Element
|
10
|
+
module ElementGuts
|
11
|
+
include Duxml
|
12
|
+
include Enumerable
|
13
|
+
include Reportable
|
14
|
+
include LazyOx
|
15
|
+
end
|
16
|
+
|
17
|
+
# basic component of XML file that can possess attributes and take sub Elements or String content
|
18
|
+
class Element < ::Ox::Element
|
19
|
+
include ElementGuts
|
20
|
+
|
21
|
+
# operates in two modes:
|
22
|
+
# - from Ruby
|
23
|
+
# - from file
|
24
|
+
# in file mode, args provide Element's line and column location then freezes each Fixnum so it cannot be overwritten
|
25
|
+
# in Ruby mode, args are some combination of new attributes/values and/or child nodes (text or XML) with which to initialize this node
|
26
|
+
#
|
27
|
+
# @param name [String] name of element, in both Ruby and file modes
|
28
|
+
# @param _line_content [Fixnum, Array, Hash] line number of element file mode; if Array, new child nodes; if Hash, attributes; can be nil
|
29
|
+
# @param _col_or_children [Fixnum, Array] column position in file mode; if Array, new child nodes; can be nil
|
30
|
+
def initialize(name, _line_or_content=nil, _col_or_children=nil)
|
31
|
+
super name
|
32
|
+
@line = _line_or_content if _line_or_content.respond_to?(:%)
|
33
|
+
_line_or_content.each do |k,v| self[k] = v end if _line_or_content.respond_to?(:key)
|
34
|
+
@nodes = NodeSet.new(self, _line_or_content) if _line_or_content.respond_to?(:pop) && _col_or_children.nil?
|
35
|
+
@column = _col_or_children if _col_or_children.respond_to?(:%)
|
36
|
+
@nodes = NodeSet.new(self, _col_or_children) if _col_or_children.respond_to?(:pop)
|
37
|
+
@nodes = NodeSet.new(self) if @nodes.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :line, :column
|
41
|
+
|
42
|
+
attr_accessor :nodes
|
43
|
+
end
|
44
|
+
|
45
|
+
module ElementGuts
|
46
|
+
# @return [Boolean] whether or not this has been written to file
|
47
|
+
def abstract?
|
48
|
+
line < 0 || column < 0
|
49
|
+
end
|
50
|
+
|
51
|
+
# @see Ox::Element#<<
|
52
|
+
# this override reports changes to history; NewText for Strings, Add for elements
|
53
|
+
#
|
54
|
+
# @param obj [Element] element or string to add to this Element
|
55
|
+
# @return [Element] self
|
56
|
+
def <<(obj)
|
57
|
+
case
|
58
|
+
when obj.is_a?(Array), obj.is_a?(NodeSet)
|
59
|
+
obj.each do |e| self << e end
|
60
|
+
when obj.is_a?(String)
|
61
|
+
type = :NewText
|
62
|
+
super(obj)
|
63
|
+
else
|
64
|
+
type = :Add
|
65
|
+
super(obj)
|
66
|
+
if nodes.last.count_observers < 1 && @observer_peers
|
67
|
+
nodes.last.add_observer(@observer_peers.first.first)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
report(type, nodes.size - 1)
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param attr_sym [String, Symbol] name of attribute
|
75
|
+
# @param val [String]
|
76
|
+
# @return [Element] self
|
77
|
+
def []=(attr_sym, val)
|
78
|
+
attr = attr_sym.to_s
|
79
|
+
raise "argument to [] must be a Symbol or a String." unless attr.is_a?(Symbol) or attr.is_a?(String)
|
80
|
+
args = [attr]
|
81
|
+
args << attributes[attr] if attributes[attr]
|
82
|
+
super(attr, val)
|
83
|
+
type = args.size == 1 ? :NewAttr : :ChangeAttr
|
84
|
+
report(type, *args)
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [String] self description
|
89
|
+
def description
|
90
|
+
"<#{name}>"
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Element] copy of this Element but with no children
|
94
|
+
def stub
|
95
|
+
Element.new(name, attributes)
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [HistoryClass] history that is observing this element for changes
|
99
|
+
def history
|
100
|
+
@observer_peers.first.first if @observer_peers.respond_to?(:any?) and @observer_peers.any? and @observer_peers.first.any?
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return [String] XML string (overrides Ox's to_s which just prints the object pointer)
|
104
|
+
def to_s
|
105
|
+
s = %(<#{name})
|
106
|
+
attributes.each do |k,v| s << %( #{k.to_s}="#{v}") end
|
107
|
+
return s+'/>' if nodes.empty?
|
108
|
+
s << ">#{nodes.collect do |n| n.to_s end.join}</#{name}>"
|
109
|
+
end
|
110
|
+
|
111
|
+
# @return #to_s
|
112
|
+
def inspect
|
113
|
+
to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
# TODO do we need this method to take Fixnum node index as well?
|
117
|
+
# @param obj [Element] element child to delete
|
118
|
+
# @return [Element] deleted element
|
119
|
+
def delete(obj)
|
120
|
+
report(:Remove, @nodes.delete(obj))
|
121
|
+
obj
|
122
|
+
end
|
123
|
+
|
124
|
+
alias_method :remove, :delete
|
125
|
+
|
126
|
+
# pre-order traverse through this node and all of its descendants
|
127
|
+
#
|
128
|
+
# @param &block [block] code to execute for each yielded node
|
129
|
+
def traverse(&block)
|
130
|
+
return self.to_enum unless block_given?
|
131
|
+
node_stack = [self]
|
132
|
+
|
133
|
+
until node_stack.empty?
|
134
|
+
current = node_stack.shift
|
135
|
+
if current
|
136
|
+
yield current
|
137
|
+
node_stack = node_stack.concat(current.nodes) if current.respond_to?(:nodes)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
self if block_given?
|
142
|
+
end
|
143
|
+
|
144
|
+
# traverse through just the children of this node
|
145
|
+
#
|
146
|
+
# @param &block [block] code to execute for each child node
|
147
|
+
def each(&block)
|
148
|
+
@nodes.each(&block)
|
149
|
+
end
|
150
|
+
|
151
|
+
# @return [String] namespace of element, derived from name e.g. '<duxml:element>' => 'duxml'
|
152
|
+
def name_space
|
153
|
+
return nil unless (i = name.index(':'))
|
154
|
+
name[0..i-1]
|
155
|
+
end
|
156
|
+
end # class Element < Node
|
157
|
+
end # module Ox
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../ruby_ext/string')
|
4
|
+
|
5
|
+
|
6
|
+
module Duxml
|
7
|
+
module LazyOx
|
8
|
+
# welcome to Lazy-Ox - where any method that doesn't exist, you can create on the fly and assign its methods to
|
9
|
+
# a corresponding Duxml::Element. see Regexp.nmtoken and String#nmtokenize and String#constantize to see how a given symbol
|
10
|
+
# can be converted into XML element names and vice versa. it can also use Class names as method calls to return children by type
|
11
|
+
#
|
12
|
+
# this method uses Ox::Element's :method_missing but adds an additional rescue block that:
|
13
|
+
# matches namespaced Ruby module to this Element's name and extends this node with module's methods
|
14
|
+
# then method matching symbol is called again with given arguments, yielding result to block if given, returning result if not
|
15
|
+
# e.g.
|
16
|
+
# module Duxml
|
17
|
+
# module Throwable
|
18
|
+
# def throw
|
19
|
+
# puts 'throwing!!'
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# Element.new('throwable').throw => 'throwing!!'
|
25
|
+
#
|
26
|
+
# if symbol name matches a class then method yields to block or returns as array child nodes that matches class
|
27
|
+
# you can further refine search results by adding the symbol of the child instance variable, including name, by which to filter
|
28
|
+
# if block given, returns first child for which block evaluates to true
|
29
|
+
#
|
30
|
+
# e.g.
|
31
|
+
# class Child; end
|
32
|
+
#
|
33
|
+
# n = Element.new('node')
|
34
|
+
# n << 'text'
|
35
|
+
# n << Element.new('child')
|
36
|
+
# n << Element.new('child')
|
37
|
+
# n.Element # returns Array of Element nodes
|
38
|
+
# => [#<Duxml::Element:0x0002 @value="child" ...>,
|
39
|
+
# #<Duxml::Element:0x0003 @value="child" ...>]
|
40
|
+
#
|
41
|
+
# n.Element.each do |child| child << 'some text' end # adding some text
|
42
|
+
# => ['text',
|
43
|
+
# #<Duxml::Element:0x0002 @value="child" ... @nodes=['some text']>,
|
44
|
+
# #<Duxml::Element 0x0003 @value="child" ... @nodes=['some text']>]
|
45
|
+
#
|
46
|
+
# n.Element do |child| child.nodes.first == 'some text' end # returns all children for which block is true
|
47
|
+
# => [#<Duxml::Element:0x0002 @value="child" ... @nodes=['some text']>]
|
48
|
+
#
|
49
|
+
# %w(bar mar).each_with_index do |x, i| next if i.zero?; n.Child[:foo] = x end # adding some attributes
|
50
|
+
# => ['text',
|
51
|
+
# #<Duxml::Element:0x0002 @value="child" @attributes={foo: 'bar'} ...>,
|
52
|
+
# #<Duxml::Element:0x0003 @value="child" @attributes={foo: 'mar'} ...>]
|
53
|
+
#
|
54
|
+
# n.Element(:foo) # returns array of Child nodes with attribute :foo
|
55
|
+
# => [#<Duxml::Element:0x0002 @value="child" @attributes={foo: 'bar'} ...>,
|
56
|
+
# #<Duxml::Element:0x0003 @value="child" @attributes={foo: 'mar'} ...>]
|
57
|
+
#
|
58
|
+
# n.Element(foo: 'bar') # returns array of Child nodes with attribute :foo equal to 'bar'
|
59
|
+
# => [#<Duxml::Element:0xfff @value="child" @attributes={foo: 'bar'} ...>]
|
60
|
+
#
|
61
|
+
#
|
62
|
+
# if element name has no matching Class or Module in namespace,
|
63
|
+
# if symbol is lower case, it is made into a method, given &block as definition, then called with *args
|
64
|
+
# e.g. n.change_color('blue') do |new_color| => #<Duxml::Element:0xfff @value="node" @attributes={color: 'blue'} @nodes=[]>
|
65
|
+
# @color = new_color
|
66
|
+
# self
|
67
|
+
# end
|
68
|
+
# n.color => 'blue'
|
69
|
+
# n.change_color('mauve').color => 'mauve'
|
70
|
+
#
|
71
|
+
# @param sym [Symbol] method, class or module
|
72
|
+
# @param *args [*several_variants] either arguments to method or initializing values for instance of given class
|
73
|
+
# @param &block [block] if yielding result, yields to given block; if defining new method, block defines its contents
|
74
|
+
def method_missing(sym, *args, &block)
|
75
|
+
super(sym, *args, &block)
|
76
|
+
rescue NoMethodError
|
77
|
+
# handling Constant look up to dynamically extend or add to element
|
78
|
+
if lowercase?(sym)
|
79
|
+
if (const = look_up_const) and const.is_a?(Module)
|
80
|
+
extend const
|
81
|
+
result = method(sym).call(*args)
|
82
|
+
return(result) unless block_given?
|
83
|
+
yield(result)
|
84
|
+
elsif block_given?
|
85
|
+
new_method = proc(&block)
|
86
|
+
self.const_set(sym, new_method)
|
87
|
+
return new_method.call *args
|
88
|
+
else
|
89
|
+
raise NoMethodError, "undefined method `#{sym.to_s}' for #{description}"
|
90
|
+
end # if (const = look_up_const) ... elsif block_given? ... else ...
|
91
|
+
else
|
92
|
+
results = filter(sym, args)
|
93
|
+
return results unless block_given?
|
94
|
+
results.keep_if do |node| yield(node) end
|
95
|
+
end # if lowercase? ... else ...
|
96
|
+
end # def method_missing(sym, *args, &block)
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# @param sym [Symbol] indicates which element type is being filtered for
|
101
|
+
# @param args [several_variants] arguments for filtering element children that matched 'sym'
|
102
|
+
# @return [[Element]] Elements of type 'sym' that match criteria 'args'
|
103
|
+
def filter(sym, args)
|
104
|
+
class_nodes = nodes.select do |node|
|
105
|
+
node.name == sym.to_s.nmtokenize or simple_class(node) == sym.to_s
|
106
|
+
end
|
107
|
+
class_nodes.keep_if do |node|
|
108
|
+
if args.empty?
|
109
|
+
true
|
110
|
+
else
|
111
|
+
args.any? do |arg|
|
112
|
+
if arg.is_a?(Hash)
|
113
|
+
node[arg.first.first] == arg.first.last
|
114
|
+
else
|
115
|
+
!node[arg].nil?
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end # if args.empty? ... else ...
|
119
|
+
end # class_nodes.keep_if do |node|
|
120
|
+
end # def filter(args)
|
121
|
+
|
122
|
+
# @param maudule [Module] module context in which to look for duck-called method's module
|
123
|
+
# @return [Module, Class] requested module or class
|
124
|
+
def look_up_const(maudule = Duxml)
|
125
|
+
mod_names = name.split(':')
|
126
|
+
until mod_names.empty?
|
127
|
+
word = mod_names.shift
|
128
|
+
k = word.constantize
|
129
|
+
if maudule.const_defined?(k, true) or Module.const_defined?(simple_class, true)
|
130
|
+
const = maudule.const_get(k)
|
131
|
+
if const.is_a?(Module)
|
132
|
+
maudule = const
|
133
|
+
end
|
134
|
+
|
135
|
+
return const if mod_names.empty? and [Module, Class].include?(const.class)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
|
141
|
+
# @param sym [Symbol] symbol for a constant
|
142
|
+
# @return [Boolean] is symbol lowercase?
|
143
|
+
def lowercase?(sym)
|
144
|
+
sym.to_s[0].match(/[A-Z]/).nil?
|
145
|
+
end
|
146
|
+
|
147
|
+
# @param obj [Object] usually Element
|
148
|
+
# @return [String] name of final Class or Module of self
|
149
|
+
def simple_class(obj=self)
|
150
|
+
obj.class.to_s.split('::').last
|
151
|
+
end
|
152
|
+
end # module LazyOx
|
153
|
+
end # module Duxml
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
2
|
+
|
3
|
+
require 'observer'
|
4
|
+
|
5
|
+
module Duxml
|
6
|
+
# subclass of Array that is Observable by History
|
7
|
+
# used to track changes in String nodes of XML Element
|
8
|
+
class NodeSet < Array
|
9
|
+
include Observable
|
10
|
+
|
11
|
+
@parent
|
12
|
+
|
13
|
+
attr_reader :parent
|
14
|
+
|
15
|
+
# @param _parent [Element] Element that is parent to this NodeSet's elements
|
16
|
+
# @param ary [[String, Element]] child nodes with which to initialize this NodeSet
|
17
|
+
def initialize(_parent, ary=[])
|
18
|
+
super ary
|
19
|
+
@parent = _parent
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [HistoryClass] object that observes this NodeSet for changes
|
23
|
+
def history
|
24
|
+
@observer_peers.first.first if @observer_peers and @observer_peers.first.any?
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param index [Fixnum] index of array where old String is to be replaced
|
28
|
+
# @param str [String] replacing String
|
29
|
+
# @return [self] reports old String and index to history
|
30
|
+
def []=(index, str)
|
31
|
+
raise Exception if count_observers < 1
|
32
|
+
old_str = self[index]
|
33
|
+
super(index, str)
|
34
|
+
changed
|
35
|
+
notify_observers(:ChangeText, parent, index, old_str)
|
36
|
+
self
|
37
|
+
end
|
38
|
+
end # class NodeSet < Array
|
39
|
+
end # module Duxml
|
data/lib/duxml/doc.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/doc/element')
|
4
|
+
|
5
|
+
module Duxml
|
6
|
+
class Doc < ::Ox::Document
|
7
|
+
include ElementGuts
|
8
|
+
def initialize(prolog={})
|
9
|
+
super(prolog)
|
10
|
+
self[:version] ||= '1.0'
|
11
|
+
@nodes = NodeSet.new(self)
|
12
|
+
# TODO should also create new metadata!!
|
13
|
+
end
|
14
|
+
|
15
|
+
def write_to(path)
|
16
|
+
s = attributes.collect do |k, v| %( #{k}="#{v}") end.join
|
17
|
+
%(<?xml #{s}?>)+nodes.first.to_s
|
18
|
+
File.write(path, s)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"#<#{self.class.to_s} @object_id='#{object_id}' @root='#{root.nil? ? '' : root.description}'>"
|
24
|
+
end
|
25
|
+
end # class Document < Element
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../grammar/pattern')
|
4
|
+
|
5
|
+
module Duxml
|
6
|
+
module AttrNamePattern; end
|
7
|
+
# pattern representing relationship between an object and one of its attributes
|
8
|
+
class AttrNamePatternClass < PatternClass
|
9
|
+
include AttrNamePattern
|
10
|
+
|
11
|
+
# @param _subject [Duxml::Element] subject doc
|
12
|
+
# @param _attr_name [String] name of attribute
|
13
|
+
def initialize(_subject, _attr_name)
|
14
|
+
@attr_name = _attr_name
|
15
|
+
super _subject
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :attr_name
|
19
|
+
end
|
20
|
+
|
21
|
+
module AttrNamePattern
|
22
|
+
def relationship
|
23
|
+
'attribute'
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Boolean] true if subject does not have the attr_name; false otherwise
|
27
|
+
def abstract?
|
28
|
+
subject[attr_name].nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def description
|
32
|
+
abstract? ? "#{subject.description} does not have #{relationship} [#{attr_name}]" :
|
33
|
+
"#{subject.description}'s #{relationship} [#{attr_name}]"
|
34
|
+
end
|
35
|
+
end # class AttrNamePattern
|
36
|
+
end # module Duxml
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../grammar/pattern')
|
4
|
+
|
5
|
+
module Duxml
|
6
|
+
module AttrValPattern; end
|
7
|
+
|
8
|
+
# pattern representing relationship between an object's attribute and its value
|
9
|
+
class AttrValPatternClass < PatternClass
|
10
|
+
include AttrValPattern
|
11
|
+
|
12
|
+
# @param _subject [Element] subject element
|
13
|
+
# @param _attr_name [String] name of attribute whose value is the object of this pattern
|
14
|
+
def initialize(_subject, _attr_name)
|
15
|
+
@attr_name = _attr_name
|
16
|
+
super _subject
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :subject, :attr_name
|
20
|
+
end
|
21
|
+
|
22
|
+
module AttrValPattern
|
23
|
+
def relationship
|
24
|
+
'value'
|
25
|
+
end
|
26
|
+
|
27
|
+
def description
|
28
|
+
"#{subject.description}'s @#{attr_name} #{relationship} of '#{value}'"
|
29
|
+
end
|
30
|
+
|
31
|
+
# current value of this attribute
|
32
|
+
def value
|
33
|
+
subject[attr_name]
|
34
|
+
end
|
35
|
+
end # class AttrValPattern
|
36
|
+
end # module Duxml
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../grammar/pattern')
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../../ruby_ext/fixnum')
|
5
|
+
|
6
|
+
module Duxml
|
7
|
+
module ChildPattern; end
|
8
|
+
# pattern representing relationship between an object and its child
|
9
|
+
class ChildPatternClass < PatternClass
|
10
|
+
include ChildPattern
|
11
|
+
|
12
|
+
# @param _subject [Duxml::Element] parent element
|
13
|
+
# @param _index [Fixnum] index of child
|
14
|
+
def initialize(_subject, _index)
|
15
|
+
@index = _index
|
16
|
+
super _subject
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method :parent, :subject
|
20
|
+
attr_reader :index
|
21
|
+
end # class ChildPatternClass
|
22
|
+
|
23
|
+
# null child patterns represent and parent child relationship where the child
|
24
|
+
# is required by the Grammar but the element is missing that child
|
25
|
+
class NullChildPatternClass < PatternClass
|
26
|
+
include ChildPattern
|
27
|
+
|
28
|
+
# @param _subject [Element] parent element
|
29
|
+
# @param _missing_child [String] nmtoken for missing child element
|
30
|
+
def initialize(_subject, _missing_child)
|
31
|
+
@missing_child = _missing_child
|
32
|
+
super _subject
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [-1] class must respond to #index; only NullChildPatternClass is allowed to have a negative index
|
36
|
+
def index
|
37
|
+
-1
|
38
|
+
end
|
39
|
+
|
40
|
+
def relationship
|
41
|
+
'missing child'
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [String] description of this child pattern
|
45
|
+
def description
|
46
|
+
"#{subject.description} #{relationship} <#{child}>"
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_reader :missing_child
|
50
|
+
alias_method :child, :missing_child
|
51
|
+
alias_method :parent, :subject
|
52
|
+
end
|
53
|
+
|
54
|
+
module ChildPattern
|
55
|
+
# @return [Element] child element
|
56
|
+
def child
|
57
|
+
subject.nodes[index]
|
58
|
+
end
|
59
|
+
|
60
|
+
alias_method :object, :child
|
61
|
+
|
62
|
+
# @return [String] describes relationship between parent and child
|
63
|
+
def relationship
|
64
|
+
"#{(index+1).ordinal_name} #{super}"
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [String] description of this child pattern
|
68
|
+
def description
|
69
|
+
"#{subject.description}'s #{relationship} #{child.description}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end # module Duxml
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../grammar/pattern')
|
4
|
+
|
5
|
+
module Duxml
|
6
|
+
module TextPattern; end
|
7
|
+
# pattern representing relationship between an object and its text-only child
|
8
|
+
class TextPatternClass < PatternClass
|
9
|
+
include TextPattern
|
10
|
+
|
11
|
+
# @param _subject [Ox::Element] parent of text node
|
12
|
+
# @param _index [Fixnum] index of text node
|
13
|
+
def initialize(_subject, _index)
|
14
|
+
@index = _index
|
15
|
+
super _subject
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :subject, :index
|
19
|
+
end
|
20
|
+
|
21
|
+
module TextPattern
|
22
|
+
def text
|
23
|
+
subject.nodes[index]
|
24
|
+
end
|
25
|
+
|
26
|
+
def description
|
27
|
+
"#{subject.description}'s #{relationship} is '#{text}'"
|
28
|
+
end
|
29
|
+
end # class ContentPattern
|
30
|
+
end # module Duxml
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Copyright (c) 2016 Freescale Semiconductor Inc.
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../ruby_ext/string')
|
4
|
+
|
5
|
+
module Duxml
|
6
|
+
# Patterns represent a relationship between a specific XML Element and
|
7
|
+
# a specific or hypothetical attribute, attribute value, text value or child Element
|
8
|
+
# For example, given an XML Element: '<element attr='value'/>', there exists a valid PatternClass
|
9
|
+
# that represents the relationship betweeen <element> and the attribute value 'value'
|
10
|
+
# There can also exists a hypothetical relationship between <element> and a new child <child/> that
|
11
|
+
# is defined as an allowed child Element in the Rules that apply to this Element.
|
12
|
+
module Pattern; end
|
13
|
+
|
14
|
+
# as an object, a Pattern consists of a subject and may or may not have an object
|
15
|
+
# a Pattern without an object represents a childless empty node with no attributes
|
16
|
+
# this class must be subclassed to be used; there is one for each type of XML relationship
|
17
|
+
# that an Element can have up to one degree of separation, that is, grandparent relationships are not considered
|
18
|
+
# and neither are the attributes of children
|
19
|
+
class PatternClass
|
20
|
+
include Pattern
|
21
|
+
@subject
|
22
|
+
|
23
|
+
# @param subj [Element] specific Element that is the subject of this pattern
|
24
|
+
def initialize(subj)
|
25
|
+
@subject = subj
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :subject
|
29
|
+
end
|
30
|
+
|
31
|
+
module Pattern
|
32
|
+
include Duxml
|
33
|
+
|
34
|
+
# @return [String] nmtoken name of this pattern without namespace prefix e.g. ChildPattern.new(parent, child).name => 'child_pattern'
|
35
|
+
def simple_name
|
36
|
+
name.split(':').last
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Boolean] if either subject or object points to a name/type i.e. not an Element
|
40
|
+
def abstract?
|
41
|
+
subject.is_a?(String) or object.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Boolean] if both subject and at least one object point to an actual XML Element
|
45
|
+
def concrete?
|
46
|
+
!abstract?
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [String] nmtoken name of this pattern e.g. ChildPattern.new(parent, child).name => 'duxml:child_pattern'
|
50
|
+
def name
|
51
|
+
c = self.class.to_s
|
52
|
+
return c.nmtokenize unless c.include?('::')
|
53
|
+
a = c.split('::')
|
54
|
+
a[-2..-1].collect do |word|
|
55
|
+
word.nmtokenize
|
56
|
+
end.join(':')
|
57
|
+
end
|
58
|
+
|
59
|
+
# returns relationship description as string by subtracting super class name
|
60
|
+
# (e.g. 'pattern' or 'rule') from simple_class
|
61
|
+
# Duxml::ChildrenRule#relationship => 'children'
|
62
|
+
# Duxml::TextPattern#relationship => 'text'
|
63
|
+
# can be overridden if class name does not match human-readable string
|
64
|
+
# @return [String] single word to describe relationship of subject to object
|
65
|
+
def relationship
|
66
|
+
simple_name.split('_').first
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [String] "#{object.description} is #{relationship} of #{subject.description}"
|
70
|
+
def description
|
71
|
+
"#{object.description} is #{relationship} of #{subject.description}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Element] will only return non-nil value when pattern represents relationship with a child Element
|
75
|
+
def object
|
76
|
+
return @object if instance_variable_defined?(:@object)
|
77
|
+
instance_variables.each do |var|
|
78
|
+
target = instance_variable_get(var)
|
79
|
+
return target unless target.is_a?(String) or target == subject
|
80
|
+
end
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param pattern [Duxml::Pattern] pattern or any subclass object
|
85
|
+
# @return [Fixnum] first applies <=> to subjects, and if equal, applies <=> to objects
|
86
|
+
def <=>(pattern)
|
87
|
+
return 1 unless pattern.respond_to?(:subject)
|
88
|
+
case subject <=> pattern.subject
|
89
|
+
when -1 then
|
90
|
+
-1
|
91
|
+
when 0 then
|
92
|
+
object <=> pattern.object
|
93
|
+
else
|
94
|
+
-1
|
95
|
+
end
|
96
|
+
end # def <=>
|
97
|
+
end # module Pattern
|
98
|
+
end # module Duxml
|