* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / yaml / basenode.rb
blob5439903f4221c1ab21a84e3f2428c4b1142a1745
2 # YAML::BaseNode class
4 require 'yaml/ypath'
6 module YAML
8     #
9     # YAML Generic Model container
10     #
11     module BaseNode
13         #
14         # Search for YPath entry and return
15         # qualified nodes.
16         #
17         def select( ypath_str )
18             matches = match_path( ypath_str )
20             #
21             # Create a new generic view of the elements selected
22             #
23             if matches
24                 result = []
25                 matches.each { |m|
26                     result.push m.last
27                 }
28                 YAML.transfer( 'seq', result )
29             end
30         end
32         #
33         # Search for YPath entry and return
34         # transformed nodes.
35         #
36         def select!( ypath_str )
37             matches = match_path( ypath_str )
39             #
40             # Create a new generic view of the elements selected
41             #
42             if matches
43                 result = []
44                 matches.each { |m|
45                     result.push m.last.transform
46                 }
47                 result
48             end
49         end
51         #
52         # Search for YPath entry and return a list of
53         # qualified paths.
54         #
55         def search( ypath_str )
56             matches = match_path( ypath_str )
58             if matches
59                 matches.collect { |m|
60                     path = []
61                     m.each_index { |i|
62                         path.push m[i] if ( i % 2 ).zero?
63                     }
64                     "/" + path.compact.join( "/" )
65                 }
66             end
67         end
69         def at( seg )
70             if Hash === @value
71                 self[seg]
72             elsif Array === @value and seg =~ /\A\d+\Z/ and @value[seg.to_i]
73                 @value[seg.to_i]
74             end
75         end
77         #
78         # YPath search returning a complete depth array
79         #
80         def match_path( ypath_str )
81             depth = 0
82             matches = []
83             YPath.each_path( ypath_str ) do |ypath|
84                 seg = match_segment( ypath, 0 )
85                 matches += seg if seg
86             end
87             matches.uniq
88         end
90         #
91         # Search a node for a single YPath segment
92         #
93         def match_segment( ypath, depth )
94             deep_nodes = []
95             seg = ypath.segments[ depth ]
96             if seg == "/"
97                 unless String === @value
98                     idx = -1
99                     @value.collect { |v|
100                         idx += 1
101                         if Hash === @value
102                             match_init = [v[0].transform, v[1]]
103                             match_deep = v[1].match_segment( ypath, depth )
104                         else
105                             match_init = [idx, v]
106                             match_deep = v.match_segment( ypath, depth )
107                         end
108                         if match_deep
109                             match_deep.each { |m|
110                                 deep_nodes.push( match_init + m )
111                             }
112                         end
113                     }
114                 end
115                 depth += 1
116                 seg = ypath.segments[ depth ]
117             end
118             match_nodes =
119                 case seg
120                 when "."
121                     [[nil, self]]
122                 when ".."
123                     [["..", nil]]
124                 when "*"
125                     if @value.is_a? Enumerable
126                         idx = -1
127                         @value.collect { |h|
128                             idx += 1
129                             if Hash === @value
130                                 [h[0].transform, h[1]]
131                             else
132                                 [idx, h]
133                             end
134                         }
135                     end
136                 else
137                     if seg =~ /^"(.*)"$/
138                         seg = $1
139                     elsif seg =~ /^'(.*)'$/
140                         seg = $1
141                     end
142                     if ( v = at( seg ) )
143                         [[ seg, v ]]
144                     end
145                 end
146             return deep_nodes unless match_nodes
147             pred = ypath.predicates[ depth ]
148             if pred
149                 case pred
150                 when /^\.=/
151                     pred = $'   # '
152                     match_nodes.reject! { |n|
153                         n.last.value != pred
154                     }
155                 else
156                     match_nodes.reject! { |n|
157                         n.last.at( pred ).nil?
158                     }
159                 end
160             end
161             return match_nodes + deep_nodes unless ypath.segments.length > depth + 1
163             #puts "DEPTH: #{depth + 1}"
164             deep_nodes = []
165             match_nodes.each { |n|
166                 if n[1].is_a? BaseNode
167                     match_deep = n[1].match_segment( ypath, depth + 1 )
168                     if match_deep
169                         match_deep.each { |m|
170                             deep_nodes.push( n + m )
171                         }
172                     end
173                 else
174                     deep_nodes = []
175                 end
176             }
177             deep_nodes = nil if deep_nodes.length == 0
178             deep_nodes
179         end
181         #
182         # We want the node to act like as Hash
183         # if it is.
184         #
185         def []( *key )
186             if Hash === @value
187                 v = @value.detect { |k,| k.transform == key.first }
188                 v[1] if v
189             elsif Array === @value
190                 @value.[]( *key )
191             end
192         end
194         def children
195             if Hash === @value
196                 @value.values.collect { |c| c[1] }
197             elsif Array === @value
198                 @value
199             end
200         end
202         def children_with_index
203             if Hash === @value
204                 @value.keys.collect { |i| [self[i], i] }
205             elsif Array === @value
206                 i = -1; @value.collect { |v| i += 1; [v, i] }
207             end
208         end
210         def emit
211             transform.to_yaml
212         end
213     end