duxml 0.8.8 → 0.8.9

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/bin/validate_xml +30 -30
  3. data/lib/duxml.rb +76 -76
  4. data/lib/duxml/doc.rb +108 -91
  5. data/lib/duxml/doc/element.rb +250 -250
  6. data/lib/duxml/doc/lazy_ox.rb +167 -167
  7. data/lib/duxml/doc/node_set.rb +38 -38
  8. data/lib/duxml/meta.rb +72 -72
  9. data/lib/duxml/meta/grammar.rb +133 -133
  10. data/lib/duxml/meta/grammar/pattern.rb +111 -111
  11. data/lib/duxml/meta/grammar/pattern/attr_name_pattern.rb +36 -36
  12. data/lib/duxml/meta/grammar/pattern/attr_val_pattern.rb +36 -36
  13. data/lib/duxml/meta/grammar/pattern/child_pattern.rb +60 -60
  14. data/lib/duxml/meta/grammar/pattern/text_pattern.rb +31 -31
  15. data/lib/duxml/meta/grammar/pattern_maker.rb +68 -68
  16. data/lib/duxml/meta/grammar/relax_ng.rb +39 -39
  17. data/lib/duxml/meta/grammar/relax_ng/attrs_rule.rb +58 -58
  18. data/lib/duxml/meta/grammar/relax_ng/children_rule.rb +82 -82
  19. data/lib/duxml/meta/grammar/relax_ng/value_rule.rb +44 -44
  20. data/lib/duxml/meta/grammar/rule.rb +58 -58
  21. data/lib/duxml/meta/grammar/rule/attrs_rule.rb +77 -77
  22. data/lib/duxml/meta/grammar/rule/children_rule.rb +135 -135
  23. data/lib/duxml/meta/grammar/rule/text_rule.rb +39 -39
  24. data/lib/duxml/meta/grammar/rule/value_rule.rb +110 -110
  25. data/lib/duxml/meta/grammar/spreadsheet.rb +34 -34
  26. data/lib/duxml/meta/history.rb +88 -88
  27. data/lib/duxml/meta/history/add.rb +30 -30
  28. data/lib/duxml/meta/history/change.rb +70 -70
  29. data/lib/duxml/meta/history/change_attr.rb +33 -33
  30. data/lib/duxml/meta/history/change_text.rb +32 -32
  31. data/lib/duxml/meta/history/error.rb +24 -24
  32. data/lib/duxml/meta/history/new_attr.rb +32 -32
  33. data/lib/duxml/meta/history/new_text.rb +36 -36
  34. data/lib/duxml/meta/history/qualify_error.rb +21 -21
  35. data/lib/duxml/meta/history/remove.rb +27 -27
  36. data/lib/duxml/meta/history/undo.rb +23 -23
  37. data/lib/duxml/meta/history/validate_error.rb +20 -20
  38. data/lib/duxml/reportable.rb +28 -28
  39. data/lib/duxml/ruby_ext/fixnum.rb +55 -55
  40. data/lib/duxml/ruby_ext/module.rb +11 -11
  41. data/lib/duxml/ruby_ext/object.rb +13 -13
  42. data/lib/duxml/ruby_ext/regexp.rb +19 -19
  43. data/lib/duxml/ruby_ext/string.rb +37 -37
  44. data/lib/duxml/saxer.rb +75 -75
  45. metadata +12 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2112e7514be18ba981441e3bf2ce709214022103
4
- data.tar.gz: c937e76e2c260d688f7607078f4c924224ef1c16
3
+ metadata.gz: ad78564c059484c71fa9c061134ba7534da88a5f
4
+ data.tar.gz: 5f4d4c73d8098085722ed2116173642d3a9996a6
5
5
  SHA512:
6
- metadata.gz: 961905236877bba259f7331b993417b381f97ed0307bddb2206a16409c3ad630d1c1d814b50008d562062cc9471ac06ff920f63e3575df06e6baaa5819965f2d
7
- data.tar.gz: d0c73ee66fc501bacd47cae5ddbee3117824154962cd41695d504b59c5b76181dcf26b389ce003089bfbee79fe87aed2c1458bb1b44b52d03c2c594c35becf8a
6
+ metadata.gz: c6f8d8e57be9041a357fd1067665907726910de00573c4a0647c8cb4b04d3ce29eea68314b61951bdf1384ea18054b8d28da23872caa9d9587ff5f36e11bd230
7
+ data.tar.gz: 903c91e7740a7093764c4405bd62dc29d21ff661f381def99b34eef087785b240f98e30ae06d09ead3f704cee471a916410e7c2b265807cd26172b4e47cbb404
@@ -1,31 +1,31 @@
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}"
20
-
21
-
22
- if conditions
23
-
24
- end
25
-
26
-
27
- def conditions
28
- asdf and b and c
29
- end
30
-
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}"
20
+
21
+
22
+ if conditions
23
+
24
+ end
25
+
26
+
27
+ def conditions
28
+ asdf and b and c
29
+ end
30
+
31
31
  File
