vcdom 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,33 @@
1
+ = VCDOM [RubyGem]
2
+
3
+ This project makes a implementation of W3C DOM working on Ruby.
4
+ Now, it's under construction...
5
+
6
+ == Example
7
+
8
+ following example shows you how to parse XML string and how to serialize DOM objects:
9
+
10
+ require "vcdom/xml_ls"
11
+
12
+ @ls_impl = VCDOM::XMLLS
13
+ # get parser
14
+ parser = @ls_impl.create_ls_parser( :mode_asynchronous, nil )
15
+
16
+ # set input xml string
17
+ input = @ls_impl.create_ls_input()
18
+ input.string_data = "<empty-element/>"
19
+ # do parse
20
+ doc = parser.parse( input )
21
+ doc.document_element.tag_name
22
+ #=> "empty-element"
23
+
24
+ # parse other xml string
25
+ input.string_data = "<test>aaaa</test>"
26
+ doc = parser.parse( input )
27
+ doc.document_element.first_child.node_value
28
+ #=> "aaaa"
29
+
30
+ # serialize
31
+ serializer = @ls_impl.create_ls_serializer()
32
+ serializer.write_to_string( doc )
33
+ #=> "<test>aaa</test>"
data/lib/vcdom/attr.rb CHANGED
@@ -8,7 +8,7 @@ module VCDOM
8
8
 
9
9
  include Parent
10
10
 
11
- def initialize( doc, name )
11
+ def initialize( doc, name ) # :nodoc:
12
12
  initialize_parent()
13
13
  super( doc )
14
14
  @owner_element = nil
@@ -22,7 +22,7 @@ module VCDOM
22
22
  def owner_element
23
23
  @owner_element
24
24
  end
25
- def _set_owner_element( elem )
25
+ def _set_owner_element( elem ) # :nodoc:
26
26
  @owner_element = elem
27
27
  end
28
28
 
@@ -51,7 +51,7 @@ module VCDOM
51
51
  end
52
52
 
53
53
  def append_child( new_child )
54
- # ノードのタイプチェックなど
54
+ # check the arg type
55
55
  if not new_child.is_a? Node then
56
56
  raise ArgumentError.new( "the argument [#{new_child.inspect}] is not an object of the expected class." )
57
57
  end
@@ -13,7 +13,14 @@ module VCDOM
13
13
  def data
14
14
  @data.to_s
15
15
  end
16
- alias :node_value :data
16
+ def data=( val )
17
+ @data = val.intern
18
+ end
19
+ alias :node_value :data
20
+ alias :node_value= :data=
21
+ alias :text_content :data
22
+ alias :text_content= :data=
23
+
17
24
  def length
18
25
  @data.to_s.length
19
26
  end
@@ -13,8 +13,7 @@ module VCDOM
13
13
 
14
14
  include Parent
15
15
 
16
-
17
- def initialize()
16
+ def initialize() # :nodoc:
18
17
  initialize_parent()
19
18
  super( nil )
20
19
  end
@@ -23,30 +22,50 @@ module VCDOM
23
22
  DOCUMENT_NODE
24
23
  end
25
24
 
25
+ def document_element
26
+ @document_element
27
+ end
28
+
29
+ # always +nil+
30
+ def text_content
31
+ nil
32
+ end
33
+
34
+ # no effect
35
+ def text_content=( val )
36
+ end
37
+
26
38
  def append_child( new_child )
27
- # ノードのタイプチェックなど
39
+ # Check the arg type
28
40
  if not new_child.is_a? Node then
29
41
  raise ArgumentError.new( "the argument [#{new_child.inspect}] is not an object of the expected class." )
30
42
  end
31
43
  # Element (maximum of one), ProcessingInstruction, Comment, DocumentType (maximum of one)
32
44
  case new_child.node_type
33
45
  when ELEMENT_NODE then
34
- # 既に追加されてるかどうかのチェック
46
+ # is there document_element already?
35
47
  if @document_element.nil? then
36
48
  @document_element = new_child
37
49
  else
38
50
  raise "HIERARCHY_REQUEST_ERR"
