├── HISTORY ├── VERSION ├── doc ├── index.html └── ECMA-357.pdf ├── work ├── deprecated │ └── meta │ │ ├── name │ │ ├── title │ │ ├── version │ │ ├── authors │ │ ├── collection │ │ ├── contact │ │ ├── repository │ │ └── description ├── re4x.rb ├── r4x.rb ├── e4x-old.rb └── r4x2.rb ├── .gitignore ├── lib └── r4x │ ├── emcascript.rb │ ├── qname.rb │ ├── xmllist_delegate.rb │ ├── xml_delegate.rb │ └── e4x.rb ├── task ├── notes.syckle ├── ridoc.syckle ├── stats.syckle ├── testrb.syckle ├── rdoc.syckle ├── box.syckle └── email.syckle ├── MANIFEST ├── README ├── PROFILE ├── .ruby ├── .gemspec └── LICENSE /HISTORY: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.5.0 2 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /work/deprecated/meta/name: -------------------------------------------------------------------------------- 1 | r4x 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/title: -------------------------------------------------------------------------------- 1 | R4X 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/version: -------------------------------------------------------------------------------- 1 | 0.5.0 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/authors: -------------------------------------------------------------------------------- 1 | Thomas Sawyer 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/collection: -------------------------------------------------------------------------------- 1 | rubyworks 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | log 2 | doc/rdoc 3 | doc/ri 4 | pkg 5 | tmp 6 | -------------------------------------------------------------------------------- /lib/r4x/emcascript.rb: -------------------------------------------------------------------------------- 1 | 2 | module EMCAScript 3 | 4 | end -------------------------------------------------------------------------------- /task/notes.syckle: -------------------------------------------------------------------------------- 1 | --- 2 | notes: 3 | service: Notes 4 | 5 | -------------------------------------------------------------------------------- /task/ridoc.syckle: -------------------------------------------------------------------------------- 1 | --- 2 | ridoc: 3 | service: RIDoc 4 | 5 | -------------------------------------------------------------------------------- /task/stats.syckle: -------------------------------------------------------------------------------- 1 | --- 2 | stats: 3 | service: Stats 4 | 5 | -------------------------------------------------------------------------------- /task/testrb.syckle: -------------------------------------------------------------------------------- 1 | --- 2 | testrb: 3 | service: Testrb 4 | 5 | -------------------------------------------------------------------------------- /work/deprecated/meta/contact: -------------------------------------------------------------------------------- 1 | rubyworks-mailinglist@rubyforge.org 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/repository: -------------------------------------------------------------------------------- 1 | git://github.com/rubyworks/r4x.git 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/description: -------------------------------------------------------------------------------- 1 | R4X is an implementation of E4X for Ruby. 2 | -------------------------------------------------------------------------------- /doc/ECMA-357.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubyworks/r4x/master/doc/ECMA-357.pdf -------------------------------------------------------------------------------- /task/rdoc.syckle: -------------------------------------------------------------------------------- 1 | --- 2 | rdoc: 3 | service : RDoc 4 | template : redfish 5 | active : true 6 | -------------------------------------------------------------------------------- /task/box.syckle: -------------------------------------------------------------------------------- 1 | --- 2 | box: 3 | service: Box 4 | types : [gz, gem] 5 | spec : true 6 | include: ~ 7 | exclude: ~ 8 | active : true 9 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast -x Syckfile -x Profile .ruby bin lib man spec test [A-Z]* 2 | .ruby 3 | lib/r4x/e4x.rb 4 | lib/r4x/emcascript.rb 5 | lib/r4x/qname.rb 6 | lib/r4x/xml_delegate.rb 7 | lib/r4x/xmllist_delegate.rb 8 | PROFILE 9 | LICENSE 10 | README 11 | HISTORY 12 | VERSION 13 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | = R4X 2 | 3 | R4X is a pure-Ruby implementation of E4X. 4 | 5 | == STATUS 6 | 7 | This implementation follows the E4X specification to the letter. 8 | At thei point it is only about half complete. 9 | 10 | One difficulty with the implementation is that Ruby can not 11 | support the exact syntax E4X designates, so an alternative 12 | needs to be used --which has not yet been fully settled upon. 13 | 14 | -------------------------------------------------------------------------------- /PROFILE: -------------------------------------------------------------------------------- 1 | --- 2 | title : R4X 3 | summary: E4X for Ruby 4 | contact: Trans 5 | license: Apache 2.0 6 | authors: Thomas Sawyer 7 | company: CubyWorks 8 | created: 2008-01-27 9 | 10 | description: 11 | R4X is an implementation of E4X for Ruby. 12 | 13 | resources: 14 | home: http://rubyworks.github.com/r4x 15 | code: http://github.com/rubyworks/r4x 16 | 17 | copyright: 18 | Copyright (c) 2008 Thomas Sawyer 19 | -------------------------------------------------------------------------------- /task/email.syckle: -------------------------------------------------------------------------------- 1 | --- 2 | email: 3 | service : Email 4 | file : ~ 5 | subject : ~ 6 | mailto : ruby-talk@ruby-lang.org 7 | from : ENV[EMAIL_ACCOUNT] 8 | server : ENV[EMAIL_SERVER] 9 | port : ENV[EMAIL_PORT] 10 | account : ENV[EMAIL_ACCOUNT] 11 | domain : ENV[EMAIL_DOMAIN] 12 | login : ENV[EMAIL_LOGIN] 13 | secure : ENV[EMAIL_SECURE] 14 | active : true 15 | 16 | #service : Email 17 | #mailto : ruby-talk@ruby-lang.org 18 | #from : admin@tigerops.org 19 | #server : mail.tigerops.org 20 | #port : 25 21 | #domain : tigerops.org 22 | #account : admin@tigerops.org 23 | #secure : false 24 | #login : login 25 | 26 | -------------------------------------------------------------------------------- /.ruby: -------------------------------------------------------------------------------- 1 | --- 2 | name: r4x 3 | company: CubyWorks 4 | title: R4X 5 | contact: Trans 6 | resources: 7 | code: http://github.com/rubyworks/r4x 8 | home: http://rubyworks.github.com/r4x 9 | pom_verison: 1.0.0 10 | manifest: 11 | - .ruby 12 | - lib/r4x/e4x.rb 13 | - lib/r4x/emcascript.rb 14 | - lib/r4x/qname.rb 15 | - lib/r4x/xml_delegate.rb 16 | - lib/r4x/xmllist_delegate.rb 17 | - PROFILE 18 | - LICENSE 19 | - README 20 | - HISTORY 21 | - VERSION 22 | version: 0.5.0 23 | copyright: Copyright (c) 2008 Thomas Sawyer 24 | licenses: 25 | - Apache 2.0 26 | description: R4X is an implementation of E4X for Ruby. 27 | summary: E4X for Ruby 28 | authors: 29 | - Thomas Sawyer 30 | created: 2008-01-27 31 | -------------------------------------------------------------------------------- /.gemspec: -------------------------------------------------------------------------------- 1 | --- !ruby/object:Gem::Specification 2 | name: r4x 3 | version: !ruby/object:Gem::Version 4 | version: 0.5.0 5 | platform: ruby 6 | authors: 7 | - rubyworks-mailinglist@rubyforge.org 8 | - Thomas Sawyer 9 | autorequire: 10 | bindir: bin 11 | cert_chain: [] 12 | 13 | date: 2009-09-27 00:00:00 -04:00 14 | default_executable: 15 | dependencies: [] 16 | 17 | description: R4X is an implementation of E4X for Ruby. 18 | email: rubyworks-mailinglist@rubyforge.org 19 | executables: [] 20 | 21 | extensions: [] 22 | 23 | extra_rdoc_files: 24 | - README 25 | - MANIFEST 26 | - LICENSE 27 | - HISTORY 28 | files: 29 | - lib 30 | - lib/r4x 31 | - lib/r4x/e4x.rb 32 | - lib/r4x/emcascript.rb 33 | - lib/r4x/qname.rb 34 | - lib/r4x/xml_delegate.rb 35 | - lib/r4x/xmllist_delegate.rb 36 | - meta 37 | - meta/abstract 38 | - meta/authors 39 | - meta/contact 40 | - meta/package 41 | - meta/project 42 | - meta/repository 43 | - meta/version 44 | - LICENSE 45 | - README 46 | - HISTORY 47 | has_rdoc: true 48 | homepage: 49 | licenses: [] 50 | 51 | post_install_message: 52 | rdoc_options: 53 | - --inline-source 54 | - --title 55 | - r4x api 56 | - --main 57 | - README 58 | require_paths: 59 | - lib 60 | required_ruby_version: !ruby/object:Gem::Requirement 61 | requirements: 62 | - - ">=" 63 | - !ruby/object:Gem::Version 64 | version: "0" 65 | version: 66 | required_rubygems_version: !ruby/object:Gem::Requirement 67 | requirements: 68 | - - ">=" 69 | - !ruby/object:Gem::Version 70 | version: "0" 71 | version: 72 | requirements: [] 73 | 74 | rubyforge_project: r4x 75 | rubygems_version: 1.3.5 76 | signing_key: 77 | specification_version: 3 78 | summary: R4X is an implementation of E4X for Ruby. 79 | test_files: [] 80 | 81 | -------------------------------------------------------------------------------- /lib/r4x/qname.rb: -------------------------------------------------------------------------------- 1 | 2 | # Defines QName and AttributeName classes. 3 | 4 | class QName 5 | 6 | class << self 7 | 8 | alias :__new :new 9 | 10 | def new( name_or_namespace, name=nil ) 11 | case name_or_namespace 12 | when QName 13 | return name_or_namespace 14 | when nil 15 | if QName === name or name.class == "QName" 16 | return name.dup 17 | else 18 | __new(name_or_namespace, name) 19 | end 20 | else 21 | __new(name_or_namespace, name ) 22 | end 23 | end 24 | 25 | end 26 | 27 | attr :name 28 | attr :local_name 29 | attr :uri 30 | attr :prefix 31 | 32 | def initialize( namespace, name ) 33 | @name = name.local_name.to_s # to_String 34 | case namespace 35 | when nil, '' 36 | namespace = (name == '*' ? nil : get_default_namespace ) 37 | end 38 | @local_name = name 39 | unless namespace 40 | @uri = nil 41 | @prefix = nil 42 | else 43 | namespace = Namespace.new( namespace ) 44 | @uri = namespace.uri 45 | @prefix = namespace.prefix 46 | end 47 | end 48 | 49 | # 50 | def to_s 51 | s = '' 52 | if uri != '' 53 | unless uri 54 | s = '*::' 55 | else 56 | s = "#{uri}::" 57 | end 58 | end 59 | "#{s}#{local_name}" 60 | end 61 | 62 | # 63 | def get_namespace( in_scope_namespaces=nil ) 64 | raise 'no uri [should not have occured]' unless uri 65 | in_scope_namespaces ||= [] 66 | ns = in_scope_namespaces.find { |n| uri == n.uri } # prefix == n.prefix ? 67 | ns = Namespace.new( uri ) unless ns # or ns = Namespace.new( prefix, uri ) ? 68 | ns 69 | end 70 | 71 | end 72 | 73 | 74 | class AttributeName 75 | 76 | attr :name 77 | 78 | def initialize( s ) 79 | @name = QName.new(s) 80 | end 81 | 82 | def to_s 83 | "@#{@name}" 84 | end 85 | 86 | end 87 | 88 | -------------------------------------------------------------------------------- /lib/r4x/xmllist_delegate.rb: -------------------------------------------------------------------------------- 1 | 2 | class XmlList 3 | 4 | class XmlListDelegate 5 | 6 | def initialize( x ) 7 | @x = x 8 | end 9 | 10 | def list ; @x.__list ; end 11 | def class ; @x.__class ; end 12 | 13 | def target_object ; @x.__target_object ; end 14 | def target_property ; @x.__target_property ; end 15 | def target_object=(to) ; @x.__target_object = to ; end 16 | def target_property=(tp) ; @x.__target_property = tp ; end 17 | 18 | def get( prop ) 19 | if prop.kind_of?(Integer) 20 | return @x.__list.at( prop ) 21 | end 22 | l = XmlList.new( [], @x, prop ) 23 | 0...length { |i| 24 | gq = @x[i].self.get( prop ) 25 | l.self.append( gq ) if gq.self.length > 0 26 | } 27 | return l 28 | end 29 | 30 | def put( prop, v ) 31 | if prop.kind_of?(Integer) 32 | i = prop 33 | if @x.__target_object 34 | r = @x.__target_object.self.resolve_value 35 | return unless r 36 | else 37 | r = nil 38 | end 39 | if i >= @x.__list.length 40 | if r.is_a?(XmlList) 41 | return if r.self.length != 1 42 | else 43 | r = r[0] 44 | end 45 | y = Xml.new( @x.__target_property.to_sym, r ) 46 | if @x.__target_property =~ /^[@_]/ 47 | attribute_exists = r.self.get( y.self.name ) 48 | return if attributes_exists.self.length > 0 49 | y.self.class = :attribute 50 | elsif (!@x.__target_property) or @x.__target_property.local_name== '*' 51 | y.self.name = nil 52 | y.self.class = :text 53 | else 54 | t.self.class = :element 55 | end 56 | i = length 57 | if y.self.class != :attribute 58 | if y.self.parent 59 | if i > 0 60 | j = 0 61 | while ((j < (y.self.parent.self.length - 1)) and (y.self.parent[j] != @x[i-1])) do 62 | j += 1 63 | end 64 | else 65 | j = y.self.parent.self.length - 1 66 | end 67 | end 68 | if v.is_a?(Xml) 69 | y.self.name = v.slef.name 70 | elsif v.is_a?(XmlList) 71 | y.self.name = v.self.property_name 72 | else 73 | raise "invalid type" 74 | end 75 | end 76 | @x.self.append y 77 | end 78 | if ( !( v.is_a?(Xml) or v.is_a?(XmlList) ) or [:text, :attribute].include?(v.self.class) ) 79 | v = v.to_s #to_String 80 | end 81 | if @x[i].self.class == :attribute 82 | @x[i].self.parent.self.put( x[i].self.name, v ) 83 | attr = x[i].self.parent.self.get( x[i].self.name ) 84 | x[i] = attr[0] 85 | elsif v.is_a?(XmlList) 86 | c = v.dup 87 | parent = x[i].self.parent 88 | if parent 89 | q = parent.self.index(@x[i]) 90 | parent.self.replace( q, c ) 91 | (0...c.self.length).each { |j| c[j] = parent[q+j] } 92 | end 93 | (c.self.length-1..i).each { |j| @x[j+c.self.length] = @x[j] } 94 | (0...c.self.length).each { |j| @x[i+j] = c[j] } 95 | elsif v.is_a?(Xml) or [:text, :comment, :instruction].include?(@x[i].self.class) 96 | parent = @x[i].self.parent 97 | if parent 98 | q = parent.self.index(@x[i]) 99 | parent.self.replace( q, v ) 100 | v = parent[q] 101 | end 102 | x[i] = v 103 | else 104 | x[i].self.put( '*', v ) 105 | end 106 | else 107 | if length == 0 108 | r = @x.resolve_value 109 | return if !r or r.self.length != 1 110 | @x.self.append( r ) 111 | end 112 | @x[0].self.put( prop, v ) 113 | end 114 | return 115 | end 116 | 117 | def append( v ) 118 | i = @x.self.length 119 | n = 1 120 | case v 121 | when XmlList 122 | @x.__target_object = v.self.target_object 123 | @x.__target_property = v.self.target_property 124 | n = v.self.length 125 | return if n == 0 126 | (0...v.self.length).each { |i| @x[i+j] = v[j] } 127 | when Xml 128 | @x.__target_object = v.self.parent 129 | if v.self.class == :instruction 130 | @x.__target_property = nil 131 | else 132 | @x.__target_property = v.self.name 133 | end 134 | @x[i] = v 135 | # @x.length += n 136 | else 137 | raise 'Xml or XmlList expected' 138 | end 139 | end 140 | 141 | def resolve_value 142 | return if length > 0 143 | unless target_object and target_property 144 | if target_property =~ /^[_@]/ or target_property.local_name == '*' 145 | return nil 146 | end 147 | end 148 | base = target_object.self.resolve_value # recursive 149 | return nil unless base 150 | target = base.self.get( target_property ) 151 | if target.self.length == 0 152 | return nil if base.is_a?(XmlList) and base.self.length > 1 153 | base.self.put( target_property, '' ) 154 | target = base.self.get( target_property ) 155 | end 156 | return target 157 | end 158 | 159 | def length 160 | @x.__list.length 161 | end 162 | 163 | end 164 | 165 | end 166 | -------------------------------------------------------------------------------- /work/re4x.rb: -------------------------------------------------------------------------------- 1 | require 'rexml/document' 2 | require 'rexml/xpath' 3 | 4 | require 'facet/module/basename' 5 | require 'facet/array/store' 6 | 7 | 8 | # Some careful modification to REXML 9 | 10 | class REXML::Attribute 11 | def replace( val ) 12 | @value = val.to_s 13 | @normalized = @unnormalized = nil 14 | end 15 | end 16 | 17 | 18 | # This is used it identify strings that conform to canonical XML, 19 | # ie. "content". 20 | 21 | module XmlCanonical 22 | module_function 23 | def ===( str ) 24 | str = str.to_s.strip 25 | #str[0,1] == '<' && str[-1,1] == '>' # should make more robust (TODO) 26 | md = (%r{\A \< (\w+) (.*?) \>}mix).match( str ) 27 | return false unless md #[2] =~ %r{xml:cdata=['"]\d+['"]} 28 | ed = (%r{\< \/ #{md[1]} [ ]* \> \Z}mix).match(str) 29 | return false unless ed 30 | true 31 | end 32 | end 33 | 34 | 35 | # The Great Call 36 | 37 | def xml( x ) 38 | case x 39 | when Xml 40 | x 41 | when REXML::Element 42 | XmlElement.new x 43 | when REXML::Text 44 | XmlText.new x 45 | when REXML::Attribute 46 | XmlAttribute.new x 47 | when REXML::Instruction 48 | XmlInstruction.new x 49 | when REXML::Comment 50 | XmlComment.new x 51 | when XmlCanonical 52 | XmlElement.new(REXML::Document.new(x).root) 53 | else 54 | raise TypeError, "non-canonical xml #{x}" 55 | # String ? 56 | # ? to_s 57 | end 58 | end 59 | 60 | # The Xml Class 61 | 62 | class Xml 63 | def node! ; @node ; end 64 | def name! ; @node.name ; end 65 | def class! ; @node.class.basename.downcase.to_sym ; end 66 | def text! ; @node.to_s ; end 67 | def value! ; @node.to_s ; end 68 | 69 | def to_s ; @node.to_s ; end 70 | def to_i ; @node.to_s.to_i ; end # ? 71 | end 72 | 73 | 74 | # The XmlText Virtual Class 75 | 76 | class XmlText < Xml 77 | def initialize( node ) 78 | raise unless REXML::Text === node 79 | @node = node 80 | end 81 | def name! ; nil ; end 82 | end 83 | 84 | # The XmlAttribute Virtual Class 85 | 86 | class XmlAttribute < Xml 87 | def initialize( node ) 88 | raise unless REXML::Attribute === node 89 | @node = node 90 | end 91 | end 92 | 93 | # The XmlElement Virtual Class 94 | 95 | class XmlElement < Xml 96 | 97 | def method_missing( name, *args ) 98 | name = name.to_s 99 | name.gsub!( /^_/, '@' ) 100 | if name =~ /=$/ 101 | name.gsub!( /=$/, "" ) 102 | put!( name, args[0] ) 103 | else 104 | get!( name ) 105 | end 106 | end 107 | 108 | def initialize( node ) 109 | raise unless REXML::Element === node 110 | @node = node 111 | end 112 | 113 | def []( key ) ; get!( key ) ; end 114 | def []=( key, val ) ; put!( key, val ) ; end 115 | 116 | def each ; @node.each { |e| yield e } ; end 117 | 118 | def text! ; @node.text ; end 119 | def value! ; @node.text ; end 120 | 121 | def attributes!() @node.attributes ; end 122 | 123 | def get!( key ) 124 | XmlList.new( xpath!( key.to_s ) ) 125 | end 126 | 127 | def put!( key, val ) 128 | key=key.to_s 129 | if key =~ /^@/ 130 | @node.attributes[ key ] = val 131 | else 132 | r = get!( key ) 133 | case r.size 134 | when 0 135 | r.put!( key, val ) 136 | when 1 137 | r.replace!( val ) 138 | else 139 | raise "how many?" 140 | end 141 | end 142 | end 143 | 144 | def add!( n ) 145 | case n 146 | when Xml 147 | @node << n.node! 148 | when XmlList 149 | n.each { |e| self << e } 150 | else 151 | @node.add_text n.to_s 152 | end 153 | self 154 | end 155 | 156 | alias :<< :add! 157 | #alias :+ :add! 158 | 159 | def replace!( val ) 160 | case @node 161 | when REXML::Attribute 162 | @node.replace( val ) 163 | when REXML::Element 164 | @node.delete_element('*') 165 | @node.text = nil 166 | add!( val ) 167 | end 168 | end 169 | 170 | def xpath!( name ) 171 | children = XmlList.new() 172 | REXML::XPath.each( @node, "#{name}" ) { |elem| 173 | children.push( xml( elem ) ) 174 | } 175 | children 176 | end 177 | 178 | end 179 | 180 | 181 | class XmlList < Array 182 | 183 | #def method_missing( sym, *args ) 184 | # self[0].send( sym, *args ) 185 | #end 186 | 187 | def to_s 188 | self.join('') 189 | end 190 | 191 | def get!( key ) 192 | case key 193 | when Integer 194 | at(key) 195 | else 196 | case size 197 | when 0 198 | [] 199 | when 1 200 | at(0).get!( key ) 201 | else 202 | XmlList.new( collect{ |e| e.xpath!( key.to_s ) } ) 203 | end 204 | end 205 | end 206 | 207 | def put!( key, val ) 208 | if Integer === key and key >= size 209 | add!( xml(val) ) #? 210 | else 211 | r = get!( key ) 212 | case r.size 213 | when 0 214 | return nil 215 | when 1 216 | r[0].replace!( val ) 217 | else 218 | return nil 219 | end 220 | end 221 | end 222 | 223 | def add!( n ) 224 | self.push(n) 225 | self 226 | end 227 | 228 | alias :<< :add! 229 | 230 | def []( key ) 231 | get!( key ) 232 | end 233 | 234 | def []=( key, val ) 235 | put!( key, val ) 236 | end 237 | 238 | end 239 | 240 | 241 | # internal testing 242 | 243 | if $0 == __FILE__ 244 | 245 | q = %{ 246 | 247 | 248 | 2004-12-18 249 | Santa 250 | 0.32 251 | 2.45 252 | 253 | 254 | 2003-12-18 255 | Bunny 256 | 0.32 257 | 1.20 258 | 259 | 260 | } 261 | 262 | x = xml( q ) 263 | 264 | puts 265 | puts x 266 | puts 267 | puts x['stamp'][0]['face'] 268 | x['stamp'][0]['face'] = "5.00" 269 | puts x['stamp'][0]['face'] 270 | 271 | end 272 | -------------------------------------------------------------------------------- /work/r4x.rb: -------------------------------------------------------------------------------- 1 | # 2 | # This key idea behingd this implementation is that the XML 3 | # is actually stored in String form throughout (albiet 4 | # the parserd form in cached for speed). as the XML is processed 5 | # it is parsed on the fly. Becuase of this, simply inserting 6 | # a valid XML string into a node will "automatically" take. 7 | # 8 | # Of course, that's the idea. Implementation is a little tricky. 9 | # 10 | 11 | 12 | require 'nano/string/shatter' 13 | require 'nano/string/shift' 14 | require 'nano/string/dequote' 15 | 16 | 17 | START_TAG_RE = %r{\A \< (\w+) (.*?) \>}mix 18 | 19 | # This is used it identify strings that conform to canonical XML element text. 20 | # It could afford to be made alittle more robust. 21 | # 22 | module XmlCanonical 23 | module_function 24 | def ===( str ) 25 | str = str.to_s.strip 26 | #str[0,1] == '<' && str[-1,1] == '>' # should make more robust (TODO) 27 | md = (%r{\A \< (\w+) (.*?) \>}mix).match( str ) 28 | return false unless md #[2] =~ %r{xml:cdata=['"]\d+['"]} 29 | ed = (%r{\< \/ #{md[1]} [ ]* \> \Z}mix).match(str) 30 | return false unless ed 31 | true 32 | end 33 | end 34 | 35 | 36 | def xml( str_or_arr ) 37 | Xml.new( str_or_arr ) 38 | end 39 | 40 | class Xml 41 | 42 | def initialize( x ) 43 | case x 44 | when String 45 | raise unless XmlCanonical === x 46 | @canonical = x.strip 47 | children 48 | when Array 49 | @canonical = x.join('') 50 | @children = x 51 | else 52 | raise ArgumentError 53 | end 54 | end 55 | 56 | #def reset 57 | # @name = nil 58 | # @attributes = nil 59 | # @elements = nil 60 | #end 61 | 62 | def children 63 | @children ||= __parse__( @canonical )[0][1...-1] 64 | end 65 | 66 | def elements 67 | @elements ||= children.select { |e| XmlCanonical === e } 68 | end 69 | 70 | def texts 71 | @texts ||= children.reject { |e| XmlCanonical === e } 72 | end 73 | 74 | def elements_assoc 75 | @elements.collect { |e| [ START_TAG_RE.match( e[0] )[1], e ] } 76 | end 77 | 78 | def name 79 | @name ||= START_TAG_RE.match( @canonical )[1] 80 | end 81 | 82 | def attributes 83 | unless @attributes 84 | s = START_TAG_RE.match( @canonical )[2].strip 85 | a = s.split( %r{\s+|\=}x ).collect { |e| e.dequote } 86 | @attributes = Hash[ *a ] 87 | end 88 | @attributes 89 | end 90 | 91 | def value 92 | @children.join('') 93 | end 94 | 95 | def []( k ) 96 | if Integer === k 97 | e = children[k] 98 | XmlCanonical === e ? xml( e ) : e 99 | elsif k == '*' 100 | xml( elements ) 101 | elsif k == '@*' 102 | attributes 103 | elsif k =~ /^[@]/ 104 | attributes[ k.shift ] 105 | else 106 | q = elements.select { |e| START_TAG_RE.match( e[0] )[1] == k } 107 | if q.length == 0 108 | nil 109 | elsif q.length == 1 110 | ::Xml.new( q[0] ) 111 | else 112 | ::XmlList.new( q ) 113 | end 114 | end 115 | end 116 | 117 | end 118 | 119 | 120 | class XmlList 121 | 122 | def initialize( a ) 123 | @canonical = a.join('') 124 | @children = a 125 | end 126 | 127 | def children 128 | @children 129 | end 130 | 131 | def elements 132 | @elements ||= children.select { |e| ::XmlCanonical === e.join('') } 133 | end 134 | 135 | def texts 136 | @texts ||= children.reject { |e| ::XmlCanonical === e.join('') } 137 | end 138 | 139 | def elements_assoc 140 | @elements.collect { |e| [ START_TAG_RE.match( e[0] )[1], e ] } 141 | end 142 | 143 | def value 144 | @children.join('') 145 | end 146 | 147 | def []( k ) 148 | case k 149 | when Integer 150 | q = @children[k] 151 | return q unless q 152 | if q.length == 1 153 | ::Xml.new( q ) 154 | else 155 | ::XmlList.new( q ) 156 | end 157 | when '*' 158 | when '@*' 159 | else 160 | elements.each { |e| START_TAG_RE.match( e[0] )[1] == k } 161 | end 162 | end 163 | 164 | end 165 | 166 | 167 | #module XmlUtil 168 | 169 | RETAG = %r{ \< (\/)? ([\w.:]+) (.*?) ([/])? \> }mix 170 | IDXEND = 1 171 | IDXNAME = 2 172 | IDXATTR = 3 173 | IDXUNIT = 4 174 | 175 | #module_function 176 | 177 | def __parse__(xmldata) 178 | if String === xmldata 179 | q = xmldata.strip.shatter(RETAG) 180 | else 181 | q = xmldata.dup 182 | end 183 | # setup 184 | build = [] 185 | current = build 186 | stack = [] 187 | # stack loop 188 | until q.empty? 189 | e = q.shift 190 | if md = RETAG.match( e ) 191 | if md[IDXEND] 192 | #close-tag 193 | current << e 194 | current = stack.pop 195 | elsif md[IDXUNIT] == '/' 196 | #unit-tag 197 | current << e 198 | else 199 | #open-tag 200 | stack << current 201 | current << [] 202 | current = current.last 203 | current << e 204 | end 205 | else 206 | current << e #if e != "\n" 207 | end 208 | end 209 | return build 210 | end 211 | 212 | #end 213 | 214 | 215 | if $0 == __FILE__ 216 | 217 | xs = %{ 218 | 219 | 220 | Tom 221 | 35 222 | 223 | 224 | George 225 | 30 226 | 227 | 228 | } 229 | 230 | x = xml( xs ) 231 | 232 | puts 233 | p x 234 | puts 235 | p x.name 236 | puts 237 | p x.attributes 238 | puts 239 | p x.children 240 | puts 241 | p x.elements 242 | # puts 243 | # p x.texts 244 | # puts 245 | # p x.elements_assoc 246 | # puts 247 | # p x[1] 248 | # puts 249 | # p x['*'] 250 | # puts 251 | # p x['@*'] 252 | # puts 253 | # p x['@type'] 254 | # puts 255 | # p x['person'] 256 | # puts 257 | # 258 | 259 | puts 260 | p x['person'] 261 | puts 262 | p x['person']['name'] 263 | 264 | end 265 | -------------------------------------------------------------------------------- /lib/r4x/xml_delegate.rb: -------------------------------------------------------------------------------- 1 | 2 | class Xml #< BlankSlate 3 | 4 | class XmlDelegate 5 | 6 | include Enumerable 7 | 8 | def initialize( x ) 9 | @x = x 10 | end 11 | 12 | def node ; @x.__node ; end 13 | def name ; @x.__node.name ; end 14 | def text ; @x.__node.text ; end 15 | 16 | def klass ; @x.__class ; end 17 | def parent ; @x.__parent ; end 18 | def attributes ; @x.__node.attributes ; end 19 | def value ; @x.__node.children.join('') ; end 20 | 21 | # 22 | def get(prop) 23 | if Integer === prop 24 | l = to_XmlList 25 | return l.__get__(prop) 26 | end 27 | n = prop.self.to_XmlName 28 | l = XmlList.new([],@x,n) 29 | if AttributeName === n 30 | @x.attributes.each { |a| 31 | if ( ( n.name.local_name =='*' || n.name.local_name == a.name.local_name ) and 32 | ( n.name.uri == nil || n.name.uri == a.name.uri ) ) 33 | l.append(a) 34 | end 35 | } 36 | return l 37 | end 38 | (0...length).each { |k| 39 | if ( (n.local_name == '*') or 40 | ((Element === @x[k]) and (@x[k].name.local_name == a.name.local_name)) ) and 41 | ( (n.uri == nil) or ((@x[k].__class__ == "element") and (n.uri == @x[k].name.uri)) ) 42 | l.append(@x[k]) 43 | end 44 | } 45 | return l 46 | end 47 | 48 | # def get( prop ) 49 | # if prop.kind_of?(Integer) 50 | # l = to_XmlList 51 | # return l.self.get( prop ) 52 | # end 53 | # #n = prop.to_XmlName 54 | # l = XmlList.new([], @x, prop) 55 | # # if prop =~ /^[@_]/ 56 | # # @x.__node.attributes.each{ |a| 57 | # # l.self.append a 58 | # # } 59 | # # return l 60 | # # end 61 | # REXML::XPath.each( @x.__node, prop.to_s ) { |elem| 62 | # l.self.append( Xml.new( elem ) ) 63 | # } 64 | # return l 65 | # end 66 | 67 | # def put( key, val ) 68 | # if key =~ /^[@_]/ 69 | # @x.__node.attributes[ key.shift ] = value 70 | # elsif gk = get(key) 71 | # case gk.size 72 | # when 0 73 | # add "<#{key}>#{val}" 74 | # when 1 75 | # gk[0] #? 76 | # else 77 | # raise "unimplemented" 78 | # #? What to do then? 79 | # end 80 | # else 81 | # insert( key, val ) 82 | # end 83 | # end 84 | 85 | # 86 | def put( prop, val ) 87 | if !(Xml === val or XmlList === val) or ( [:text,:attribute].include?(val.self.class) ) 88 | c = val.to_s 89 | else 90 | c = val.self.deepcopy 91 | end 92 | raise ArgumentError if Numeric === prop 93 | return if [:text,:attribute,:comment,:instruction].include?( klass ) 94 | n = prop.to_XmlName 95 | default_namespace = get_default_namespace() 96 | if AttributeName === n 97 | return unless is_XmlName(n.self.name) 98 | if XmlList === c 99 | if c.length === 0 100 | c = '' 101 | else 102 | #s = c[0].to_s 103 | #(1...c.length).each { |i| s += " #{c[i].to_s}" 104 | s = c.join(' ') 105 | end 106 | else 107 | c = c.to_s 108 | end 109 | a = nil 110 | x.attributes.each { |j| 111 | if (n.name.local_name == j.name.local_name) and (n.name.uri == nil or n.name.uri == j.name.uri) 112 | a = j unless a 113 | else 114 | x.delete(j.name) 115 | end 116 | } 117 | unless a 118 | unless n.name.uri 119 | nons = Namespace.new 120 | name = QName.new( nons, n.name ) 121 | else 122 | name = QName.new( n.name ) 123 | end 124 | a = Xml.new { |s| s.name=name ; s.classification=:attribute ; s.parent=x } 125 | x.attributes << a 126 | ns = name.get_namespace 127 | x.add_in_scope_namespace(ns) 128 | end 129 | a.value = c 130 | return 131 | end 132 | is_valid_name = is_XmlName(n) 133 | return if !is_valid_name and n.local_name != '*' 134 | i = nil 135 | primitive_assign = ((!(Xml === c or XmlList === c)) and n.local_name != '*') 136 | (x.length-1).downto(0) { |k| 137 | if ( (n.local_name == '*') or ( (@x[k].classification == :element) and (@x[k].name.locall_name == n.local_name) ) ) and 138 | ( (n.uri == nil) or ( (@x[k].classification==:element) and (n.uri == @x[k].name.uri) ) ) 139 | if i 140 | x.delete_by_index(i.to_s) 141 | i = k 142 | end 143 | end 144 | } 145 | unless i 146 | i = x.length 147 | if primitive_assign 148 | unless n.uri 149 | name = QName.new( default_namespace, n ) 150 | else 151 | name = QName.new(n) 152 | end 153 | y=Xml.new{|s| s.name=name; s.classification=:element; s.parent=x} 154 | ns=name.get_namespace 155 | x.replace(i.to_s,y) 156 | y.add_in_scope_namespace(ns) 157 | end 158 | end 159 | if primitive_asign 160 | # x[i].delete_all_properties 161 | s = c.to_s 162 | x[i].replace("0",s) if s != '' 163 | else 164 | x.replace(i.to_s,c) 165 | end 166 | return 167 | end 168 | 169 | def add( nodes ) 170 | case nodes 171 | when XmlList 172 | nodes.each { |n| @x.__node << n.__node } 173 | else 174 | @x.__node << nodes.__node 175 | end 176 | @x 177 | end 178 | 179 | # 180 | def delete(prop) 181 | raise ArgumentError if Numeric === prop 182 | n = prop.self.to_XmlName 183 | if AttributeName === n 184 | attribs = attributes.collect{|a| 185 | if ((n.name.local_name == '*') or (n.name.local_name == a.name.local_name)) and 186 | ((n.name.uri == nil) or (n.name.uri == a.name.uri)) 187 | a.parent = nil 188 | nil 189 | else 190 | a 191 | end 192 | }.compact 193 | return true 194 | end 195 | dp = 0 196 | (0...length).each { |q| 197 | if ((n.local_name == '*') or 198 | (@x[q].self.class == :element and @x[q].self.name.local_namespace == n.local_name)) and 199 | ((n.uri == nil) or (@x[q].self.class == :element and n.uri == @x[q].self.name.uri)) 200 | x[q].parent = nil 201 | x.delete_at(q) 202 | dp+=1 203 | else 204 | if dp > 0 205 | @x[q - dp] = x[q] 206 | @x.delete_at(q) 207 | end 208 | end 209 | } 210 | return true 211 | end 212 | 213 | # 214 | def delete_by_index(prop) 215 | end 216 | 217 | # 218 | def default_value 219 | end 220 | 221 | # 222 | def has_property 223 | end 224 | 225 | # 226 | def deep_copy 227 | end 228 | 229 | # 230 | def descendents(prop) 231 | end 232 | 233 | # 234 | def equals(value) 235 | end 236 | 237 | # 238 | def resolve_value 239 | @x 240 | end 241 | 242 | # 243 | def insert(prop,value) 244 | end 245 | 246 | # 247 | def replace(prop,value) 248 | end 249 | 250 | # 251 | def add_in_scope_namespace__(namespace) 252 | end 253 | 254 | def delete( key ) 255 | @x.__node.delete_element( key ) #string key is an xpath 256 | end 257 | 258 | 259 | # Conversions 260 | 261 | def to_XmlList 262 | XmlList.new( @x ) 263 | end 264 | 265 | # def to_XmlList 266 | # target_object = self.__parent__ 267 | # target_poperty = self.__name__ 268 | # l = XmlList.new([self], target_object, target_property) 269 | # end 270 | 271 | def to_XmlName 272 | s = to_String 273 | if s =~ /^[_@]/ 274 | to_AttributeName( s.shift ) 275 | else 276 | QName.new(s) 277 | end 278 | end 279 | 280 | def to_String 281 | return value if [:attribute, :text].include?(@x) 282 | if has_simple_content 283 | s = '' 284 | @x.each { |e| s << e unless [:comment,:instruction].include?(e.self.class) } 285 | return s 286 | else 287 | to_XmlString 288 | end 289 | end 290 | 291 | def to_XmlString( ancestor_namespaces=nil, indent_level=nil ) 292 | 293 | end 294 | 295 | end 296 | 297 | end 298 | -------------------------------------------------------------------------------- /work/e4x-old.rb: -------------------------------------------------------------------------------- 1 | # 2 | # E4X for Ruby 3 | # 4 | # 5 | 6 | require 'rexml/document' 7 | require 'rexml/xpath' 8 | 9 | def xml(xmldata) 10 | Xml.new(xmldata) 11 | end 12 | 13 | module EMCAScript 14 | 15 | def toXmlList 16 | 17 | end 18 | 19 | end 20 | 21 | 22 | class Xml 23 | 24 | # def method_missing( name, *args ) 25 | # name = name.to_s 26 | # if ( name =~ /^_/ ) 27 | # name.gsub!( /^_/, "" ) 28 | # if ( name =~ /=$/ ) 29 | # name.gsub!( /=$/, "" ) 30 | # _write_attribute( name, args[0] ) 31 | # else 32 | # _read_attribute( name ) 33 | # end 34 | # else 35 | # xpath( "#{name}" ) 36 | # end 37 | # end 38 | 39 | def initialize( node ) 40 | #@node = node 41 | @node = REXML::Document.new(xmldata).root 42 | end 43 | 44 | def to_XmlList 45 | target_object = self.__parent__ 46 | target_poperty = self.__name__ 47 | l = XmlList.new([self], target_object, target_property) 48 | end 49 | 50 | def name 51 | @node.name 52 | end 53 | 54 | def parent 55 | @node.parent 56 | end 57 | 58 | def attributes 59 | @node.attributes 60 | end 61 | 62 | def in_scope_namspace 63 | @node.namespace 64 | end 65 | 66 | def length 67 | @node.children.length 68 | end 69 | 70 | # 71 | def get(prop) 72 | x = self 73 | if Integer === prop 74 | l = to_XmlList 75 | return l.__get__(prop) 76 | end 77 | n = prop.to_XmlName 78 | l = XmlList.new([],x,n) 79 | if AttributeName === n 80 | x.attributes.each { |a| 81 | if (( n.name.local_name =='*' || n.name.local_name == a.name.local_name ) 82 | and ( n.name.uri == nil || n.name.uri == a.name.uri )) 83 | l.append(a) 84 | end 85 | } 86 | return l 87 | end 88 | (0...x.Length).each { |k| 89 | if ((n.local_name == '*') 90 | or ((Element === x[k]) and (x[k].name.local_name == a.name.local_name))) 91 | and ((n.uri == nil) or ((x[k].__class__ == "element") and (n.uri == x[k].name.uri))) 92 | l.append(x[k]) 93 | end 94 | } 95 | return l 96 | end 97 | 98 | # 99 | def put( prop, val ) 100 | x = self 101 | if !(Xml === val or XmlList === val) or ( [:text,:attribute].include?(val.classification) ) 102 | c = val.to_s 103 | else 104 | c = val.deepcopy 105 | end 106 | raise ArgumentError if Numeric === prop 107 | return if [:text,:attribute,:comment,:processing_instruction].inlcude?( x.classification ) 108 | n = prop.to_XmlName 109 | default_namespace = get_default_namespace 110 | if AttributeName === n 111 | return unless is_XmlName(n.name) 112 | if XmlList === c 113 | if c.length === 0 114 | c = '' 115 | else 116 | #s = c[0].to_s 117 | #(1...c.length).each { |i| s += " #{c[i].to_s}" 118 | s = c.join(' ') 119 | end 120 | else 121 | c = c.to_s 122 | end 123 | a = nil 124 | x.attributes.each { |j| 125 | if (n.name.local_name == j.name.local_name and (n.name.uri == nil or n.name.uri == j.name.uri) 126 | a = j unless a 127 | else 128 | x.delete(j.name) 129 | end 130 | } 131 | unless a 132 | unless n.name.uri 133 | nons = Namespace.new 134 | name = QName.new( nons, n.name ) 135 | else 136 | name = QName.new( n.name ) 137 | end 138 | a = Xml.new { |s| s.name=name ; s.classification=:attribute ; s.parent=x } 139 | x.attributes << a 140 | ns = name.get_namespace 141 | x.add_in_scope_namespace(ns) 142 | end 143 | a.value = c 144 | return 145 | end 146 | is_valid_name = is_XmlName(n) 147 | return if !is_valid_name and n.local_name != '*' 148 | i = nil 149 | primitive_assign = ((!(Xml === c or XmlList === c)) and n.local_name != '*') 150 | (x.length-1).downto(0) { |k| 151 | if ((n.local_name == '*') or (( x[k].classification == :element) and (x[k].name.locall_name == n.local_name))) 152 | and ((n.uri == nil) or ((x[k].classification==:element) and (n.uri == x[k].name.uri))) 153 | if i 154 | x.delete_by_index(i.to_s) 155 | i = k 156 | end 157 | end 158 | } 159 | unless i 160 | i = x.length 161 | if primitive_assign 162 | unless n.uri 163 | name = QName.new( default_namespace, n ) 164 | else 165 | name = QName.new(n) 166 | end 167 | y=Xml.new{|s| s.name=name; s.classification=:element; s.parent=x} 168 | ns=name.get_namespace 169 | x.replace(i.to_s,y) 170 | y.add_in_scope_namespace(ns) 171 | end 172 | end 173 | if primitive_asign 174 | # x[i].delete_all_properties 175 | s = c.to_s 176 | x[i].replace("0",s) if s != '' 177 | else 178 | x.replace(i.to_s,c) 179 | end 180 | return 181 | end 182 | 183 | # 184 | def delete(prop) 185 | x = self 186 | raise ArgumentError if Numeric === prop 187 | n = to_XmlName(prop) 188 | if AttributeName === n 189 | x.attributes = x.attributes.collect{|a| 190 | if ((n.name.local_name == '*') or (n.name.local_name == a.name.local_name)) 191 | and ((n.name.uri == nil) or (n.name.uri == a.name.uri)) 192 | a.parent = nil 193 | nil 194 | else 195 | a 196 | end 197 | }.compact 198 | return true 199 | end 200 | dp = 0 201 | (0...x.length).each{|q| 202 | if ((n.local_name == '*') 203 | or (x[q].classification == :element and x[q].name.local_namespace == n.local_name)) 204 | and ((n.uri == nil) or (x[q].classification == :element and n.uri == x[q].name.uri)) 205 | x[q].parent = nil 206 | x.delete_at(q) 207 | dp+=1 208 | else 209 | if dp > 0 210 | x[q - dp] = x[q] 211 | x.delete_at(q) 212 | end 213 | end 214 | } 215 | x.length = x.length - dp # really need to do this? 216 | return true 217 | end 218 | 219 | # 220 | def delete_by_index(prop) 221 | end 222 | 223 | # 224 | def __default_value__ 225 | end 226 | 227 | # 228 | def __has_property__ 229 | end 230 | 231 | # 232 | def __deep_copy__ 233 | end 234 | 235 | # 236 | def __descendents__(prop) 237 | end 238 | 239 | # 240 | def __equals__(value) 241 | end 242 | 243 | # 244 | def __resolve_value__ 245 | end 246 | 247 | # 248 | def __insert__(prop,value) 249 | end 250 | 251 | # 252 | def __replace__(prop,value) 253 | end 254 | 255 | # 256 | def __add_in_scope_namespace__(namespace) 257 | end 258 | 259 | 260 | 261 | 262 | 263 | 264 | def __text() @node.text; end 265 | 266 | def to_s() @node.to_s; end 267 | 268 | def to_i() @node.to_s.to_i; end 269 | 270 | def _add ( nodes ) 271 | @node << nodes._get_node 272 | self 273 | end 274 | 275 | alias :<< :_add 276 | 277 | alias :+ :_add 278 | 279 | def _get_node() @node; end 280 | 281 | def xpath( name ) 282 | out = XmlList.new() 283 | @node.each_element( name ) { |elem| 284 | out.push( Xml.new( elem ) ) 285 | } 286 | return out 287 | #children = XmlList.new() 288 | #REXML::XPath.each( @node, "#{name}" ) { |elem| 289 | # children.push( Xml.new( elem ) ) 290 | #} 291 | #children 292 | end 293 | 294 | private 295 | 296 | def _read_attribute( name ) 297 | @node.value.to_s if @node.class == REXML::Attribute 298 | @node.attributes[ name ].to_s if @node.class == REXML::Element 299 | end 300 | 301 | def _write_attribute( name, value ) 302 | @node.attributes[ name ] = value 303 | end 304 | 305 | end 306 | 307 | class XmlList < Array 308 | def initialize( content, target_object, target_property ) 309 | @target_object = target_object 310 | @target_property = target_poperty 311 | super( content ) 312 | end 313 | 314 | def method_missing( name, *args ) 315 | name = name.to_s 316 | if (args.size > 0) 317 | self[0].send( name, args ) 318 | else 319 | self[0].send(name) 320 | end 321 | end 322 | end 323 | 324 | class AttributeName 325 | def initialize( name ) 326 | @name = name 327 | end 328 | 329 | def to_s 330 | "@#{@name}" 331 | end 332 | end 333 | 334 | 335 | # test 336 | 337 | x = xml %Q{ 338 | 339 | 340 | Tom 341 | 35 342 | 343 | 344 | Becky 345 | 32 346 | 347 | 348 | } 349 | 350 | p x 351 | puts x.people 352 | 353 | 354 | -------------------------------------------------------------------------------- /lib/r4x/e4x.rb: -------------------------------------------------------------------------------- 1 | 2 | # E4X (Lite) 3 | # 4 | # This is a E4X library making it easy to parse and manipulate XML documents. 5 | # It is not a compliant implementaion of E4X, but it is very close to being so 6 | # from the end-user's point-of-view. 7 | # 8 | # This library uses REXML as a backend. An option to use libxml may be added in 9 | # the future for speed. 10 | 11 | require 'rexml/document' 12 | require 'rexml/xpath' 13 | 14 | #require 'facet/object/deepcopy' 15 | require 'facet/string/shift' 16 | require 'facet/string/pop' 17 | 18 | require 'e4x/qname' 19 | require 'e4x/xml_delegate' 20 | require 'e4x/xmllist_delegate' 21 | 22 | 23 | def xml( xmldata ) 24 | Xml.new(xmldata) 25 | end 26 | 27 | class Xml 28 | 29 | # This is used it identify strings that conform to canonical XML element text. 30 | module XmlCanonical 31 | module_function 32 | def ===( str ) 33 | str = str.to_s.strip 34 | #str[0,1] == '<' && str[-1,1] == '>' # should make more robust (TODO) 35 | md = (%r{\A \< (\w+) (.*?) \>}mix).match( str ) 36 | return false unless md #[2] =~ %r{xml:cdata=['"]\d+['"]} 37 | ed = (%r{\< \/ #{md[1]} [ ]* \> \Z}mix).match(str) 38 | return false unless ed 39 | true 40 | end 41 | end 42 | 43 | 44 | VALID_CLASSES = [ :element, :text, :comment, :instruction, :attribute ] 45 | 46 | # These are for a special usage (non-standard XML); ignored for all standard purposes. 47 | #VERB_INDICATOR = '|' 48 | #VERB_REGEX = %r{ \< ([\w.:]+) (.*?) ([#{VERB_INDICATOR}]\d*) \> }mix 49 | 50 | class << self 51 | alias :__new :new 52 | def new( xmldata, parent=nil ) 53 | case xmldata 54 | when nil, '' 55 | raise ArgumentError 56 | when Xml, Symbol, XmlCanonical 57 | __new( xmldata, parent ) 58 | when REXML::Element, REXML::Text, REXML::Attribute, REXML::Instruction 59 | __new( xmldata, parent ) 60 | else 61 | XmlList.new(xmldata, parent) 62 | end 63 | end 64 | end 65 | 66 | def initialize( xmldata, parent=nil ) 67 | @parent = parent 68 | case xmldata 69 | when Xml 70 | @node = xmldata.__node.dup 71 | @class = xmldata.__class.dup 72 | @name = xmldata.__node.name.dup 73 | when Symbol 74 | #@node = REXML::Document.new( "<#{xmldata}>" ).root 75 | #@node = REXML::Element.new( "<#{xmldata}>" ).root 76 | @node = REXML::Element.new( xmldata ) 77 | @class = :element 78 | @name = @node.name 79 | when XmlCanonical 80 | xmldata = xmldata.to_s 81 | @node = REXML::Document.new( xmldata.strip ).root 82 | #@node = REXML::Element.new( xmldata.strip ) 83 | @class = :element 84 | @name = @node.name 85 | when REXML::Element 86 | @node = xmldata 87 | @class = :element 88 | @name = @node.name 89 | when REXML::Text 90 | @node = xmldata 91 | @class = :text 92 | @name = nil 93 | when REXML::Attribute 94 | @node = xmldata 95 | @class = :attribute 96 | @name = xmldata.name 97 | when REXML::Instruction 98 | @node = xmldata 99 | @class = :instruction 100 | @name = xmldata.target 101 | else 102 | raise ArgumentError, "invlaid xml" 103 | end 104 | @self ||= XmlDelegate.new(self) 105 | end 106 | 107 | # These are reserved tag names, i.e. they can't be used as xml tags 108 | # and then accessed via the call syntax of E4X. 109 | # 110 | # self 111 | # __node 112 | # __class 113 | # to_s 114 | # to_i 115 | # each 116 | # 117 | 118 | # This is how to access the underlying Xml object, i.e. via the delegate. 119 | def self ; @self ; end 120 | 121 | # This is how the delegate accesses the node. 122 | def __node ; @node ; end 123 | 124 | # This is how the delegate accesses the node classification. 125 | def __class ; @class ; end 126 | 127 | # Important for this to work in string interpolation. 128 | def to_s() @node.to_s ; end 129 | 130 | # ? 131 | def to_i() @node.to_s.to_i; end 132 | 133 | # (neccessary?) FIX!!! 134 | def each() 135 | @node.each_child { |c| yield(c) } 136 | end 137 | 138 | # Shortcut for add. 139 | def <<( n ) 140 | @self.add( n ) 141 | end 142 | 143 | # XPath for all elements. 144 | def * ; @self.get('*') ; end 145 | 146 | # Shortcut for XPath '@*', meaning all attributes. 147 | def _ ; @self.get('@*') ; end 148 | 149 | # XPath get operator. 150 | def []( key ) 151 | @self.get( key ) 152 | end 153 | 154 | #XPath put operator. 155 | def []=( key, val ) 156 | @self.put( key, val ) 157 | end 158 | 159 | # Here's where all the fun's at! 160 | # It's more complicated then it looks ;-) 161 | def method_missing( sym, *args ) 162 | sym = sym.to_s 163 | if sym.slice(-1) == '=' 164 | self["#{sym.pop}"] = args[0] 165 | else 166 | self["#{sym}"] 167 | end 168 | end 169 | 170 | end 171 | 172 | # 173 | # XmlList 174 | # 175 | class XmlList 176 | 177 | class << self 178 | alias_method( :__new, :new ) 179 | def new( xd=[], to=nil, tp=nil ) 180 | case xd 181 | when XmlList 182 | xd 183 | else 184 | __new( xd, to, tp ) 185 | end 186 | end 187 | end 188 | 189 | def initialize( xd=[], to=nil, tp=nil ) 190 | @self = XmlListDelegate.new(self) 191 | case xd 192 | when [] 193 | @list = [] 194 | @target_object = to 195 | @target_property = tp 196 | when Xml 197 | @list = [xd] 198 | @target_object = xd.self.parent 199 | @target_property = xd.self.name 200 | else 201 | xd = REXML::Document.new(%{<_>#{xd}}).root 202 | a = []; xd.each{ |n| a << Xml.new(n) } 203 | @list = a 204 | @target_object = nil 205 | @target_property = nil 206 | end 207 | end 208 | 209 | # This is how to access the underlying Xml object, i.e. via the delegate. 210 | def self ; @self ; end 211 | 212 | # This is how the delegate accesses the node. 213 | def __list ; @list ; end 214 | 215 | # This is how the delegate accesses the node classification. 216 | def __class ; :xmllist ; end 217 | 218 | # This is how the delegate accesses the node classification. 219 | def __target_object ; @target_object ; end 220 | def __target_object=(to) ; @target_object = to ; end 221 | 222 | # This is how the delegate accesses the node classification. 223 | def __target_property ; @target_property ; end 224 | def __target_property=(tp) ; @target_property = tp ; end 225 | 226 | # Important for this to work in string interpolation. 227 | def to_s() @list.to_s ; end 228 | 229 | # ? 230 | def to_i() @list.to_s.to_i; end 231 | 232 | # each 233 | def each(&blk) ; @list.each(&blk) ; end 234 | 235 | # Shortcut for add. 236 | def <<( n ) 237 | @self.add( n ) 238 | end 239 | 240 | # XPath for all elements. 241 | def * ; @self.get('*') ; end 242 | 243 | # Shortcut for XPath '@*', meaning all attributes. 244 | def _ ; @self.get('@*') ; end 245 | 246 | def []( v ) 247 | @self.get( v ) 248 | end 249 | 250 | def []=( prop, v ) 251 | @self.put( prop , v ) 252 | end 253 | 254 | # 255 | def method_missing( sym, *args ) 256 | sym = sym.to_s 257 | p sym 258 | if (args.size > 0) 259 | self[0].send( sym, args ) 260 | else 261 | self[0].send( sym ) 262 | end 263 | end 264 | 265 | end 266 | 267 | 268 | 269 | # test 270 | if $0 == __FILE__ 271 | 272 | XmlList.new('abc 123') 273 | 274 | x = xml %Q{ 275 | 276 | 277 | 278 | Tom 279 | ... 280 | 123 281 | 282 | 35 283 | 284 | 285 | Becky 286 | 32 287 | 288 | 289 | } 290 | 291 | puts x.* 292 | puts x._ 293 | 294 | puts x.self.name 295 | puts x.self.attributes 296 | 297 | puts x.person 298 | puts x['person'] 299 | puts x._id 300 | puts x['@id'] 301 | p x 302 | puts x[1] # skip whatespace? 303 | puts "HERE" 304 | x << xml(%{Timmy}) 305 | 306 | puts x['@*'] 307 | 308 | end 309 | 310 | 311 | 312 | # SCRAP 313 | 314 | # def __verb?(str) 315 | # str = str.strip 316 | # return nil unless __tag?(str) 317 | # md = (%r{\A \< (\w+) (.*?) \>}mix).match( str ) 318 | # p md 319 | # return nil unless md[2] =~ %r{xml:cdata=['"]\d+['"]} 320 | # ed = (%r{\< \/ #{n} [ ]* \> \Z}mix).match(str) 321 | # return nil unless ed 322 | # c = md[0] + '' + ed[0] 323 | # p c 324 | # c 325 | # #str = str.sub( %r{\A \< (\w+) .*? \>}mix ) { |m| n=$1 ; "#{m} \Z}mix ) { |m| "]]>#{m}" } 327 | # # n << md.post_match.gsub(%r{\< \/ #{md[1]} [ ]* \> \Z}mix, '') #[0...(md.post_match.rindex('<'))] # not robust enough 328 | # # n << "]]>" 329 | # # n << "" 330 | # end 331 | -------------------------------------------------------------------------------- /work/r4x2.rb: -------------------------------------------------------------------------------- 1 | require 'facet/string/shatter' 2 | require 'facet/string/dequote' 3 | 4 | 5 | def xml( x ) 6 | R4X::Xml.newq( x ) 7 | end 8 | 9 | 10 | module R4X 11 | 12 | START_TAG_RE = %r{\A \< (\w+) (.*?) \>}mix 13 | 14 | # This is used it identify strings that conform to canonical XML element text. 15 | # It could afford to be made alittle more robust. 16 | # 17 | module XmlCanonical 18 | def self.===( sa ) 19 | #sa = sa.join('') if Array === sa 20 | str = sa.to_s #.strip 21 | stag = (%r{\A \< (\w+) (.*?) (\/)? \>}mix).match( str ) 22 | return false unless stag 23 | return true if stag[3] # non-content 24 | etag = (%r{\< \/ #{stag[1]} [ ]* \> \Z}mix).match(str) 25 | return false unless etag 26 | true 27 | end 28 | end 29 | 30 | # Common routine 31 | define_method :get_namespace { |prefix| 32 | 33 | } 34 | 35 | # 36 | # QName 37 | # 38 | 39 | QName = Class.new { 40 | 41 | attr :name 42 | attr :namespace 43 | attr :prefix 44 | 45 | class << self 46 | def newq( unqualified_name ) 47 | name, prefix = *unqualified_name.split(':').reverse 48 | new( name, prefix ) 49 | end 50 | end 51 | 52 | define_method :initialize { |name, prefix| 53 | @name = name 54 | @prefix = prefix 55 | @namesapce = get_namespace( prefix ) 56 | end 57 | 58 | def to_s 59 | return "#{@prefix}:#{@name}" if @prefix 60 | @name 61 | end 62 | 63 | } 64 | 65 | 66 | # 67 | # XmlTag 68 | # 69 | class XmlTag 70 | attr :name # QName 71 | attr :attributes # Hash { QName => String, ... } 72 | 73 | class << self 74 | def newq( name, attributes={} ) 75 | new( QName.newq( name ), attributes ) 76 | end 77 | end 78 | 79 | def initialize( name, attributes={} ) 80 | @name = name.to_s 81 | @attributes = attributes 82 | end 83 | 84 | def to_s 85 | "#{@name}" 86 | end 87 | end 88 | 89 | 90 | class XmlList 91 | attr :list # Array [ Xml ; String , ... ] 92 | 93 | include Enumerable 94 | 95 | def initialize( list ) 96 | @list = list 97 | end 98 | 99 | def each ; @list.each { |e| yield e } ; end 100 | def size ; @list.size ; end 101 | def at(*args) ; @list.at(*args) ; end 102 | 103 | def children ; @list ; end 104 | 105 | def elements 106 | return [] if @list.size == 0 107 | if @list.size == 1 108 | l = @list[0] 109 | else 110 | l = @list 111 | end 112 | l.select { |e| Xml === e } 113 | end 114 | 115 | # The clever query 116 | def [](q) 117 | case q 118 | when Integer 119 | return list.at(q) 120 | when '*' 121 | return elements 122 | when '@*' 123 | return attributes 124 | when /^@/ 125 | return attributes.fetch(q) 126 | when /\:/ 127 | return elements.select { |e| e.name == q } 128 | else 129 | return elements.select { |e| e.local_name == q } 130 | end 131 | end 132 | 133 | # The clever updater/inserter 134 | def []=(q,v) 135 | r = self[q] 136 | case r.size 137 | when 0 138 | @list << v.to_s 139 | when 1 140 | case q 141 | when Integer 142 | list.store(q,v) 143 | when '*' 144 | raise "not implemented" 145 | when '@*' 146 | raise "not implemented" 147 | when /^@/ 148 | attributes[q] = v 149 | else 150 | r[0].replace( v ) 151 | end 152 | else 153 | # more than one match 154 | return nil #? 155 | end 156 | end 157 | 158 | #def method_missing( sym, *args ) 159 | # at(0).send( sym, *args ) 160 | #end 161 | end 162 | 163 | # 164 | # Xml 165 | # 166 | class Xml < XmlList 167 | attr :tag # XmlTag 168 | #attr :list # Array [ Xml ; String , ... ] 169 | 170 | class << self 171 | def newq( str_or_arr ) 172 | #a = ( String === str_or_arr ? parse(str_or_arr) : str_or_arr ) 173 | a = ( Array === str_or_arr ? str_or_arr : parse(str_or_arr) ) 174 | 175 | r = a.collect do |e| 176 | case e 177 | when Array 178 | if ::XmlCanonical === e.join('') 179 | tag = XmlTag.newq( name( e[0] ), attributes( e[0] ) ) 180 | list = ( e.size > 1 ? xml( e[1...-1] ) : nil ) 181 | Xml.new( tag, list ) 182 | else 183 | xml( e ) 184 | end 185 | else 186 | e # String === e 187 | end 188 | end 189 | 190 | if XmlCanonical === str_or_arr 191 | tag = XmlTag.newq( name( r[0] ), attributes( r[0] ) ) 192 | list = ( r.size > 1 ? xml( r[1...-1] ) : nil ) 193 | Xml.new( tag, list ) 194 | else 195 | XmlList.new( r ) 196 | end 197 | end 198 | 199 | def name( e0 ) 200 | START_TAG_RE.match(e0)[1] 201 | end 202 | 203 | def attributes( e0 ) 204 | s = START_TAG_RE.match( e0 )[2].strip 205 | a = s.split( %r{\s+|\=}x ).collect { |e| e.dequote } 206 | return Hash[ *a ] 207 | end 208 | end 209 | 210 | # 211 | def initialize( tag, list ) 212 | @tag = tag 213 | super( list ) 214 | end 215 | 216 | def name() @tag.to_s ; end 217 | def local_name() @tag.name ; end 218 | def attributes() @tag.attributes ; end 219 | 220 | def inspect 221 | "<#{tag}> ... " 222 | end 223 | 224 | def to_s 225 | attrs = attributes.collect{ |k,v| "#{k}=#{v}" }.join(' ') 226 | attrs = " #{attrs}" unless attrs.empty? 227 | 228 | lstr = @list.collect { |e| 229 | case e 230 | when Xml 231 | e.to_s 232 | when Array 233 | e.collect { |ee| ee.to_s }.join('') 234 | else 235 | e.to_s 236 | end 237 | }.join('') 238 | 239 | %{<#{tag.name}#{attrs}>#{lstr}} 240 | end 241 | 242 | def replace( x ) 243 | @list = [ x.to_s ] 244 | end 245 | 246 | end 247 | 248 | # 249 | # XmlInstruction 250 | # 251 | class XmlInstruction 252 | def initialize(name, data) 253 | end 254 | end 255 | 256 | # 257 | # XmlComment 258 | # 259 | class XmlComment 260 | def initialize(comment) 261 | end 262 | end 263 | 264 | 265 | # 266 | # R4X Parser 267 | # 268 | 269 | RETAG = %r{ \< (\/)? ([\w.:]+) (.*?) ([/])? \> }mix 270 | IDXEND = 1 271 | IDXNAME = 2 272 | IDXATTR = 3 273 | IDXUNIT = 4 274 | 275 | #module_function 276 | 277 | def parse(xmldata) 278 | if String === xmldata 279 | q = xmldata.strip.shatter(RETAG) 280 | else 281 | q = xmldata.dup 282 | end 283 | # setup 284 | build = [] 285 | current = build 286 | stack = [] 287 | # stack loop 288 | until q.empty? 289 | e = q.shift 290 | if md = RETAG.match( e ) 291 | if md[IDXEND] 292 | #close-tag 293 | current << e 294 | current = stack.pop 295 | elsif md[IDXUNIT] == '/' 296 | #unit-tag 297 | current << e 298 | else 299 | #open-tag 300 | stack << current 301 | current << [] 302 | current = current.last 303 | current << e 304 | end 305 | else 306 | current << e #if e != "\n" 307 | end 308 | end 309 | return build 310 | end 311 | 312 | end 313 | 314 | 315 | include R4X 316 | 317 | q = %{ 318 | 319 | 320 | 2004-12-18 321 | Santa 322 | 0.32 323 | 2.45 324 | 325 | 326 | 2003-12-18 327 | Bunny 328 | 0.32 329 | 1.20 330 | 331 | 332 | } 333 | 334 | x = xml( q ) 335 | 336 | puts 337 | puts x 338 | 339 | puts x['stamp'] 340 | puts 341 | puts x['stamp'][0]['issued'] 342 | puts 343 | x['stamp'][0]['issued'] = "2000-01-01" 344 | puts x['stamp'][0]['issued'] 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | # # 372 | # # XmlList 373 | # # 374 | # class XmlList 375 | # 376 | # class << self 377 | # alias_method :__new, :new 378 | # def new( xa ) 379 | # XmlList === xa ? xa : __new( xa ) 380 | # end 381 | # end 382 | # 383 | # def initialize( list ) 384 | # @list = list 385 | # end 386 | # 387 | # def [](q) 388 | # case @list.size 389 | # when 0 390 | # [] 391 | # when 1 392 | # @list.at(0)[q] 393 | # else 394 | # case q 395 | # when Integer 396 | # @list[q] 397 | # when String 398 | # XmlList.new( @list.collect { |e| e[q] } ) 399 | # end 400 | # end 401 | # end 402 | # 403 | # def []=( q, v ) 404 | # r = self[q] 405 | # p r 406 | # case r.size 407 | # when 1 408 | # p r 409 | # end 410 | # end 411 | # 412 | # def select( &blk ) 413 | # XmlList.new( @list.select( &blk ) ) 414 | # end 415 | # 416 | # def to_s 417 | # @list.collect { |e| e.to_s }.join("\n") 418 | # end 419 | # 420 | # def method_missing( meth, *args ) 421 | # @list.send( meth, *args ) 422 | # end 423 | # 424 | # end 425 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | . 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | --------------------------------------------------------------------------------