3
## 1998-2000 by yoshidam
5
## XPointer support is contributed by Masaki Fukushima
6
## <fukusima@goto.info.waseda.ac.jp>
13
= XML::DOM (XML::SimpleTree)
31
replace character '&','<','>',"'",'"' in string s to character reference.
36
str.gsub!("&", "&")
37
str.gsub!("<", "<")
38
str.gsub!(">", ">")
39
str.gsub!("'", "'")
40
str.gsub!('"', """)
48
Constants related to XML Specification.
53
## Constants related to XML Specification
54
## (W3C Recommendation or Working Draft)
59
NameChar_s = "(#{Letter_s}|#{Digit_s}|[\\.\\-_:])"
60
Name_s = "(#{Letter_s}|[_:])#{NameChar_s}*"
61
SkipLit_s = "(\"[^\"]*\"|'[^']*')"
63
SkipList = /^#{SkipLit_s}$/o
66
Instance_s = "(\\+|-)?[1-9]#{Digit_s}*"
67
Instance = /^#{Instance_s}$/o
73
== Module XML::DOM (XML::SimpleTree)
81
## Fundamental Interfaces
85
== Class XML::DOM::DOMException
93
class DOMException<Exception
96
HIERARCHY_REQUEST_ERR = 3
97
WRONG_DOCUMENT_ERR = 4
99
NO_DATA_ALLOWED_ERR = 6
100
NO_MODIFICATION_ALLOWED_ERR = 7
102
NOT_SUPPORTED_ERR = 9
103
INUSE_ATTRIBUTE_ERR = 10
112
"no modification allowed",
121
--- DOMException.new(code = 0)
123
generate DOM exception.
126
def initialize(code = 0)
133
--- DOMException#code()
135
return code of exception.
144
--- DOMException#to_s()
146
return the string representation of the error.
155
== Class XML::DOM::DOMImplementation
158
class DOMImplementation
162
--- DOMImplementation#hasFeature(feature, version)
164
test if DOM implementation has correct feature and version.
167
def hasFeature(feature, version)
168
if feature =~ /^XML$/i && (version.nil? || version == "1.0")
176
== Class XML::DOM::Node
185
CDATA_SECTION_NODE = 4
186
ENTITY_REFERENCE_NODE = 5
188
PROCESSING_INSTRUCTION_NODE = 7
191
DOCUMENT_TYPE_NODE = 10
192
DOCUMENT_FRAGMENT_NODE = 11
201
# ENTITY_REFERENCE = 5
204
# PROCESSING_INSTRUCTION = 7
208
# DOCUMENT_FRAGMENT = 11
214
--- Node.new(*children)
217
children is a Array of child, or sequence of child.
218
child is a String or Node.
221
## new([child1, child2, ...]) or
222
## new(child1, child2, ...)
223
## child?: String or Node
224
def initialize(*children)
227
self.childNodes = children if children.length > 0
245
--- Node#parentNode=(p)
247
set node p as parent.
290
def nodeValue; nil; end
293
--- Node#nodeValue=(p)
304
--- Node#childNodes()
307
if method has block, apply block for children nodes.
308
without block, return children nodelist.
313
@children.each do |child|
317
return @children if !@children.nil?
318
@children = NodeList.new
324
--- Node#childNodes=(p)
330
@children = NodeList.new
334
if p.nil? || (p.is_a?(Array) && p.length == 0)
339
if child.is_a?(String)
343
elsif child.is_a?(Node)
344
@children.push(child)
345
child.parentNode = self
347
raise "parameter error"
356
return attributes of node(but always return nil?).
363
## proper parameter type?
368
--- Node#[]=(index, nodes)
370
set children node as nodes with []-style.
372
def []=(index, nodes)
373
@children[index..index] = nodes
374
@children.each do |child|
375
child.parentNode = self
382
get children node with []-style.
400
returns the string representation of the Node.
407
--- Node#dump(depth = 0)
412
print ' ' * depth * 2
413
print nodeName + "\n"
414
@children.each do |child|
415
child.dump(depth + 1)
422
returns the human-readable string representation.
425
"#<#{self.class}: #{self.nodeName}>"
429
--- Node#firstChild()
432
return the first child node.
436
return nil if !@children || @children.length == 0
444
return the last child node.
448
return nil if !@children || @children.length == 0
453
--- Node#previousSibling()
456
return the previous sibling node.
460
return nil if !@parent
462
@parent.childNodes do |child|
463
return prev if child == self
470
--- Node#nextSibling()
473
return the next sibling node.
477
return nil if !@parent
479
@parent.childNodes.reverse.each do |child|
480
return nexts if child == self
486
def _getChildIndex(node)
488
@children.each do |child|
500
parent.removeChild(self)
505
raise DOMException.new(DOMException::HIERARCHY_REQUEST_ERR)
508
def _insertNodes(index, node)
509
if node.nodeType == DOCUMENT_FRAGMENT_NODE
511
node.childNodes.to_a.each_with_index do |n, i|
515
_insertNodes(index + i, n)
518
elsif node.is_a?(Node)
525
@children[index, 0] = node
527
node.parentNode = self
529
raise ArgumentError, "invalid value for Node"
533
def _removeNode(index, node)
534
@children[index, 1] = nil
535
node.parentNode = nil
539
# --- Node#insertAfter(newChild, refChild)
541
# insert newChild into the node after refChild.
543
# def insertAfter(newChild, refChild)
544
# if @children.nil? || @children.length == 0
545
# raise DOMException.new(DOMException::NOT_FOUND_ERR)
547
# index = _getChildIndex(refChild)
548
# raise DOMException.new(DOMException::NOT_FOUND_ERR) if index.nil?
549
# _insertNodes(index, newChild)
553
--- Node#insertBefore(newChild, refChild)
556
insert newChild into the node before refChild.
559
def insertBefore(newChild, refChild)
560
if @children.nil? || @children.length == 0
561
raise DOMException.new(DOMException::NOT_FOUND_ERR)
563
index = _getChildIndex(refChild)
564
raise DOMException.new(DOMException::NOT_FOUND_ERR) if !index
565
_insertNodes(index, newChild)
569
--- Node#replaceChild(newChild, oldChild)
572
replace the child node oldChild with newChild.
575
def replaceChild(newChild, oldChild)
576
if @children.nil? || @children.length == 0
577
raise DOMException.new(DOMException::NOT_FOUND_ERR)
579
index = _getChildIndex(oldChild)
580
raise DOMException.new(DOMException::NOT_FOUND_ERR) if !index
581
_removeNode(index, oldChild)
582
_insertNodes(index, newChild)
586
--- Node#removeChild(oldChild)
589
remove the children node oldChild.
592
def removeChild(oldChild)
593
if @children.nil? || @children.length == 0
594
raise DOMException.new(DOMException::NOT_FOUND_ERR)
596
index = _getChildIndex(oldChild)
597
raise DOMException.new(DOMException::NOT_FOUND_ERR) if !index
598
_removeNode(index, oldChild)
603
--- Node#appendChild(newChild)
606
adds the node newChild to the end of the list of children of this node.
609
def appendChild(newChild)
610
@children = NodeList.new if !@children
611
_insertNodes(-1, newChild)
615
--- Node#hasChildNodes()
618
returns true if node has children, or return false if node has no children.
622
!@children.nil? && @children.length > 0
625
## get the Node object by IDs
626
## [experimental implement]
627
def _searchID(value, ids = nil)
630
if nodeType == DOCUMENT_NODE
632
elsif !ownerDocument.nil?
637
ids = doc._getIDAttrs
639
if nodeType == ELEMENT_NODE && _getIDVals(ids).include?(value)
641
elsif !@children.nil?
642
@children.each do |node|
643
if !(match = node._searchID(value, ids)).nil?
651
def _getMyLocation(parent)
652
index = parent._getChildIndex(self)
654
"child(#{index + 1},#all)"
661
--- Node#makeXPointer(use_id = true)
663
return XPointer's expression of this node.
665
def makeXPointer(use_id = true)
666
if use_id && !attributes.nil? && !(idvals = _getIDVals).empty?
668
elsif @parent.nil? || @parent.nodeType == DOCUMENT_NODE
671
@parent.makeXPointer(use_id) + "." + self._getMyLocation(@parent)
675
## [Masaki Fukushima]
676
def _child(reverse = false)
677
return if @children.nil?
678
@children.reversible_each(reverse) do |child|
683
## [Masaki Fukushima]
684
def _descendant(reverse = false)
685
return if @children.nil?
686
@children.reversible_each(reverse) do |child|
688
child._descendant(reverse) do |node|
694
## [Masaki Fukushima]
695
def _ancestor(reverse = false)
696
return if @parent.nil?
697
yield @parent if !reverse
698
@parent._ancestor(reverse) do |node| yield node end
699
yield @parent if reverse
702
## [Masaki Fukushima]
703
def __sibling(reverse, only_appeared_before_self)
704
return if @parent.nil?
705
self_appeared = false
706
@parent.childNodes.reversible_each(reverse) do |node|
711
if only_appeared_before_self
712
break if self_appeared
714
else # only appeared after self
715
yield node if self_appeared
720
## [Masaki Fukushima]
721
def _psibling(reverse = false)
722
__sibling(!reverse, reverse) do |sib|
727
## [Masaki Fukushima]
728
def _fsibling(reverse = false)
729
__sibling(reverse, reverse) do |sib|
734
## [Masaki Fukushima]
735
def _preceding(reverse = false)
736
return if @parent.nil?
737
prev_sib = previousSibling
739
prev_sib._preceding(reverse) {|node| yield node} if reverse
741
prev_sib._descendant(!reverse) {|node| yield node}
742
prev_sib._preceding(reverse) {|node| yield node} if !reverse
744
@parent._preceding(reverse) {|node| yield node} if reverse
746
@parent._preceding(reverse) {|node| yield node} if !reverse
750
## [Masaki Fukushima]
751
def _following(reverse = false)
752
return if @parent.nil?
753
next_sib = nextSibling
755
next_sib._following(reverse) {|node| yield node} if reverse
757
next_sib._descendant(reverse) {|node| yield node}
758
next_sib._following(reverse) {|node| yield node} if !reverse
760
@parent._following(reverse) {|node| yield node} if reverse
762
@parent._following(reverse) {|node| yield node} if !reverse
766
## [Masaki Fukushima]
767
def _matchAttribute?(attr, value)
774
return false if attr.nil?
778
when /^"([^"]*)"$/, /^'([^']*)'$/
784
raise "invalid attribute value: #{value}"
787
return attr.nodeValue.downcase == value.downcase
789
return attr.nodeValue == value
793
## [Masaki Fukushima]
794
def _matchNodeAttributes?(node, attributes)
795
return true if attributes.nil?
796
raise TypeError if !attributes.is_a?(Hash)
797
return true if attributes.length == 0
798
return false if node.nodeType != ELEMENT_NODE
800
attributes.each do |name, value|
803
return catch(:match) {
804
node.attributes.each do |attr|
805
throw(:match, true) if _matchAttribute?(attr, value)
810
attr = node.attributes[name] unless node.attributes.nil?
811
return _matchAttribute?(attr, value)
813
raise "invalid attribute name: '#{name}'"
818
## [Masaki Fukushima]
819
def _matchNodeType?(node, ntype)
822
return (node.nodeType == ELEMENT_NODE)
824
return (node.nodeType == PROCESSING_INSTRUCTION_NODE)
826
return (node.nodeType == COMMENT_NODE)
828
return (node.nodeType == TEXT_NODE ||
829
node.nodeType == CDATA_SECTION_NODE)
831
return (node.nodeType == CDATA_SECTION_NODE)
834
when ELEMENT_NODE, PROCESSING_INSTRUCTION_NODE, COMMENT_NODE,
835
TEXT_NODE, CDATA_SECTION_NODE
841
raise "unknown node type: '#{ntype}'"
843
return (node.nodeType == ELEMENT_NODE && node.nodeName == ntype)
845
raise "invalid element type: '#{ntype}'"
849
## [Masaki Fukushima]
850
def _matchNode?(node, ntype, attributes)
851
_matchNodeType?(node, ntype) &&
852
_matchNodeAttributes?(node, attributes)
855
## [Masaki Fukushima]
856
def _nodesByRelativeLocationTerm(location)
857
if location !~ /^([a-z]+)\(([^\)]*)\)$/
858
raise "invalid relative location: '#{location}'"
864
ntype = '#element' if ntype.nil?
868
# check instance number
871
raise "missing instance number: '#{location}'"
880
raise "unknown instance number: '#{number}'"
884
if attributes.length % 2 != 0
885
raise " missing attribute value: '#{location}'"
887
attributes = Hash[*attributes]
889
# iterate over nodes specified with keyword
891
self.send("_#{keyword}", reverse) do |node|
892
next unless _matchNode?(node, ntype, attributes)
905
## [Masaki Fukushima]
906
def _nodesByLocationTerms(location, pre_keyword = nil)
907
if location !~ /^([a-z]*)\(([^)]*)\)(\.(.+))?$/
908
raise "invalid location: \"#{location}\""
914
keyword = pre_keyword if keyword == ''
916
raise "cannot determine preceding keyword: \"#{location}\""
920
when 'child', 'descendant', 'ancestor', 'psibling', 'fsibling',
921
'preceding', 'following'
922
# relative location term
923
_nodesByRelativeLocationTerm("#{keyword}(#{args})") do |node|
927
node._nodesByLocationTerms(rest, keyword) do |n|
933
# attribute location term
934
if args !~ Spec::Name
935
raise "invalid attribute name: '#{args}'"
937
attr = attributes[args]
938
value = (attr.nil? ? nil : Text.new(attr.nodeValue))
942
value._nodesByLocationTerms(rest) do |node|
946
when 'span', 'string'
947
raise "unsupported keyword: '#{keyword}'"
949
raise "unknown keyword: '#{keyword}'"
953
## [Masaki Fukushima]
954
def _getNodeByAbsoluteLocationTerm(location)
957
if nodeType == DOCUMENT_NODE
958
root = documentElement
959
elsif !ownerDocument.nil?
960
root = ownerDocument.documentElement
962
root = self if root.nil?
966
when /^id\(([^\)]*)\)$/
968
raise "invalid id value: #{value}" if value !~ Spec::Name
969
return _searchID(value)
970
when /^html\(([^\)]*)\)$/
972
return getNodesByXPointer("root().descendant(1,A,NAME,\"#{value}\")")[0]
974
raise "unknown keyword: #{location}"
979
--- Node#getNodeByXPointer(pointer)
981
return node indicated by the XPointer pointer.
983
## [Masaki Fukushima]
984
def getNodesByXPointer(pointer)
985
if pointer !~ /^([a-z]+)\(([^)]*)\)(\.(.+))?$/
986
raise "invalid XPointer: \"#{pointer}\""
993
when 'root', 'origin', 'id', 'html'
994
src = _getNodeByAbsoluteLocationTerm("#{keyword}(#{args})")
996
src = _getNodeByAbsoluteLocationTerm("root()")
1004
yield src if iterator?
1007
src._nodesByLocationTerms(rest) do |node|
1008
yield node if iterator?
1016
--- Node#ownerDocument()
1019
Document object associated with this node.
1022
## Floating objects are not owned by any documents.
1024
return @ownerDocument if @ownerDocument
1025
parent = self.parentNode
1026
return nil if parent.nil?
1027
if parent.nodeType == DOCUMENT_NODE
1030
return parent.ownerDocument
1034
def ownerDocument=(document); @ownerDocument = document; end
1038
--- Node#cloneNode()
1041
return the copy of the Node.
1044
def cloneNode(deep = true, *args)
1045
ret = self.class.new(*args)
1047
@children.each do |child|
1048
ret.appendChild(child.cloneNode(true))
1055
--- Node#trim(preserve = false)
1057
trim extra whitespaces.
1059
## trim extra whitespaces
1060
## if attribute 'xml:space' is 'preserve',
1061
## don't trim any white spaces
1062
def trim(preserve = false)
1063
return nil if @children.nil?
1064
children = @children.to_a.dup
1065
children.each do |child|
1066
if !preserve && (child.nodeType == TEXT_NODE ||
1067
child.nodeType == CDATA_SECTION_NODE)
1069
self.removeChild(child)
1072
child.trim(preserve)
1082
== Class XML::DOM::NamedNodeMap
1091
--- NamedNodeMap.new(nodes = nil)
1093
creates a new NamedNodeMap.
1095
def initialize(nodes = nil)
1097
nodes.each do |node|
1098
@nodes[node.nodeName] = node
1105
--- NamedNodeMap#getNamedItem(name)
1108
retrieves a node specified by name.
1111
def getNamedItem(name)
1116
--- NamedNodeMap#setNamedItem(node)
1119
adds a node using its nodeName attribute.
1122
def setNamedItem(node)
1123
@nodes[node.nodeName] = node
1127
--- NamedNodeMap#removeNamedItem(name)
1130
removes a node specified by name.
1133
def removeNamedItem(name)
1140
--- NamedNodeMap#item(index)
1143
returns the index item in the map.
1147
v = @nodes.to_a[index]
1153
--- NamedNodeMap#[](name)
1155
returns nodes associated to name.
1162
--- NamedNodeMap#[]=(name, node)
1164
sets node named name.
1167
raise "parameter error" if node.nodeName != name
1172
--- NamedNodeMap#each()
1174
iterates over each pair of name and node(name, node) of the namedNodeMap.
1177
@nodes.each do |key, value|
1183
--- NamedNodeMap#size()
1186
returns the number of nodes in the map.
1194
## get nodeValues by names
1195
## names ::= name ('|' name)*
1196
def _getValues(names)
1198
names.split('|').each do |name|
1199
if !@nodes[name].nil?
1200
ret.push(@nodes[name].nodeValue)
1208
== Class XML::DOM::NodeList
1217
--- NodeList.new(nodes = nil)
1219
creates a new NodeList.
1221
def initialize(nodes = nil)
1224
elsif nodes.is_a?(Array)
1227
raise "parameter error"
1234
--- NodeList#item(index)
1237
return the indexth item in the NodeList.
1247
return size of NodeList.
1255
--- NodeList#[](index)
1257
return indexth node of the NodeList.
1264
--- NodeList#[]=(*p)
1266
set node of indexth node of the NodeList.
1272
@nodes[p[0], p[1]] = p[2]
1279
iterates over each node of the NodeList.
1282
@nodes.each do |value|
1288
--- NodeList#reversible_each(reverse = false)
1290
iterates over each node of the reversed NodeList.
1292
## [Masaki Fukushima]
1293
def reversible_each(reverse = false)
1295
@nodes.each do |value|
1299
@nodes.reverse_each do |value|
1306
--- NodeList#push(*nodes)
1308
adds nodes into the NodeList.
1311
nodes.each do |node|
1312
if node.is_a?(Array)
1314
elsif node.is_a?(NodeList)
1315
@nodes.concat(node.to_a)
1316
elsif node.is_a?(Node)
1319
raise "parameter error"
1326
--- NodeList#concat(*nodes)
1328
alias of NodeList#push.
1335
pops and returns the last node of the NodeList.
1344
removes and returns the first node of the NodeList.
1353
returns the string representation of the NodeList.
1360
--- NodeList#reverse
1362
returns the reversed NodeList.
1371
converts the NodeList into an array.
1378
--- NodeList#+(nodes)
1380
return the newly created concatenated NodeList.
1384
NodeList.new(@nodes)
1385
elsif nodes.is_a?(Array)
1386
NodeList.new(@nodes + nodes)
1387
elsif nodes.is_a?(NodeList)
1388
NodeList.new(@nodes + nodes.to_a)
1389
elsif nodes.is_a?(Node)
1390
NodeList.new(@nodes + [nodes])
1392
raise "parameter error"
1397
--- NodeList#<<(nodes)
1399
appends nodes to the NodeList.
1401
## modified by Masaki Fukushima
1405
elsif nodes.is_a?(Array)
1406
@nodes.concat(nodes)
1407
elsif nodes.is_a?(NodeList)
1408
@nodes.concat(nodes.to_a)
1409
elsif nodes.is_a?(Node)
1412
raise "parameter error"
1417
## get nodeValues by names
1418
## names ::= name ('|' name)*
1419
def _getValues(names)
1421
names.split('|').each do |name|
1422
if !@nodes[name].nil?
1423
ret.push(@nodes[name].nodeValue)
1431
== Class XML::DOM::DocumentFragment
1437
class DocumentFragment<Node
1442
--- DocumentFragment.new(*children)
1444
creates a new DocumentFragment.
1447
def initialize(*children)
1454
--- DocumentFragment#nodeType
1457
returns the nodeType.
1461
DOCUMENT_FRAGMENT_NODE
1465
--- DocumentFragment#nodeName
1468
returns the nodeName.
1472
"#document-fragment"
1476
--- DocumentFragment#parentNode=(p)
1478
returns the parent of this node.
1480
## DocumentFragment should not have the parent node.
1482
@children.each do |child|
1483
child.parentNode = p
1487
def _checkNode(node)
1488
unless node.nodeType == ELEMENT_NODE ||
1489
node.nodeType == PROCESSING_INSTRUCTION_NODE ||
1490
node.nodeType == COMMENT_NODE ||
1491
node.nodeType == TEXT_NODE ||
1492
node.nodeType == CDATA_SECTION_NODE ||
1493
node.nodeType == ENTITY_REFERENCE_NODE
1494
raise DOMException.new(DOMException::HIERARCHY_REQUEST_ERR)
1501
== Class XML::DOM::Document
1510
--- Document.new(*children)
1512
creates a new Document.
1515
## new([child1, child2, ...]) or
1516
## new(child1, child2, ...)
1517
## child?: String or Node
1518
def initialize(*children)
1525
--- Document#nodeType
1528
returns the nodeType.
1536
--- Document#nodeName
1539
returns the nodeName.
1547
--- Document#documentElement
1550
returns root element of the Docuemnt.
1554
@children.each do |child|
1555
if child.nodeType == ELEMENT_NODE
1563
--- Document#doctype
1566
returns DTD associated with this document.
1570
@children.each do |child|
1571
if child.nodeType == DOCUMENT_TYPE_NODE
1579
--- Document#getElementsByTagName(tagname)
1582
returns a NodeList of all the Elements with a given tag name.
1584
## [DOM] (but this is not "live")
1585
def getElementsByTagName(tagname)
1587
@children.each do |node|
1588
if node.nodeType == ELEMENT_NODE
1589
if tagname == '*' || node.nodeName == tagname
1592
ret << node.getElementsByTagName(tagname)
1599
--- Document#createElement(tagName)
1605
def createElement(tagName)
1606
ret = Element.new(tagName)
1607
ret.ownerDocument = self
1612
--- Document#createTextNode(data)
1618
def createTextNode(data)
1619
ret = Text.new(data)
1620
ret.ownerDocument = self
1625
--- Document#createCDATASection(data)
1628
creates a CDATASection.
1631
def createCDATASection(data)
1632
ret = CDATASection.new(data)
1633
ret.ownerDocument = self
1638
--- Document#createComment(data)
1644
def createComment(data)
1645
ret = Comment.new(data)
1646
ret.ownerDocument = self
1651
--- Document#createProcessingInstruction(target, data)
1654
create a ProcessingInstruction.
1657
def createProcessingInstruction(target, data)
1658
ret = ProcessingInstruction.new(target, data)
1659
ret.ownerDocument = self
1664
--- Document#createAttribute(name)
1670
def createAttribute(name)
1671
ret = Attr.new(name)
1672
ret.ownerDocument = self
1677
--- Document#createEntityReference(name)
1680
create a EntityReference.
1683
def createEntityReference(name)
1684
ret = EntityReference.new(name)
1685
ret.ownerDocument = self
1690
--- Document#createDocumentFragment()
1693
create a DocumentFragment.
1696
def createDocumentFragment
1697
ret = DocumentFragment.new
1698
ret.ownerDocument = self
1702
## set the ID list by the attribute name with the element name
1704
## [experimental implement]
1705
def _setIDAttr(attrname, elemname = '*')
1706
@idattrs = {} if @idattrs.nil?
1707
@idattrs[elemname] = attrname
1711
## [experimental implement]
1713
return {'*'=>'id'} if @idattrs.nil?
1719
return @implemantation if @implemantation
1721
@implemantation = DOMImplementation.instance
1724
def implementation=(impl)
1725
@implemantation = impl
1728
def _checkNode(node)
1729
unless node.nodeType == ELEMENT_NODE ||
1730
node.nodeType == PROCESSING_INSTRUCTION_NODE ||
1731
node.nodeType == COMMENT_NODE ||
1732
node.nodeType == DOCUMENT_TYPE_NODE
1733
raise DOMException.new(DOMException::HIERARCHY_REQUEST_ERR)
1736
if node.nodeType == ELEMENT_NODE
1737
@children.each do |n|
1738
if n.nodeType == ELEMENT_NODE
1739
raise DOMException.new(DOMException::HIERARCHY_REQUEST_ERR)
1744
if node.nodeType == DOCUMENT_TYPE_NODE
1745
@children.each do |n|
1746
if n.nodeType == DOCUMENT_TYPE_NODE
1747
raise DOMException.new(DOMException::HIERARCHY_REQUEST_ERR)
1756
== Class XML::DOM::Attr
1763
## new(name, [text1, text2, ...]) or
1764
## new(name, text1, text2, ...)
1766
## text?: String or Node
1771
--- Attr.new(name = nil, *text)
1775
def initialize(name = nil, *text)
1777
raise "parameter error" if !name
1788
returns the nodeType.
1799
returns the nodeName.
1807
--- Attr#nodeValue()
1810
returns the nodeValue.
1815
@children.each do |child|
1816
ret << child.nodeValue
1822
--- Attr#nodeValue=(text)
1825
returns the value of this node.
1828
def nodeValue=(text)
1829
self.childNodes = [text]
1835
return the string representation of the Attr.
1839
nodeValue.each_byte do |code|
1842
value << sprintf("&#x%X;", code)
1853
"#{@name}=\"#{value}\""
1857
--- Attr#dump(depth = 0)
1862
print ' ' * depth * 2
1863
print "// #{self.to_s}\n"
1867
--- Attr#cloneNode(deep = true)
1870
returns the copy of the Attr.
1873
def cloneNode(deep = true)
1891
--- Attr#value=(value)
1894
alias of nodeValue=.
1897
alias value nodeValue
1898
alias value= nodeValue=
1901
def specified; @specified; end
1902
def specified=(is_specified); @specified = is_specified; end
1904
def _checkNode(node)
1905
unless node.nodeType == TEXT_NODE ||
1906
node.nodeType == ENTITY_REFERENCE_NODE
1907
raise DOMException.new(DOMException::HIERARCHY_REQUEST_ERR)
1914
== Class XML::DOM::Attribute
1921
== Class XML::DOM::Element
1932
--- Element.new(tag = nil, attrs = nil, *children)
1934
create a new Element.
1936
## new(tag, attrs, [child1, child2, ...]) or
1937
## new(tag, attrs, child1, child2, ...)
1939
## attrs: Hash, Attr or Array of Attr (or nil)
1940
## child?: String or Node
1941
def initialize(tag = nil, attr = nil, *children)
1943
raise "parameter error" if !tag
1946
@attr = NamedNodeMap.new([])
1947
elsif attr.is_a?(Hash)
1949
attr.each do |key, value|
1950
nodes.push(Attr.new(key, value))
1952
@attr = NamedNodeMap.new(nodes)
1953
elsif attr.is_a?(Array)
1954
@attr = NamedNodeMap.new(attr)
1955
elsif attr.is_a?(Attr)
1956
@attr = NamedNodeMap.new([attr])
1958
raise "parameter error: #{attr}"
1965
--- Element#nodeType()
1968
returns the nodeType.
1976
--- Element#nodeName()
1979
returns the nodeName.
1987
--- Element#attributes()
1990
returns the attributes of this Element.
1995
@attr.each do |key, value|
2006
return the string representation of the Element.
2011
attr << ' ' + a.to_s
2015
ret = "<#{@name}#{attr}>#{content}</#{@name}>"
2017
ret = "<#{@name}#{attr}/>"
2019
ret << "\n" if parentNode.nodeType == DOCUMENT_NODE
2024
--- Element#dump(depth = 0)
2030
@attr.each do |a| ## self.attributes do |a|
2031
attr += a.to_s + ", "
2035
print ' ' * depth * 2
2036
print "#{@name}(#{attr})\n"
2037
@children.each do |child|
2038
child.dump(depth + 1)
2043
--- Element#tagName()
2049
alias tagName nodeName
2052
--- Element#getAttribute(name)
2055
retrieves an attribute value by name.
2058
def getAttribute(name)
2059
attr = getAttributeNode(name)
2068
--- Element#setAttribute(name, value)
2071
adds a new attribute.
2074
def setAttribute(name, value)
2076
attr = @ownerDocument.createAttribute(name)
2077
attr.appendChild(@ownerDocument.createTextNode(value))
2079
attr = Attribute.new(name)
2080
attr.appendChild(Text.new(value))
2082
setAttributeNode(attr)
2086
--- Element#removeAttribute(name)
2089
remove an attribute by name.
2092
def removeAttribute(name)
2093
ret = getAttributeNode(name)
2094
removeAttributeNode(ret) if ret
2098
--- Element#getAttributeNode(name)
2101
retrieves an Attr node by name.
2104
def getAttributeNode(name)
2105
@attr.getNamedItem(name)
2109
--- Element#setAttributeNode(newAttr)
2112
adds a new attribute.
2115
def setAttributeNode(newAttr)
2116
ret = getAttributeNode(newAttr.nodeName)
2118
raise DOMException.new(DOMException::INUSE_ATTRIBUTE_ERR)
2120
@attr.setNamedItem(newAttr)
2125
--- Element#removeAttributeNode(oldAttr)
2128
removes the specified attribute.
2131
def removeAttributeNode(oldAttr)
2132
ret = getAttributeNode(oldAttr.nodeName)
2133
if ret.nil? || ret != oldAttr
2134
raise DOMException.new(DOMException::NOT_FOUND_ERR)
2136
@attr.removeNamedItem(oldAttr.nodeName)
2141
--- Element#getElementsByTagName(tagname)
2144
returns a NodeList of all descendant elements with given tag name.
2146
## [DOM] (but this is not "live")
2147
def getElementsByTagName(tagname)
2149
@children.each do |node|
2150
if node.nodeType == ELEMENT_NODE
2151
if tagname == '*' || node.nodeName == tagname
2154
ret << node.getElementsByTagName(tagname)
2160
def _getMyLocation(parent)
2162
parent.childNodes do |child|
2164
return "child(#{index},#{@name})"
2166
if child.nodeType == ELEMENT_NODE && child.nodeName == @name
2175
--- Element#normalize
2178
puts all Text nodes in the full depth of the sub-tree under this
2183
return if @children.nil?
2185
children = @children.to_a.dup
2186
children.each do |child|
2187
if !old.nil? && old.nodeType == TEXT_NODE &&
2188
child.nodeType == TEXT_NODE
2189
old.appendData(child.nodeValue)
2190
self.removeChild(child)
2192
if child.nodeType == ELEMENT_NODE
2201
--- Element#cloneNode(deep = true)
2204
returns the copy of the Element.
2207
def cloneNode(deep = true)
2209
@attr.each do |attr|
2210
attrs.push(attr.cloneNode(true))
2212
super(deep, @name, attrs)
2215
## get the list of nodeValues by IDs
2216
## [experimental implement]
2217
def _getIDVals(ids = nil)
2220
return [] if doc.nil?
2221
ids = doc._getIDAttrs
2225
if !ids[nodeName].nil?
2226
return attributes._getValues(ids[nodeName])
2227
elsif !ids['*'].nil?
2228
return attributes._getValues(ids['*'])
2234
--- Element#trim(preserve = false)
2236
trim extra whitespaces.
2238
## trim extra whitespaces
2239
## if attribute 'xml:space' is 'preserve',
2240
## don't trim any white spaces
2241
def trim(preserve = false)
2242
if !attributes['xml:space'].nil?
2243
value = attributes['xml:space'].nodeValue
2244
if value == 'preserve'
2246
elsif value == 'default'
2250
return nil if @children.nil?
2251
children = @children.to_a.dup
2252
children.each do |child|
2253
if !preserve && (child.nodeType == TEXT_NODE ||
2254
child.nodeType == CDATA_SECTION_NODE)
2256
self.removeChild(child)
2259
child.trim(preserve)
2265
def _checkNode(node)
2266
unless node.nodeType == ELEMENT_NODE ||
2267
node.nodeType == TEXT_NODE ||
2268
node.nodeType == COMMENT_NODE ||
2269
node.nodeType == PROCESSING_INSTRUCTION_NODE ||
2270
node.nodeType == CDATA_SECTION_NODE ||
2271
node.nodeType == ENTITY_REFERENCE_NODE
2272
raise DOMException.new(DOMException::HIERARCHY_REQUEST_ERR)
2279
== Class XML::DOM::CharacterData
2285
class CharacterData<Node
2290
--- CharacterData.new(text)
2292
creates a new CharacterData.
2296
def initialize(text = nil)
2298
raise "parameter error" if !text
2305
--- CharacterData#data()
2308
returns the character data of the node.
2316
--- CharacterData#data=(p)
2319
set the character data of the node.
2327
--- CharacterData#length()
2330
returns length of this CharacterData.
2338
--- CharacterData#substringData(start, count)
2341
extracts a range of data from the node.
2344
def substringData(start, count)
2345
if start < 0 || start > @value.length || count < 0
2346
raise DOMException.new(DOMException::INDEX_SIZE_ERR)
2348
## if the sum of start and count > length,
2349
## return all characters to the end of the value.
2350
@value[start, count]
2354
--- CharacterData#appendData(str)
2357
append the string to the end of the character data.
2365
--- CharacterData#insertData(offset, str)
2368
insert a string at the specified character offset.
2371
def insertData(offset, str)
2372
if offset < 0 || offset > @value.length
2373
raise DOMException.new(DOMException::INDEX_SIZE_ERR)
2375
@value[offset, 0] = str
2379
--- CharacterData#deleteData(offset, count)
2382
removes a range of characters from the node.
2385
def deleteData(offset, count)
2386
if offset < 0 || offset > @value.length || count < 0
2387
raise DOMException.new(DOMException::INDEX_SIZE_ERR)
2389
@value[offset, count] = ''
2393
--- CharacterData#replaceData(offset, count, str)
2396
replaces the characters starting at the specified character offset
2397
with specified string.
2400
def replaceData(offset, count, str)
2401
if offset < 0 || offset > @value.length || count < 0
2402
raise DOMException.new(DOMException::INDEX_SIZE_ERR)
2404
@value[offset, count] = str
2408
--- CharacterData#cloneData(deep = true)
2411
returns the copy of the CharacterData.
2414
def cloneNode(deep = true)
2415
super(deep, @value.dup)
2431
--- CharacterData#nodeValue=(p)
2444
== Class XML::DOM::Text
2450
class Text<CharacterData
2461
def initialize(text = nil)
2471
returns the nodeType.
2482
returns the nodeName.
2492
return the string representation of the Text.
2496
@value.each_byte do |code|
2499
ret << sprintf("&#x%X;", code)
2514
--- Text#dump(depth = 0)
2519
print ' ' * depth * 2
2520
print "#{@value.inspect}\n"
2523
def _getMyLocation(parent)
2525
parent.childNodes do |child|
2527
return "child(#{index},#text)"
2529
if child.nodeType == TEXT_NODE
2537
--- Text#splitText(offset)
2540
breaks this Text node into two Text nodes at the specified offset.
2543
def splitText(offset)
2544
if offset > @value.length || offset < 0
2545
raise DOMException.new(DOMException::INDEX_SIZE_ERR)
2547
newText = @value[offset, @value.length]
2548
newNode = Text.new(newText)
2549
if !self.parentNode.nil?
2550
self.parentNode.insertAfter(newNode, self)
2552
@value[offset, @value.length] = ""
2557
--- Text#trim(preserve = false)
2559
trim extra whitespaces.
2561
def trim(preserve = false)
2563
@value.sub!(/\A\s*([\s\S]*?)\s*\Z/, "\\1")
2572
== Class XML::DOM::Comment
2578
class Comment<CharacterData
2583
--- Comment.new(text)
2585
creates a new Comment.
2589
def initialize(text = nil)
2591
raise "parameter error" if !text
2597
--- Comment#nodeType
2600
returns the nodeType.
2608
--- Comment#nodeName
2611
returns the nodeName.
2621
returns the string representation of the Comment.
2624
ret = "<!--#{@value}-->"
2625
ret << "\n" if parentNode.nodeType == DOCUMENT_NODE
2630
--- Comment#dump(depth =0)
2635
print ' ' * depth * 2
2636
print "<!--#{@value.inspect}-->\n"
2639
def _getMyLocation(parent)
2641
parent.childNodes do |child|
2643
return "child(#{index},#comment)"
2645
if child.nodeType == COMMENT_NODE
2654
## Extended Interfaces
2657
== Class XML::DOM::CDATASection
2663
class CDATASection<Text
2667
--- CDATASection.new(text = nil)
2669
creates a new CDATASection.
2671
def initialize(text = nil)
2673
raise "parameter error" if !text
2679
--- CDATASection#nodeType
2682
returns the nodeType.
2690
--- CDATASection#nodeName
2693
returns the nodeName.
2701
--- CDATASection#to_s
2703
returns the string representation of the CDATASection.
2706
"<![CDATA[#{@value}]]>"
2710
--- CDATASection#dump(depth = 0)
2712
dumps the CDATASection.
2715
print ' ' * depth * 2
2716
print "<![CDATA[#{@value.inspect}]]>\n"
2719
def _getMyLocation(parent)
2721
parent.childNodes do |child|
2723
return "child(#{index},#cdata)"
2725
if child.nodeType == CDATA_SECTION_NODE
2734
== Class XML::DOM::DocumentType
2739
class DocumentType<Node
2744
--- DocumentType.new(name, value = nil, *children)
2746
creates a new DocuemntType.
2748
def initialize(name, value = nil, *children)
2750
raise "parameter error" if !name
2752
@value = value.freeze
2758
--- DocumentType#nodeType
2761
returns the nodeType.
2769
--- DocumentType#nodeName
2772
returns the nodeName.
2780
--- DocumentType#to_s
2782
returns the string representation of the DocumentType.
2785
ret = "<!DOCTYPE " + @name
2787
ret <<= " " + @value
2789
if !@children.nil? && @children.length > 0
2791
@children.each do |child|
2792
if child.nodeType == PROCESSING_INSTRUCTION_NODE ||
2793
child.nodeType == COMMENT_NODE
2794
ret <<= child.to_s + "\n"
2796
ret <<= child.nodeValue + "\n"
2805
--- DocumentType#dump(depth = 0)
2807
dumps the DocumentType.
2810
print ' ' * depth * 2
2811
print "<!DOCTYPE #{@name} #{@value} [\n"
2812
@children.each do |child|
2813
print ' ' * (depth + 1) * 2
2814
if child.nodeType == PROCESSING_INSTRUCTION_NODE ||
2815
child.nodeType == COMMENT_NODE
2818
print child.nodeValue, "\n"
2821
print ' ' * depth * 2
2826
--- DocumentType#cloneNode(deep = true)
2829
returns the copy of the DocumentType.
2832
def cloneNode(deep = true)
2833
super(deep, @name, @value)
2837
## def entities; @entities; end
2838
## def notations; @notations; end
2842
== Class XML::DOM::Notation
2851
--- Notation.new(name, pubid, sysid)
2853
creates a new Notation.
2855
def initialize(name, pubid, sysid)
2858
@pubid = pubid.freeze
2859
@sysid = sysid.freeze
2865
--- Notation#nodeType
2868
returns the nodeType.
2876
--- Notation#nodeName
2879
returns the nodeName.
2887
--- Notation#publicId
2889
returns the publicId of the Notation.
2896
--- Notation#systemId
2898
returns the systemId of the Notation.
2905
--- Notation#cloneNode(deep = true)
2908
returns the copy of the Notation.
2911
def cloneNode(deep = true)
2912
super(deep, @name, @pubid, @sysid)
2917
== Class XML::DOM::Entity
2927
--- Entity.new(name, pubid, sysid, notation)
2929
creates a new Entity.
2931
def initialize(name, pubid, sysid, notation)
2934
@pubid = pubid.freeze
2935
@sysid = sysid.freeze
2936
@notation = notation.freeze
2945
returns the nodeType.
2956
returns the nodeName.
2966
returns the publicId of the Entity.
2975
returns the systemId of the Entity.
2982
--- Entity#notationName
2984
returns the notationname of the Entity.
2991
--- Entity#cloneNode(deep = true)
2994
returns the copy of the Entity.
2997
def cloneNode(deep = true)
2998
super(deep, @name, @pubid, @sysid, @notation)
3001
def _checkNode(node)
3002
unless node.nodeType == ELEMENT_NODE ||
3003
node.nodeType == PROCESSING_INSTRUCTION_NODE ||
3004
node.nodeType == COMMENT_NODE ||
3005
node.nodeType == TEXT_NODE ||
3006
node.nodeType == CDATA_SECTION_NODE ||
3007
node.nodeType == ENTITY_REFERENCE_NODE
3008
raise DOMException.new(DOMException::HIERARCHY_REQUEST_ERR)
3015
== Class XML::DOM::EntityReference
3020
class EntityReference<Node
3025
--- EntityReference.new(name, *children)
3027
creates a new EntityReference.
3029
def initialize(name, *children)
3031
raise "parameter error" if !name
3038
--- EntityReference#nodeType
3041
returns the nodeType.
3045
ENTITY_REFERENCE_NODE
3049
--- EntityReference#nodeName
3052
returns the nodeName.
3060
--- EntityReference#to_s
3062
returns the string representation of the EntityReference.
3064
## reference form or expanded form?
3070
--- EntityReference#dump(depth = 0)
3072
dumps the EntityReference.
3075
print ' ' * depth * 2
3076
print "&#{@name}{\n"
3077
@children.each do |child|
3078
child.dump(depth + 1)
3080
print ' ' * depth * 2
3085
--- EntityReference#cloneNode(deep = true)
3088
returns the copy of the EntityReference.
3091
def cloneNode(deep = true)
3095
def _checkNode(node)
3096
unless node.nodeType == ELEMENT_NODE ||
3097
node.nodeType == PROCESSING_INSTRUCTION_NODE ||
3098
node.nodeType == COMMENT_NODE ||
3099
node.nodeType == TEXT_NODE ||
3100
node.nodeType == CDATA_SECTION_NODE ||
3101
node.nodeType == ENTITY_REFERENCE_NODE
3102
raise DOMException.new(DOMException::HIERARCHY_REQUEST_ERR)
3109
== Class XML::DOM::ProcessingInstruction
3115
class ProcessingInstruction<Node
3120
--- ProcessingInstruction.new(target = nil, data = nil)
3122
creates a new ProcessingInstruction.
3124
## new(target, data)
3127
def initialize(target = nil, data = nil)
3129
raise "parameter error" if !data
3130
@target = target.freeze
3133
@value << " #{data}" if data != ""
3140
--- ProcessingInstruction#nodeType
3143
returns the nodeType.
3147
PROCESSING_INSTRUCTION_NODE
3151
--- ProcessingInstruction#nodeName
3154
returns the nodeName.
3158
"#proccessing-instruction"
3162
--- ProcessingInstruction#target
3165
returns the target of the ProcessingInstruction.
3173
--- ProcessingInstruction#target=(p)
3176
set p to the target of the ProcessingInstruction.
3181
@value = @target.dup
3182
@value << " #{@data}" if @data != ""
3187
--- ProcessingInstruction#data
3190
return the content of the ProcessingInstruction.
3198
--- ProcessingInstruction#data=(p)
3201
sets p to the content of the ProcessingInstruction.
3206
@value = @target.dup
3207
@value << " #{@data}" if @data != ""
3212
--- ProcessingInstruction#nodeValue
3223
## inhibit changing value without target= or data=
3227
--- ProcessingInstruction#to_s
3229
returns the string representation of the ProcessingInstruction.
3232
ret = "<?#{@value}?>"
3233
ret << "\n" if parentNode.nodeType == DOCUMENT_NODE
3238
--- ProcessingInstruction#dump(depth = 0)
3240
dumps the ProcessingInstruction.
3243
print ' ' * depth * 2
3244
print "<?#{@value.inspect}?>\n"
3247
def _getMyLocation(parent)
3249
parent.childNodes do |child|
3251
return "child(#{index},#pi)"
3253
if child.nodeType == PROCESSING_INSTRUCTION_NODE
3261
--- ProcessingInstruction#cloneNode(deep = true)
3264
returns the copy of the ProcessingInstruction.
3267
def cloneNode(deep = true)
3268
super(deep, @target.dup, @data.dup)