Class | REXML::QuickPath |
In: |
lib/rexml/quickpath.rb
|
Parent: | Object |
EMPTY_HASH | = | {} |
# File lib/rexml/quickpath.rb, line 206 206: def QuickPath::attribute( name ) 207: return Functions.node.attributes[name] if Functions.node.kind_of? Element 208: end
# File lib/rexml/quickpath.rb, line 106 106: def QuickPath::axe( elements, axe_name, rest ) 107: matches = [] 108: matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u 109: case axe_name 110: when /^descendant/u 111: elements.each do |element| 112: matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element 113: end 114: when /^ancestor/u 115: elements.each do |element| 116: while element.parent 117: matches << element.parent 118: element = element.parent 119: end 120: end 121: matches = filter( matches, rest ) 122: when "self" 123: matches = filter( elements, rest ) 124: when "child" 125: elements.each do |element| 126: matches |= filter( element.to_a, rest ) if element.kind_of? Element 127: end 128: when "attribute" 129: elements.each do |element| 130: matches << element.attributes[ rest ] if element.kind_of? Element 131: end 132: when "parent" 133: matches = filter(elements.collect{|element| element.parent}.uniq, rest) 134: when "following-sibling" 135: matches = filter(elements.collect{|element| element.next_sibling}.uniq, 136: rest) 137: when "previous-sibling" 138: matches = filter(elements.collect{|element| 139: element.previous_sibling}.uniq, rest ) 140: end 141: return matches.uniq 142: end
# File lib/rexml/quickpath.rb, line 15 15: def QuickPath::each element, path, namespaces=EMPTY_HASH, &block 16: path = "*" unless path 17: match(element, path, namespaces).each( &block ) 18: end
Given an array of nodes it filters the array based on the path. The result is that when this method returns, the array will contain elements which match the path
# File lib/rexml/quickpath.rb, line 48 48: def QuickPath::filter elements, path 49: return elements if path.nil? or path == '' or elements.size == 0 50: case path 51: when /^\/\//u # Descendant 52: return axe( elements, "descendant-or-self", $' ) 53: when /^\/?\b(\w[-\w]*)\b::/u # Axe 54: axe_name = $1 55: rest = $' 56: return axe( elements, $1, $' ) 57: when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child 58: rest = $' 59: results = [] 60: elements.each do |element| 61: results |= filter( element.to_a, rest ) 62: end 63: return results 64: when /^\/?(\w[-\w]*)\(/u # / Function 65: return function( elements, $1, $' ) 66: when Namespace::NAMESPLIT # Element name 67: name = $2 68: ns = $1 69: rest = $' 70: elements.delete_if do |element| 71: !(element.kind_of? Element and 72: (element.expanded_name == name or 73: (element.name == name and 74: element.namespace == Functions.namespace_context[ns]))) 75: end 76: return filter( elements, rest ) 77: when /^\/\[/u 78: matches = [] 79: elements.each do |element| 80: matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element 81: end 82: return matches 83: when /^\[/u # Predicate 84: return predicate( elements, path ) 85: when /^\/?\.\.\./u # Ancestor 86: return axe( elements, "ancestor", $' ) 87: when /^\/?\.\./u # Parent 88: return filter( elements.collect{|e|e.parent}, $' ) 89: when /^\/?\./u # Self 90: return filter( elements, $' ) 91: when /^\*/u # Any 92: results = [] 93: elements.each do |element| 94: results |= filter( [element], $' ) if element.kind_of? Element 95: #if element.kind_of? Element 96: # children = element.to_a 97: # children.delete_if { |child| !child.kind_of?(Element) } 98: # results |= filter( children, $' ) 99: #end 100: end 101: return results 102: end 103: return [] 104: end
# File lib/rexml/quickpath.rb, line 11 11: def QuickPath::first element, path, namespaces=EMPTY_HASH 12: match(element, path, namespaces)[0] 13: end
# File lib/rexml/quickpath.rb, line 222 222: def QuickPath::function( elements, fname, rest ) 223: args = parse_args( elements, rest ) 224: Functions.pair = [0, elements.size] 225: results = [] 226: elements.each do |element| 227: Functions.pair[0] += 1 228: Functions.node = element 229: res = Functions.send( fname, *args ) 230: case res 231: when true 232: results << element 233: when Fixnum 234: results << element if Functions.pair[0] == res 235: end 236: end 237: return results 238: end
# File lib/rexml/quickpath.rb, line 20 20: def QuickPath::match element, path, namespaces=EMPTY_HASH 21: raise "nil is not a valid xpath" unless path 22: results = nil 23: Functions::namespace_context = namespaces 24: case path 25: when /^\/([^\/]|$)/u 26: # match on root 27: path = path[1..-1] 28: return [element.root.parent] if path == '' 29: results = filter([element.root], path) 30: when /^[-\w]*::/u 31: results = filter([element], path) 32: when /^\*/u 33: results = filter(element.to_a, path) 34: when /^[\[!\w:]/u 35: # match on child 36: matches = [] 37: children = element.to_a 38: results = filter(children, path) 39: else 40: results = filter([element], path) 41: end 42: return results 43: end
# File lib/rexml/quickpath.rb, line 214 214: def QuickPath::method_missing( id, *args ) 215: begin 216: Functions.send( id.id2name, *args ) 217: rescue Exception 218: raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}" 219: end 220: end
# File lib/rexml/quickpath.rb, line 210 210: def QuickPath::name() 211: return Functions.node.name if Functions.node.kind_of? Element 212: end
# File lib/rexml/quickpath.rb, line 240 240: def QuickPath::parse_args( element, string ) 241: # /.*?(?:\)|,)/ 242: arguments = [] 243: buffer = "" 244: while string and string != "" 245: c = string[0] 246: string.sub!(/^./u, "") 247: case c 248: when ?, 249: # if depth = 1, then we start a new argument 250: arguments << evaluate( buffer ) 251: #arguments << evaluate( string[0..count] ) 252: when ?( 253: # start a new method call 254: function( element, buffer, string ) 255: buffer = "" 256: when ?) 257: # close the method call and return arguments 258: return arguments 259: else 260: buffer << c 261: end 262: end 263: "" 264: end
A predicate filters a node-set with respect to an axis to produce a new node-set. For each node in the node-set to be filtered, the PredicateExpr is evaluated with that node as the context node, with the number of nodes in the node-set as the context size, and with the proximity position of the node in the node-set with respect to the axis as the context position; if PredicateExpr evaluates to true for that node, the node is included in the new node-set; otherwise, it is not included.
A PredicateExpr is evaluated by evaluating the Expr and converting the result to a boolean. If the result is a number, the result will be converted to true if the number is equal to the context position and will be converted to false otherwise; if the result is not a number, then the result will be converted as if by a call to the boolean function. Thus a location path para[3] is equivalent to para[position()=3].
# File lib/rexml/quickpath.rb, line 160 160: def QuickPath::predicate( elements, path ) 161: ind = 1 162: bcount = 1 163: while bcount > 0 164: bcount += 1 if path[ind] == ?[ 165: bcount -= 1 if path[ind] == ?] 166: ind += 1 167: end 168: ind -= 1 169: predicate = path[1..ind-1] 170: rest = path[ind+1..-1] 171: 172: # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c' 173: predicate.gsub!( /([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)/u ) { 174: "#$1 #$2 #$3 and #$3 #$4 #$5" 175: } 176: # Let's do some Ruby trickery to avoid some work: 177: predicate.gsub!( /&/u, "&&" ) 178: predicate.gsub!( /=/u, "==" ) 179: predicate.gsub!( /@(\w[-\w.]*)/u ) { 180: "attribute(\"#$1\")" 181: } 182: predicate.gsub!( /\bmod\b/u, "%" ) 183: predicate.gsub!( /\b(\w[-\w.]*\()/u ) { 184: fname = $1 185: fname.gsub( /-/u, "_" ) 186: } 187: 188: Functions.pair = [ 0, elements.size ] 189: results = [] 190: elements.each do |element| 191: Functions.pair[0] += 1 192: Functions.node = element 193: res = eval( predicate ) 194: case res 195: when true 196: results << element 197: when Fixnum 198: results << element if Functions.pair[0] == res 199: when String 200: results << element 201: end 202: end 203: return filter( results, rest ) 204: end