@@ -1,76 +1,76 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
-
3
- require File.expand_path(File.dirname(__FILE__) + '/duxml/saxer')
4
-
5
- module Duxml
6
- include Saxer
7
-
8
- # most recently processed document - default location for validated documents that are not loaded explicitly
9
- @doc
10
-
11
- attr_reader :doc
12
-
13
- # @param _file [String] loads XML file from given path and finds or creates corresponding metadata file e.g. '.xml_file.duxml'
14
- # @param grammar_path [nil, String, Duxml::Grammar] optional - provide an external grammar file or object
15
- # @return [Doc] XML document as Ruby object
16
- def load(_file, grammar_path=nil)
17
- meta_path = Meta.meta_path(_file)
18
-
19
- if File.exists?(meta_path)
20
- meta = Ox.parse_obj(File.read meta_path)
21
- meta.grammar = grammar_path unless grammar_path.nil? or meta.grammar.defined?
22
- else
23
- meta = MetaClass.new(grammar_path)
24
- end
25
-
26
- if File.exists?(_file)
27
- @doc = sax(_file, meta.history)
28
- else
29
- @doc = Doc.new
30
- @doc.path = _file
31
-
32
- doc.delete_observers if doc.count_observers > 0 and doc.observer_peers.first.object_id != meta.history.object_id
33
- doc.add_observer meta.history if doc.count_observers < 1
34
- end
35
- doc.set_meta meta
36
- end # def load
37
-
38
- # @param file [String] saves current content XML to given file path
39
- # @param xml [Doc, Element] current Doc by default, but can also be unattached XML Element
40
- def save(file, xml=doc)
41
- @doc = xml.respond_to?(:root) ? xml : Doc.new << xml
42
- doc.write_to file
43
- File.write(Meta.meta_path(file), Ox.dump(doc.meta, circular: true))
44
- end
45
-
46
- # @param file [String] output file path for logging human-readable validation error messages
47
- def log(file)
48
- File.write(file, meta.history.description)
49
- end
50
-
51
- # @param *Args [String, Doc] if string then path to load Doc, else Doc to validate
52
- # @param options [Hash, Symbol] currently just supports suppression of console output of results with :quiet
53
- # @return [Boolean] whether file passed validation
54
- def validate(path_or_doc, options={})
55
- doc = path_or_doc.is_a?(Doc) ? path_or_doc : load(path_or_doc)
56
- unless doc.grammar.defined?
57
- raise Exception, "grammar not defined!" unless options[:grammar]
58
- doc.grammar = options[:grammar]
59
- end
60
- raise Exception, "XML document has no root element!" unless doc.root
61
- results = []
62
- doc.root.traverse do |n| results << doc.grammar.validate(n) unless n.is_a?(String) end
63
- puts(doc.history.description) unless options[:quiet]
64
- !results.any? do |r| !r end
65
- end # def validate
66
-
67
- private
68
- # @param g_path [String, GrammarClass] grammar to associate with @doc, which must be set to a Doc and not nil
69
- def associate_grammar(g_path)
70
- doc.grammar = g_path if g_path
71
- end
72
-
73
- def find_meta
74
-
75
- end
76
- end # module Duxml
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/duxml/saxer')
4
+
5
+ module Duxml
6
+ include Saxer
7
+
8
+ # most recently processed document - default location for validated documents that are not loaded explicitly
9
+ @doc
10
+
11
+ attr_reader :doc
12
+
13
+ # @param _file [String] loads XML file from given path and finds or creates corresponding metadata file e.g. '.xml_file.duxml'
14
+ # @param grammar_path [nil, String, Duxml::Grammar] optional - provide an external grammar file or object
15
+ # @return [Doc] XML document as Ruby object
16
+ def load(_file, grammar_path=nil)
17
+ meta_path = Meta.meta_path(_file)
18
+
19
+ if File.exists?(meta_path)
20
+ meta = Ox.parse_obj(File.read meta_path)
21
+ meta.grammar = grammar_path unless grammar_path.nil? or meta.grammar.defined?
22
+ else
23
+ meta = MetaClass.new(grammar_path)
24
+ end
25
+
26
+ if File.exists?(_file)
27
+ @doc = sax(_file, meta.history)
28
+ else
29
+ @doc = Doc.new
30
+ @doc.path = _file
31
+
32
+ doc.delete_observers if doc.count_observers > 0 and doc.observer_peers.first.object_id != meta.history.object_id
33
+ doc.add_observer meta.history if doc.count_observers < 1
34
+ end
35
+ doc.set_meta meta
36
+ end # def load
37
+
38
+ # @param file [String] saves current content XML to given file path
39
+ # @param xml [Doc, Element] current Doc by default, but can also be unattached XML Element
40
+ def save(file, xml=doc)
41
+ @doc = xml.respond_to?(:root) ? xml : Doc.new << xml
42
+ doc.write_to file
43
+ File.write(Meta.meta_path(file), Ox.dump(doc.meta, circular: true))
44
+ end
45
+
46
+ # @param file [String] output file path for logging human-readable validation error messages
47
+ def log(file)
48
+ File.write(file, meta.history.description)
49
+ end
50
+
51
+ # @param *Args [String, Doc] if string then path to load Doc, else Doc to validate
52
+ # @param options [Hash, Symbol] currently just supports suppression of console output of results with :quiet
53
+ # @return [Boolean] whether file passed validation
54
+ def validate(path_or_doc, options={})
55
+ doc = path_or_doc.is_a?(Doc) ? path_or_doc : load(path_or_doc)
56
+ unless doc.grammar.defined?
57
+ raise Exception, "grammar not defined!" unless options[:grammar]
58
+ doc.grammar = options[:grammar]
59
+ end
60
+ raise Exception, "XML document has no root element!" unless doc.root
61
+ results = []
62
+ doc.root.traverse do |n| results << doc.grammar.validate(n) unless n.is_a?(String) end
63
+ puts(doc.history.description) unless options[:quiet]
64
+ !results.any? do |r| !r end
65
+ end # def validate
66
+
67
+ private
68
+ # @param g_path [String, GrammarClass] grammar to associate with @doc, which must be set to a Doc and not nil
69
+ def associate_grammar(g_path)
70
+ doc.grammar = g_path if g_path
71
+ end
72
+
73
+ def find_meta
74
+
75
+ end
76
+ end # module Duxml
@@ -1,92 +1,109 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
-
3
- require File.expand_path(File.dirname(__FILE__) + '/doc/element')
4
- require File.expand_path(File.dirname(__FILE__) + '/meta')
5
-
6
- module Duxml
7
- class Doc < ::Ox::Document
8
- include ElementGuts
9
-
10
- # path of file where this Doc is saved to
11
- @path
12
-
13
- # meta data for this Doc; contains reference to grammar if it exists and history
14
- @meta
15
-
16
- attr_reader :meta, :path
17
-
18
- def initialize(prolog={})
19
- super(prolog)
20
- self[:version] ||= '1.0'
21
- @meta = MetaClass.new
22
- @nodes = NodeSet.new(self)
23
- add_observer meta.history
24
- end
25
-
26
- # @param _path [String] assigns file path to document, creating it if it does not already exist along with metadata file
27
- def path=(_path)
28
- @path = _path
29
- set_meta _path unless path
30
- unless File.exists?(path)
31
- write_to(path)
32
- end
33
- end
34
-
35
- # @return [String] summary of XML document as Ruby object and description of root element
36
- def to_s
37
- "#<#{self.class.to_s} @object_id='#{object_id}' @root='#{root.nil? ? '' : root.description}'>"
38
- end
39
-
40
- # @param path_or_obj [String, MetaClass] metadata object itself or path of metadata for this file; if none given, saves existing metadata to file using @path
41
- # @return [Doc] self
42
- def set_meta(path_or_obj=nil)
43
- @meta = case path_or_obj
44
- when MetaClass, Element then path_or_obj
45
- when String && File.exists?(path_or_obj)
46
- Ox.parse_obj(path_or_obj)
47
- else
48
- File.write(Meta.meta_path(path), Ox.dump(meta)) if path
49
- meta
50
- end
51
- self
52
- end
53
-
54
- # shortcut method @see Meta#grammar
55
- def grammar
56
- meta.grammar
57
- end
58
-
59
- # shortcut method @see Meta#grammar=
60
- def grammar=(grammar_or_file)
61
- meta.grammar = grammar_or_file
62
- end
63
-
64
- # shortcut method @see Meta#history
65
- def history
66
- meta.history
67
- end
68
-
69
- # @return [String] one word description of what this object is: 'document'
70
- def description
71
- 'document'
72
- end
73
-
74
- # @param path [String] document's file path
75
- # @return [Doc] returns self after writing contents to file
76
- def write_to(path)
77
- s = attributes.collect do |k, v| %( #{k}="#{v}") end.join
78
- File.write(path, %(<?xml #{s}?>\n) + root.to_s)
79
- x = meta.xml
80
- File.write(Meta.meta_path(path), meta.xml.to_s)
81
- self
82
- end
83
-
84
- def <<(obj)
85
- super(obj)
86
- obj.set_doc! self
87
- self
88
- end
89
- end # class Document < Element
90
- end
91
-
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/doc/element')
4
+ require File.expand_path(File.dirname(__FILE__) + '/meta')
5
+
6
+ module Duxml
7
+ class Doc < ::Ox::Document
8
+ include ElementGuts
9
+
10
+ # path of file where this Doc is saved to
11
+ @path
12
+
13
+ # meta data for this Doc; contains reference to grammar if it exists and history
14
+ @meta
15
+
16
+ # hash of all unique-id elements within document; gets initialized as each is searched for
17
+ @id_hash
18
+
19
+ attr_reader :meta, :path, :id_hash
20
+
21
+ def initialize(prolog={})
22
+ super(prolog)
23
+ self[:version] ||= '1.0'
24
+ @id_hash = {}
25
+ @meta = MetaClass.new
26
+ @nodes = NodeSet.new(self)
27
+ add_observer meta.history
28
+ end
29
+
30
+ # @param _path [String] assigns file path to document, creating it if it does not already exist along with metadata file
31
+ def path=(_path)
32
+ @path = _path
33
+ set_meta _path unless path
34
+ unless File.exists?(path)
35
+ write_to(path)
36
+ end
37
+ end
38
+
39
+ # @return [String] summary of XML document as Ruby object and description of root element
40
+ def to_s
41
+ "#<#{self.class.to_s} @object_id='#{object_id}' @root='#{root.nil? ? '' : root.description}'>"
42
+ end
43
+
44
+ # @param path_or_obj [String, MetaClass] metadata object itself or path of metadata for this file; if none given, saves existing metadata to file using @path
45
+ # @return [Doc] self
46
+ def set_meta(path_or_obj=nil)
47
+ @meta = case path_or_obj
48
+ when MetaClass, Element then path_or_obj
49
+ when String && File.exists?(path_or_obj)
50
+ Ox.parse_obj(path_or_obj)
51
+ else
52
+ File.write(Meta.meta_path(path), Ox.dump(meta)) if path
53
+ meta
54
+ end
55
+ self
56
+ end
57
+
58
+ # shortcut method @see Meta#grammar
59
+ def grammar
60
+ meta.grammar
61
+ end
62
+
63
+ # shortcut method @see Meta#grammar=
64
+ def grammar=(grammar_or_file)
65
+ meta.grammar = grammar_or_file
66
+ end
67
+
68
+ # shortcut method @see Meta#history
69
+ def history
70
+ meta.history
71
+ end
72
+
73
+ # @return [String] one word description of what this object is: 'document'
74
+ def description
75
+ 'document'
76
+ end
77
+
78
+ # @param path [String] document's file path
79
+ # @return [Doc] returns self after writing contents to file
80
+ def write_to(path)
81
+ s = attributes.collect do |k, v| %( #{k}="#{v}") end.join
82
+ File.write(path, %(<?xml #{s}?>\n) + root.to_s)
83
+ x = meta.xml
84
+ File.write(Meta.meta_path(path), meta.xml.to_s)
85
+ self
86
+ end
87
+
88
+ def <<(obj)
89
+ super(obj)
90
+ obj.set_doc! self
91
+ self
92
+ end
93
+
94
+ # @param id [String, Symbol] document-unique id attribute value
95
+ # @return [Element, NilClass] found element or nil if not found
96
+ def find_by_id(id)
97
+ id_str = id.to_s
98
+ return @id_hash[id_str] if @id_hash[id_str]
99
+ root.traverse do |node|
100
+ if node.respond_to?(:nodes) and node[:id] == id_str
101
+ return @id_hash[id_str] = node
102
+ end
103
+ end
104
+ nil
105
+ end
106
+ end # class Document < Element
107
+ end
108
+
92
109
  Hash
@@ -1,251 +1,251 @@
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
- # line number
22
- @line
23
-
24
- # column
25
- @column
26
-
27
- # document to which this Element belongs
28
- @doc
29
-
30
- # operates in two modes:
31
- # - from Ruby
32
- # - from file
33
- # in file mode, args provide Element's line and column location then freezes each Fixnum so it cannot be overwritten
34
- # in Ruby mode, args are some combination of new attributes/values and/or child nodes (text or XML) with which to initialize this node
35
- #
36
- # @param name [String] name of element, in both Ruby and file modes
37
- # @param _line_or_content [Fixnum, Array, Hash] line number of element file mode; if Array, new child nodes; if Hash, attributes; can be nil
38
- # @param _col_or_children [Fixnum, Array] column position in file mode; if Array, new child nodes; can be nil
39
- # @return [Element] new XML Element
40
- def initialize(name, _line_or_content=nil, _col_or_children=nil)
41
- super name
42
- @line = _line_or_content if _line_or_content.respond_to?(:%)
43
- _line_or_content.each do |k,v| self[k] = v end if _line_or_content.respond_to?(:key)
44
- @nodes = NodeSet.new(self, _line_or_content) if _line_or_content.respond_to?(:pop) && _col_or_children.nil?
45
- @column = _col_or_children if _col_or_children.respond_to?(:%)
46
- @nodes = NodeSet.new(self, _col_or_children) if _col_or_children.respond_to?(:pop)
47
- @nodes = NodeSet.new(self) if @nodes.empty?
48
- end
49
-
50
- attr_reader :line, :column, :doc
51
-
52
- attr_accessor :nodes
53
- end
54
-
55
- module ElementGuts
56
- # @return [Boolean] whether or not this has been written to file
57
- def abstract?
58
- line < 0 || column < 0
59
- end
60
-
61
- # @param _doc [Doc] document to which this element belongs - recursively applies to all descendants of this node
62
- # @return [Element] self
63
- def set_doc!(_doc)
64
- @doc = _doc
65
- traverse do |node|
66
- next if node === self or node.is_a?(String)
67
- node.set_doc!(_doc)
68
- end
69
- self
70
- end
71
-
72
- # @see Ox::Element#<<
73
- # this override reports changes to history; NewText for Strings, Add for elements
74
- #
75
- # @param obj [Element, Array] element or string to add to this Element; can insert arrays which are always inserted in order
76
- # @return [Element] self
77
- def <<(obj)
78
- add(obj)
79
- end
80
-
81
- # @see #<<
82
- # this version of the method allows insertions between existing elements
83
- #
84
- # @param obj [Element, Array] element or string to add to this Element; can insert arrays which are always inserted in order
85
- # @param index [Fixnum] index at which to insert new node; inserts at end of element by default; when inserting arrays, index is incremented for each item to avoid reversing array order
86
- # @return [Element] self
87
- def add(obj, index=-1)
88
- case
89
- when obj.is_a?(Array), obj.is_a?(NodeSet)
90
- obj.each_with_index do |e, i|
91
- add(e, index == -1 ? index : index+i)
92
- end
93
- when obj.is_a?(String)
94
- if obj[0] == '<' and obj[-1] == '>' and (s = Ox.parse(obj))
95
- add dclone s
96
- else
97
- type = :NewText
98
- nodes.insert(index, obj)
99
- end
100
- else
101
- type = :Add
102
- nodes.insert(index, obj)
103
- if obj.count_observers < 1 && @observer_peers
104
- obj.add_observer(@observer_peers.first.first)
105
- end
106
- obj.set_doc! @doc
107
- end
108
- report(type, obj, index)
109
- self
110
- end
111
-
112
- # @param index_or_attr [String, Symbol, Fixnum] string or symbol of attribute or index of child node
113
- # @return [Element, String] string if attribute value or text node; Element if XML node
114
- def [](index_or_attr)
115
- index_or_attr.is_a?(Fixnum) ? nodes[index_or_attr] : super(index_or_attr)
116
- end
117
-
118
- # @param attr_sym [String, Symbol, Fixnum] name of attribute or index of child to replace
119
- # @param val [String] new attribute value or replacement child node
120
- # @return [Element] self
121
- def []=(attr_sym, val)
122
- if attr_sym.is_a?(Fixnum)
123
- remove nodes[attr_sym]
124
- add(val, attr_sym)
125
- return self
126
- end
127
- attr = attr_sym.to_s
128
- raise "argument to [] must be a Symbol or a String." unless attr.is_a?(Symbol) or attr.is_a?(String)
129
- args = [attr]
130
- args << attributes[attr] if attributes[attr]
131
- super(attr, val)
132
- type = args.size == 1 ? :NewAttr : :ChangeAttr
133
- report(type, *args)
134
- self
135
- end
136
-
137
- # @return [String] self description
138
- def description
139
- "<#{name}>"
140
- end
141
-
142
- # @return [Element] copy of this Element but with no children
143
- def stub
144
- Element.new(name, attributes)
145
- end
146
-
147
- # @return [HistoryClass] history that is observing this element for changes
148
- def history
149
- @observer_peers.first.first if @observer_peers.respond_to?(:any?) and @observer_peers.any? and @observer_peers.first.any?
150
- end
151
-
152
- # @return [String] XML string (overrides Ox's to_s which just prints the object pointer)
153
- def to_s
154
- s = %(<#{name})
155
- attributes.each do |k,v| s << %( #{k.to_s}="#{v}") end
156
- return s+'/>' if nodes.empty?
157
- s << ">#{nodes.collect do |n| n.to_s end.join}</#{name}>"
158
- end
159
-
160
- # @return #to_s
161
- def inspect
162
- to_s
163
- end
164
-
165
- # TODO do we need this method to take Fixnum node index as well?
166
- # @param obj [Element] element child to delete
167
- # @return [Element] deleted element
168
- def delete(obj)
169
- report(:Remove, @nodes.delete(obj))
170
- obj
171
- end
172
-
173
- alias_method :remove, :delete
174
-
175
- # pre-order traverse through this node and all of its descendants
176
- #
177
- # @param &block [block] code to execute for each yielded node
178
- def traverse(node=nil, &block)
179
- return self.to_enum unless block_given?
180
- node_stack = [node || self]
181
-
182
- until node_stack.empty?
183
- current = node_stack.shift
184
- if current
185
- yield current
186
- node_stack = node_stack.insert(0, *current.nodes) if current.respond_to?(:nodes)
187
- end
188
- end
189
-
190
- node || self if block_given?
191
- end
192
-
193
- # traverse through just the children of this node
194
- #
195
- # @param &block [block] code to execute for each child node
196
- def each(&block)
197
- @nodes.each(&block)
198
- end
199
-
200
- # @return [String] namespace of element, derived from name e.g. '<duxml:element>' => 'duxml'
201
- def name_space
202
- return nil unless (i = name.index(':'))
203
- name[0..i-1]
204
- end
205
-
206
-
207
- # @param source [Element] if not explicitly provided, creates deep clone of this element; source can be any XML element (not only Duxml) that responds to traverse with pre-order traversal
208
- # @return [Element] deep clone of source, including its attributes and recursively cloned children
209
- def dclone(source = self)
210
- input_stack = []
211
- output_stack = []
212
- traverse(source) do |node|
213
- if node.is_a?(String)
214
- output_stack.last << node
215
- next
216
- end
217
- copy = Element.new(node.name, node.attributes)
218
- if output_stack.empty?
219
- output_stack << copy
220
- input_stack << node
221
- else
222
-
223
- if input_stack.last.nodes.none? do |n| n === node end
224
- input_stack.pop
225
- output_stack.pop
226
- end
227
-
228
- output_stack.last << copy
229
- if node.nodes.any?
230
- output_stack << copy
231
- input_stack << node
232
- end
233
-
234
- end
235
- end
236
- output_stack.pop
237
- end
238
-
239
- # @return [Element] shallow clone of this element, with all attributes and children only if none are Elements
240
- def sclone
241
- stub = Element.new(name, attributes)
242
- stub << nodes if text?
243
- stub
244
- end
245
-
246
- # @return [true, false] true if all child nodes are text only; false if any child nodes are XML
247
- def text?
248
- nodes.all? do |node| node.is_a?(String) end
249
- end
250
- end # class Element < Node
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
+ # line number
22
+ @line
23
+
24
+ # column
25
+ @column
26
+
27
+ # document to which this Element belongs
28
+ @doc
29
+
30
+ # operates in two modes:
31
+ # - from Ruby
32
+ # - from file
33
+ # in file mode, args provide Element's line and column location then freezes each Fixnum so it cannot be overwritten
34
+ # in Ruby mode, args are some combination of new attributes/values and/or child nodes (text or XML) with which to initialize this node
35
+ #
36
+ # @param name [String] name of element, in both Ruby and file modes
37
+ # @param _line_or_content [Fixnum, Array, Hash] line number of element file mode; if Array, new child nodes; if Hash, attributes; can be nil
38
+ # @param _col_or_children [Fixnum, Array] column position in file mode; if Array, new child nodes; can be nil
39
+ # @return [Element] new XML Element
40
+ def initialize(name, _line_or_content=nil, _col_or_children=nil)
41
+ super name
42
+ @line = _line_or_content if _line_or_content.respond_to?(:%)
43
+ _line_or_content.each do |k,v| self[k] = v end if _line_or_content.respond_to?(:key)
44
+ @nodes = NodeSet.new(self, _line_or_content) if _line_or_content.respond_to?(:pop) && _col_or_children.nil?
45
+ @column = _col_or_children if _col_or_children.respond_to?(:%)
46
+ @nodes = NodeSet.new(self, _col_or_children) if _col_or_children.respond_to?(:pop)
47
+ @nodes = NodeSet.new(self) if @nodes.empty?
48
+ end
49
+
50
+ attr_reader :line, :column, :doc
51
+
52
+ attr_accessor :nodes
53
+ end
54
+
55
+ module ElementGuts
56
+ # @return [Boolean] whether or not this has been written to file
57
+ def abstract?
58
+ line < 0 || column < 0
59
+ end
60
+
61
+ # @param _doc [Doc] document to which this element belongs - recursively applies to all descendants of this node
62
+ # @return [Element] self
63
+ def set_doc!(_doc)
64
+ @doc = _doc
65
+ traverse do |node|
66
+ next if node === self or node.is_a?(String)
67
+ node.set_doc!(_doc)
68
+ end
69
+ self
70
+ end
71
+
72
+ # @see Ox::Element#<<
73
+ # this override reports changes to history; NewText for Strings, Add for elements
74
+ #
75
+ # @param obj [Element, Array] element or string to add to this Element; can insert arrays which are always inserted in order
76
+ # @return [Element] self
77
+ def <<(obj)
78
+ add(obj)
79
+ end
80
+
81
+ # @see #<<
82
+ # this version of the method allows insertions between existing elements
83
+ #
84
+ # @param obj [Element, Array] element or string to add to this Element; can insert arrays which are always inserted in order
85
+ # @param index [Fixnum] index at which to insert new node; inserts at end of element by default; when inserting arrays, index is incremented for each item to avoid reversing array order
86
+ # @return [Element] self
87
+ def add(obj, index=-1)
88
+ case
89
+ when obj.is_a?(Array), obj.is_a?(NodeSet)
90
+ obj.each_with_index do |e, i|
91
+ add(e, index == -1 ? index : index+i)
92
+ end
93
+ when obj.is_a?(String)
94
+ if obj[0] == '<' and obj[-1] == '>' and (s = Ox.parse(obj))
95
+ add dclone s
96
+ else
97
+ type = :NewText
98
+ nodes.insert(index, obj)
99
+ end
100
+ else
101
+ type = :Add
102
+ nodes.insert(index, obj)
103
+ if obj.count_observers < 1 && @observer_peers
104
+ obj.add_observer(@observer_peers.first.first)
105
+ end
106
+ obj.set_doc! @doc
107
+ end
108
+ report(type, obj, index)
109
+ self
110
+ end
111
+
112
+ # @param index_or_attr [String, Symbol, Fixnum] string or symbol of attribute or index of child node
113
+ # @return [Element, String] string if attribute value or text node; Element if XML node
114
+ def [](index_or_attr)
115
+ index_or_attr.is_a?(Fixnum) ? nodes[index_or_attr] : super(index_or_attr)
116
+ end
117
+
118
+ # @param attr_sym [String, Symbol, Fixnum] name of attribute or index of child to replace
119
+ # @param val [String] new attribute value or replacement child node
120
+ # @return [Element] self
121
+ def []=(attr_sym, val)
122
+ if attr_sym.is_a?(Fixnum)
123
+ remove nodes[attr_sym]
124
+ add(val, attr_sym) if val
125
+ return self
126
+ end
127
+ attr = attr_sym.to_s
128
+ raise "argument to [] must be a Symbol or a String." unless attr.is_a?(Symbol) or attr.is_a?(String)
129
+ args = [attr]
130
+ args << attributes[attr] if attributes[attr]
131
+ val.nil? ? @attributes.delete(attr) : super(attr, val)
132
+ type = args.size == 1 ? :NewAttr : :ChangeAttr
133
+ report(type, *args)
134
+ self
135
+ end
136
+
137
+ # @return [String] self description
138
+ def description
139
+ "<#{name}>"
140
+ end
141
+
142
+ # @return [Element] copy of this Element but with no children
143
+ def stub
144
+ Element.new(name, attributes)
145
+ end
146
+
147
+ # @return [HistoryClass] history that is observing this element for changes
148
+ def history
149
+ @observer_peers.first.first if @observer_peers.respond_to?(:any?) and @observer_peers.any? and @observer_peers.first.any?
150
+ end
151
+
152
+ # @return [String] XML string (overrides Ox's to_s which just prints the object pointer)
153
+ def to_s
154
+ s = %(<#{name})
155
+ attributes.each do |k,v| s << %( #{k.to_s}="#{v}") end
156
+ return s+'/>' if nodes.empty?
157
+ s << ">#{nodes.collect do |n| n.to_s end.join}</#{name}>"
158
+ end
159
+
160
+ # @return #to_s
161
+ def inspect
162
+ to_s
163
+ end
164
+
165
+ # TODO do we need this method to take Fixnum node index as well?
166
+ # @param obj [Element] element child to delete
167
+ # @return [Element] deleted element
168
+ def delete(obj)
169
+ report(:Remove, @nodes.delete(obj))
170
+ obj
171
+ end
172
+
173
+ alias_method :remove, :delete
174
+
175
+ # pre-order traverse through this node and all of its descendants
176
+ #
177
+ # @param &block [block] code to execute for each yielded node
178
+ def traverse(node=nil, &block)
179
+ return self.to_enum unless block_given?
180
+ node_stack = [node || self]
181
+
182
+ until node_stack.empty?
183
+ current = node_stack.shift
184
+ if current
185
+ yield current
186
+ node_stack = node_stack.insert(0, *current.nodes) if current.respond_to?(:nodes)
187
+ end
188
+ end
189
+
190
+ node || self if block_given?
191
+ end
192
+
193
+ # traverse through just the children of this node
194
+ #
195
+ # @param &block [block] code to execute for each child node
196
+ def each(&block)
197
+ @nodes.each(&block)
198
+ end
199
+
200
+ # @return [String] namespace of element, derived from name e.g. '<duxml:element>' => 'duxml'
201
+ def name_space
202
+ return nil unless (i = name.index(':'))
203
+ name[0..i-1]
204
+ end
205
+
206
+
207
+ # @param source [Element] if not explicitly provided, creates deep clone of this element; source can be any XML element (not only Duxml) that responds to traverse with pre-order traversal
208
+ # @return [Element] deep clone of source, including its attributes and recursively cloned children
209
+ def dclone(source = self)
210
+ input_stack = []
211
+ output_stack = []
212
+ traverse(source) do |node|
213
+ if node.is_a?(String)
214
+ output_stack.last << node
215
+ next
216
+ end
217
+ copy = Element.new(node.name, node.attributes)
218
+ if output_stack.empty?
219
+ output_stack << copy
220
+ input_stack << node
221
+ else
222
+
223
+ if input_stack.last.nodes.none? do |n| n === node end
224
+ input_stack.pop
225
+ output_stack.pop
226
+ end
227
+
228
+ output_stack.last << copy
229
+ if node.nodes.any?
230
+ output_stack << copy
231
+ input_stack << node
232
+ end
233
+
234
+ end
235
+ end
236
+ output_stack.pop
237
+ end
238
+
239
+ # @return [Element] shallow clone of this element, with all attributes and children only if none are Elements
240
+ def sclone
241
+ stub = Element.new(name, attributes)
242
+ stub << nodes if text?
243
+ stub
244
+ end
245
+
246
+ # @return [true, false] true if all child nodes are text only; false if any child nodes are XML
247
+ def text?
248
+ nodes.all? do |node| node.is_a?(String) end
249
+ end
250
+ end # class Element < Node
251
251
  end # module Ox