39
51
  end
40
52
  when DOCUMENT_TYPE_NODE then
41
- # 既に追加されてるかどうかのチェック
53
+ # is there document_type already?
42
54
  when PROCESSING_INSTRUCTION_NODE, COMMENT_NODE then
43
55
  # OK
44
56
  else
45
57
  # ERROR
46
- raise "ERROR"
58
+ raise "ERROR : new_child.node_type = #{new_child.node_type}"
47
59
  end
48
60
  _append_child( new_child )
49
61
  end
62
+ def remove_child( old_child )
63
+ old_child = super( old_child )
64
+ if old_child.equal? @document_element then
65
+ @document_element = nil
66
+ end
67
+ return old_child
68
+ end
50
69
 
51
70
  def create_element( tag_name )
52
71
  elem = nil
@@ -113,13 +132,10 @@ module VCDOM
113
132
  end
114
133
 
115
134
  def create_text_node( data )
116
- node = nil
117
- if data.is_a? String then
118
- node = Text._new( self, data.intern )
119
- else
135
+ if not data.is_a? String then
120
136
  raise ArgumentError.new( "the argument [#{data.inspect}] is not an object of the expected class. the class : #{data.class}" )
121
137
  end
122
- node
138
+ node = Text._new( self, data.intern )
123
139
  end
124
140
 
125
141
  end
data/lib/vcdom/element.rb CHANGED
@@ -11,7 +11,7 @@ module VCDOM
11
11
  include Parent
12
12
  include Child
13
13
 
14
- def initialize( doc, tag_name )
14
+ def initialize( doc, tag_name ) # :nodoc:
15
15
  initialize_parent()
16
16
  super( doc )
17
17
  @local_name = tag_name
@@ -84,6 +84,18 @@ module VCDOM
84
84
  self.set_attribute_node( attr )
85
85
  nil
86
86
  end
87
+ # Retrieves an attribute value by name.
88
+ def get_attribute( name )
89
+ @attr_nodes.each do |attr|
90
+ if attr.name == name then
91
+ #//////////////////////////
92
+ # 変更する
93
+ #//////////////////////////
94
+ return attr.value
95
+ end
96
+ end
97
+ return ""
98
+ end
87
99
 
88
100
  # setAttributeNodeNS introduced in DOM Level 2
89
101
  # Adds a new attribute. If an attribute with that local name and that namespace URI is already present in the element, it is replaced by the new one. Replacing an attribute node by itself has no effect.
@@ -328,5 +340,79 @@ module VCDOM
328
340
  end # end Element.normalizeNamespaces
329
341
  end
330
342
 
