duxml 0.8.8 → 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
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