duxml 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|