343
+ # The first child node of that element which is of nodeType +ELEMENT_NODE+, as an Element object.
344
+ # If the element on which this attribute is accessed does not have any child nodes,
345
+ # or if none of those child nodes are element nodes, then this attribute return +nil+.
346
+ # (defiend at W3C Element Traversal Specification)
347
+ def first_element_child
348
+ n = self.first_child
349
+ while n do
350
+ if n.node_type == ELEMENT_NODE then
351
+ break
352
+ end
353
+ n = n.next_sibling
354
+ end
355
+ return n
356
+ end
357
+
358
+ # The last child node of that element which is of nodeType +ELEMENT_NODE+, as an Element object.
359
+ # If the element on which this attribute is accessed does not have any child nodes,
360
+ # or if none of those child nodes are element nodes, then this attribute return +nil+.
361
+ # (defiend at W3C Element Traversal Specification)
362
+ def last_element_child
363
+ n = self.last_child
364
+ while n do
365
+ if n.node_type == ELEMENT_NODE then
366
+ break
367
+ end
368
+ n = n.previous_sibling
369
+ end
370
+ return n
371
+ end
372
+
373
+ # The sibling node of that element which most immediately precedes that element in document order,
374
+ # and which is of nodeType +ELEMENT_NODE+, as an Element object.
375
+ # If the element on which this attribute is accessed does not have any preceding sibling nodes,
376
+ # or if none of those preceding sibling nodes are element nodes, then this attribute must return +nil+.
377
+ # (defiend at W3C Element Traversal Specification)
378
+ def previous_element_sibling
379
+ n = self.previous_sibling
380
+ while n do
381
+ if n.node_type == ELEMENT_NODE then
382
+ break
383
+ end
384
+ n = n.previous_sibling
385
+ end
386
+ return n
387
+ end
388
+
389
+ # The sibling node of that element which most immediately follows that element in document order,
390
+ # and which is of nodeType +ELEMENT_NODE+, as an Element object.
391
+ # If the element on which this attribute is accessed does not have any following sibling nodes,
392
+ # or if none of those following sibling nodes are element nodes, then this attribute must return +nil+.
393
+ # (defiend at W3C Element Traversal Specification)
394
+ def next_element_sibling
395
+ n = self.next_sibling
396
+ while n do
397
+ if n.node_type == ELEMENT_NODE then
398
+ break
399
+ end
400
+ n = n.next_sibling
401
+ end
402
+ return n
403
+ end
404
+
405
+ # The current number of child nodes of that element which are of nodeType +ELEMENT_NODE+.
406
+ # This value is not stored, but calculated when you access this attribute.
407
+ def child_element_count
408
+ num = 0
409
+ self.each_child_node do |n|
410
+ if n.node_type == ELEMENT_NODE then
411
+ num += 1
412
+ end
413
+ end
414
+ return num
415
+ end
416
+
331
417
  end
332
418
  end
data/lib/vcdom/node.rb CHANGED
@@ -21,7 +21,7 @@ module VCDOM
21
21
  private :new
22
22
  end
23
23
 
24
- def initialize( owner_document )
24
+ def initialize( owner_document ) # :nodoc:
25
25
  @owner_document = owner_document
26
26
  end
27
27
 
@@ -58,7 +58,29 @@ module VCDOM
58
58
  nil
59
59
  end
60
60
 
61
+ # Concatenation of the textContent attribute value of every child node,
62
+ # excluding +COMMENT_NODE+ and +PROCESSING_INSTRUCTION_NODE+ nodes.
63
+ # This is the empty string if the node has no children.
64
+ def text_content
65
+ str = String.new()
66
+ self.each_child_node do |n|
67
+ if n.node_type != COMMENT_NODE and n.node_type != PROCESSING_INSTRUCTION_NODE then
68
+ str << n.text_content
69
+ end
70
+ end
71
+ return str
72
+ end
61
73
 
74
+ # Any possible children this node have are removed and, if the new value is not empty +String+ object or +nil+,
75
+ # replaced by a single Text node containing the new value string.
76
+ def text_content=( val )
77
+ n = self.first_child
78
+ while n do
79
+ self.remove_child( n )
80
+ n = self.first_child
81
+ end
82
+ self.append_child( self.owner_document.create_text_node( val ) )
83
+ end
62
84
 
63
85
  def lookup_prefix( namespace_uri )
64
86
  if namespace_uri.nil? then #(namespaceURI has no value, i.e. namespaceURI is null or empty string) {
@@ -100,6 +122,12 @@ module VCDOM
100
122
  return nil
101
123
  end
102
124
  end
125
+
126
+ # Always +false+.
127
+ def has_child_nodes()
128
+ return false
129
+ end
130
+
103
131
  def lookup_namespace_prefix( namespace_uri, original_element )
104
132
  if not self.namespace_uri.nil? and self.namespace_uri == namespace_uri and
105
133
  not self.prefix.nil? and original_element.lookup_namespace_uri( self.prefix ) == namespace_uri then
data/lib/vcdom/parent.rb CHANGED
@@ -70,6 +70,15 @@ module VCDOM
70
70
  @child_nodes.last_child
71
71
  end
72
72
 
73
+ # Returns whether this node has any children.
74
+ def has_child_nodes()
75
+ if @child_nodes.first_child.nil? then
76
+ return false
77
+ else
78
+ return true
79
+ end
80
+ end
81
+
73
82
  def _append_child( new_child )
74
83
  #@child_nodes << new_child
75
84
  if not new_child.parent_node.nil? then
@@ -2,110 +2,129 @@
2
2
 
3
3
  require "vcdom/document"
4
4
 
5
- module VCDOM
6
- class XMLInput
7
- def string_data
8
- @string_data
9
- end
10
- def string_data=( str_data )
11
- @string_data = str_data
12
- end
5
+ module VCDOM::XMLLS
6
+
7
+ class XMLInput
8
+ def string_data
9
+ @string_data
13
10
  end
14
- class XMLContentHandler
15
- def initialize( document_class )
16
- @document_class = document_class
17
- @ns_stack = Array.new()
18
- end
19
- def ns_stack
20
- @ns_stack
21
- end
22
- def start_document
23
- @doc = @document_class._new()
24
- @cur = @doc
25
- end
26
- def end_document
27
- return @doc
28
- end
29
- def on_stag( name, ns_uri )
30
- @cur = @cur.append_child( @doc.create_element_ns( ns_uri, name ) )
11
+ def string_data=( str_data )
12
+ @string_data = str_data
13
+ end
14
+ end
15
+
16
+ class XMLContentHandler # :nodoc:
17
+ def initialize( document_class )
18
+ @document_class = document_class
19
+ @ns_stack = Array.new()
20
+ end
21
+ def ns_stack
22
+ @ns_stack
23
+ end
24
+ def start_document
25
+ @doc = @document_class._new()
26
+ @cur = @doc
27
+ end
28
+ def end_document
29
+ return @doc
30
+ end
31
+ def on_stag( name, ns_uri )
32
+ @cur = @cur.append_child( @doc.create_element_ns( ns_uri, name ) )
33
+ end
34
+ def on_etag( name )
35
+ if @cur.node_name != name then
36
+ raise "[ERROR] @cur.node_name : #{@cur.node_name}, name : #{name}"
31
37
  end
32
- def on_etag( name )
33
- if @cur.node_name != name then
34
- raise "[ERROR] @cur.node_name : #{@cur.node_name}, name : #{name}"
38
+ @cur = @cur.parent_node
39
+ end
40
+ def on_eetag( name, ns_uri )
41
+ @cur = @cur.append_child( @doc.create_element_ns( ns_uri, name ) )
42
+ end
43
+ def on_end_eetag()
44
+ @cur = @cur.parent_node
45
+ end
46
+ def on_begin_attr( name, ns_uri )
47
+ attr = @doc.create_attribute_ns( ns_uri, name )
48
+ @cur.set_attribute_node_ns( attr )
49
+ @cur = attr
50
+ end
51
+ def on_end_attr()
52
+ @cur = @cur.owner_element
53
+ end
54
+ def on_chardata( char_data )
55
+ if @cur.node_type == VCDOM::Node::DOCUMENT_NODE then
56
+ if char_data !~ /\A[\x20\x09\x0D\x0A]*\Z/ then
57
+ raise "ERROR : " + char_data.inspect
35
58
  end
36
- @cur = @cur.parent_node
37
- end
38
- def on_eetag( name, ns_uri )
39
- @cur = @cur.append_child( @doc.create_element_ns( ns_uri, name ) )
40
- end
41
- def on_end_eetag()
42
- @cur = @cur.parent_node
43
- end
44
- def on_begin_attr( name, ns_uri )
45
- attr = @doc.create_attribute_ns( ns_uri, name )
46
- @cur.set_attribute_node_ns( attr )
47
- @cur = attr
48
- end
49
- def on_end_attr()
50
- @cur = @cur.owner_element
51
- end
52
- def on_chardata( char_data )
59
+ else
53
60
  @cur.append_child( @doc.create_text_node( char_data ) )
54
61
  end
55
62
  end
56
- class XMLParser
57
-
58
- def parse( input )
59
- source = input.string_data
60
- if source.class == String then
61
- _parse_xml_str( source, XMLContentHandler.new( VCDOM::Document ) )
62
- else
63
- raise ArgumentTypeError.new()
63
+ end
64
+
65
+ class XMLParser
66
+
67
+ def parse( input )
68
+ source = input.string_data
69
+ if source.class == String then
70
+ # Ruby 1.8 の場合, String を 1 文字ずつに分ける
71
+ if RUBY_VERSION < "1.9"
72
+ source = source.split //u
73
+ class << source
74
+ def []( *args )
75
+ res = super( *args )
76
+ args.length == 2 ? res.join : res
77
+ end
78
+ end
64
79
  end
80
+ _parse_xml_str( source, XMLContentHandler.new( VCDOM::Document ) )
81
+ else
82
+ raise ArgumentTypeError.new()
65
83
  end
66
-
67
- def _parse_xml_str( xml_str, content_handler )
68
- content_handler.start_document()
69
- i = 0
70
- loop do
71
- if xml_str[i].nil? then
72
- break
73
- elsif xml_str[i] == "<" then
84
+ end
85
+
86
+ def _parse_xml_str( xml_str, content_handler )
87
+ content_handler.start_document()
88
+ i = 0
89
+ loop do
90
+ if xml_str[i].nil? then
91
+ break
92
+ elsif xml_str[i] == "<" then
93
+ i += 1
94
+ if xml_str[i] == "/" then
95
+ # ETag ::= '</' Name S? '>'
96
+ i = _parse_end_tag( xml_str, i+1, content_handler )
97
+ elsif xml_str[i] == "?" then
98
+ # PI
99
+ elsif xml_str[i] == "!" then
74
100
  i += 1
75
- if xml_str[i] == "/" then
76
- # ETag ::= '</' Name S? '>'
77
- i = _parse_end_tag( xml_str, i+1, content_handler )
78
- elsif xml_str[i] == "?" then
79
- # PI
80
- elsif xml_str[i] == "!" then
81
- i += 1
82
- if xml_str[i,2] == "--" then
83
- # Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
84
- i = _parse_comment( xml_str, i+2, content_handler )
85
- elsif xml_str[i,7] == "[CDATA[" then
86
- # CDATA
87
- i = _parse_cdata( xml_str, i+7, content_handler )
88
- else
89
- $stdout << "ERROR" << "\n"
90
- end
101
+ if xml_str[i,2] == "--" then
102
+ # Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
103
+ i = _parse_comment( xml_str, i+2, content_handler )
104
+ elsif xml_str[i,7] == "[CDATA[" then
105
+ # CDATA
106
+ i = _parse_cdata( xml_str, i+7, content_handler )
91
107
  else
92
- i = _parse_stag_or_eetag( xml_str, i, content_handler )
108
+ $stdout << "ERROR" << "\n"
93
109
  end
94
- elsif xml_str[i] == "&" and not _is_char_ref_or_predef_entity_ref?( xml_str, i ) then
95
- raise "NOT SUPPORT"
96
- # Reference ::= EntityRef | CharRef
97
- # EntityRef ::= '&' Name ';'
98
- # CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
99
110
  else
100
- # CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
101
- i = _parse_chardata_or_entity_reference( xml_str, i, content_handler )
111
+ i = _parse_stag_or_eetag( xml_str, i, content_handler )
102
112
  end
113
+ elsif xml_str[i] == "&" and not _is_char_ref_or_predef_entity_ref?( xml_str, i ) then
114
+ raise "NOT SUPPORT"
115
+ # Reference ::= EntityRef | CharRef
116
+ # EntityRef ::= '&' Name ';'
117
+ # CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
118
+ else
119
+ # CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
120
+ i = _parse_chardata_or_entity_reference( xml_str, i, content_handler )
103
121
  end
104
- return content_handler.end_document()
105
122
  end
106
- private :_parse_xml_str
107
-
108
- def _parse_stag_or_eetag( str, i, content_handler )
123
+ return content_handler.end_document()
124
+ end
125
+ private :_parse_xml_str
126
+
127
+ def _parse_stag_or_eetag( str, i, content_handler )
109
128
  #@listener.on_begin_stag_or_eetag()
110
129
  # 要素名の取得
111
130
  elem_name = String.new()
@@ -253,6 +272,7 @@ module VCDOM
253
272
  end
254
273
  return i
255
274
  end
275
+ private :_parse_stag_or_eetag
256
276
 
257
277
  def _parse_end_tag( str, i, content_handler )
258
278
  # 要素名の取得
@@ -280,6 +300,7 @@ module VCDOM
280
300
  content_handler.ns_stack.pop()
281
301
  return i
282
302
  end
303
+ private :_parse_end_tag
283
304
 
284
305
  def _is_char_ref_or_predef_entity_ref?( str, i )
285
306
  if str[i] == "&" then
@@ -293,6 +314,7 @@ module VCDOM
293
314
  end
294
315
  end
295
316
  end
317
+ private :_is_char_ref_or_predef_entity_ref?
296
318
 
297
319
  def _parse_chardata_or_entity_reference( str, i, content_handler )
298
320
  # CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
@@ -304,16 +326,21 @@ module VCDOM
304
326
  # break
305
327
  elsif str[i] == "&" then
306
328
  if str[i+1] != "#" then
307
- if str[i+2,3] == "lt;" then
329
+ if str[i+1,3] == "lt;" then
308
330
  chardata << "<"
309
- elsif str[i+2,3] == "gt;" then
331
+ i += 4
332
+ elsif str[i+1,3] == "gt;" then
310
333
  chardata << ">"
311
- elsif str[i+2,5] == "quot;" then
334
+ i += 4
335
+ elsif str[i+1,5] == "quot;" then
312
336
  chardata << "\""
313
- elsif str[i+2,5] == "apos;" then
337
+ i += 6
338
+ elsif str[i+1,5] == "apos;" then
314
339
  chardata << "'"
315
- elsif str[i+2,4] == "amp;" then
340
+ i += 6
341
+ elsif str[i+1,4] == "amp;" then
316
342
  chardata << "&"
343
+ i += 5
317
344
  else
318
345
  # entity reference
319
346
  break
@@ -352,6 +379,7 @@ module VCDOM
352
379
  content_handler.on_chardata( chardata )
353
380
  return i
354
381
  end
382
+ private :_parse_chardata_or_entity_reference
355
383
 
356
384
  def _skip_white_spaces( str, i )
357
385
  loop do
@@ -363,6 +391,7 @@ module VCDOM
363
391
  end
364
392
  return i
365
393
  end
394
+ private :_skip_white_spaces
366
395
 
367
396
  end
368
397
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "vcdom/node"
4
4
 
5
- module VCDOM
5
+ module VCDOM::XMLLS
6
6
  class XMLSerializer
7
7
 
8
8
  def write_to_string( node )
@@ -0,0 +1,42 @@
1
+ # coding : utf-8
2
+
3
+ require "vcdom/xml_ls/xml_parser"
4
+ require "vcdom/xml_ls/xml_serializer"
5
+
6
+ module VCDOM
7
+ module XMLLS
8
+
9
+ extend self
10
+
11
+ MODE_SYNCHRONOUS = :mode_synchronous
12
+ MODE_ASYNCHRONOUS = :mode_asynchronous
13
+
14
+ def create_ls_parser( mode, schema_type )
15
+ case mode
16
+ when MODE_ASYNCHRONOUS
17
+ # OK : do nothing
18
+ when MODE_SYNCHRONOUS
19
+ raise "SORRY... arg +mode+ is not supported. must be +MODE_ASYNCHRONOUS+"
20
+ else
21
+ raise ArgumentError.new( "invalid mode [" + mode.inspect + "]" )
22
+ end
23
+ if not schema_type.nil? then
24
+ raise "SORRY... arg +schema_type+ is not still supported. must be +nil+"
25
+ end
26
+ XMLParser.new()
27
+ end
28
+
29
+ def create_ls_serializer()
30
+ XMLSerializer.new()
31
+ end
32
+
33
+ def create_ls_input()
34
+ XMLInput.new()
35
+ end
36
+
37
+ def create_ls_output()
38
+ XMLOutput.new()
39
+ end
40
+
41
+ end
42
+ end