зеркало из https://github.com/github/ruby.git
Make rexml library to the bundle gems
[Feature #16485][ruby-core:96683]
This commit is contained in:
Родитель
012f297311
Коммит
c3ccf23d58
|
@ -239,10 +239,6 @@ Zachary Scott (zzak)
|
|||
aycabta
|
||||
https://github.com/ruby/reline
|
||||
https://rubygems.org/gems/reline
|
||||
[lib/rexml/*]
|
||||
Kouhei Sutou (kou)
|
||||
https://github.com/ruby/rexml
|
||||
https://rubygems.org/gems/rexml
|
||||
[lib/rss.rb, lib/rss/*]
|
||||
Kouhei Sutou (kou)
|
||||
https://github.com/ruby/rss
|
||||
|
@ -357,3 +353,5 @@ Zachary Scott (zzak)
|
|||
https://github.com/test-unit/test-unit
|
||||
[xmlrpc]
|
||||
https://github.com/ruby/xmlrpc
|
||||
[rexml]
|
||||
https://github.com/ruby/rexml
|
||||
|
|
|
@ -80,7 +80,6 @@ Prime:: Prime numbers and factorization library
|
|||
PStore:: Implements a file based persistence mechanism based on a Hash
|
||||
Racc:: A LALR(1) parser generator written in Ruby.
|
||||
RDoc:: Produces HTML and command-line documentation for Ruby
|
||||
REXML:: An XML toolkit for Ruby
|
||||
RSS:: Family of libraries that support various formats of XML "feeds"
|
||||
Singleton:: Implementation of the Singleton pattern for Ruby
|
||||
Timeout:: Auto-terminate potentially long-running operations in Ruby
|
||||
|
@ -119,3 +118,4 @@ PowerAssert:: Power Assert for Ruby.
|
|||
Rake:: Ruby build program with capabilities similar to make
|
||||
Test::Unit:: A compatibility layer for MiniTest
|
||||
XMLRPC:: Remote Procedure Call over HTTP support for Ruby
|
||||
REXML:: An XML toolkit for Ruby
|
||||
|
|
|
@ -4,3 +4,4 @@ power_assert 1.1.5 https://github.com/k-tsj/power_assert
|
|||
rake 13.0.1 https://github.com/ruby/rake
|
||||
test-unit 3.3.4 https://github.com/test-unit/test-unit
|
||||
xmlrpc 0.3.0 https://github.com/ruby/xmlrpc
|
||||
rexml 3.2.3 https://github.com/ruby/rexml
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
#vim:ts=2 sw=2 noexpandtab:
|
||||
require_relative 'child'
|
||||
require_relative 'source'
|
||||
|
||||
module REXML
|
||||
# This class needs:
|
||||
# * Documentation
|
||||
# * Work! Not all types of attlists are intelligently parsed, so we just
|
||||
# spew back out what we get in. This works, but it would be better if
|
||||
# we formatted the output ourselves.
|
||||
#
|
||||
# AttlistDecls provide *just* enough support to allow namespace
|
||||
# declarations. If you need some sort of generalized support, or have an
|
||||
# interesting idea about how to map the hideous, terrible design of DTD
|
||||
# AttlistDecls onto an intuitive Ruby interface, let me know. I'm desperate
|
||||
# for anything to make DTDs more palateable.
|
||||
class AttlistDecl < Child
|
||||
include Enumerable
|
||||
|
||||
# What is this? Got me.
|
||||
attr_reader :element_name
|
||||
|
||||
# Create an AttlistDecl, pulling the information from a Source. Notice
|
||||
# that this isn't very convenient; to create an AttlistDecl, you basically
|
||||
# have to format it yourself, and then have the initializer parse it.
|
||||
# Sorry, but for the foreseeable future, DTD support in REXML is pretty
|
||||
# weak on convenience. Have I mentioned how much I hate DTDs?
|
||||
def initialize(source)
|
||||
super()
|
||||
if (source.kind_of? Array)
|
||||
@element_name, @pairs, @contents = *source
|
||||
end
|
||||
end
|
||||
|
||||
# Access the attlist attribute/value pairs.
|
||||
# value = attlist_decl[ attribute_name ]
|
||||
def [](key)
|
||||
@pairs[key]
|
||||
end
|
||||
|
||||
# Whether an attlist declaration includes the given attribute definition
|
||||
# if attlist_decl.include? "xmlns:foobar"
|
||||
def include?(key)
|
||||
@pairs.keys.include? key
|
||||
end
|
||||
|
||||
# Iterate over the key/value pairs:
|
||||
# attlist_decl.each { |attribute_name, attribute_value| ... }
|
||||
def each(&block)
|
||||
@pairs.each(&block)
|
||||
end
|
||||
|
||||
# Write out exactly what we got in.
|
||||
def write out, indent=-1
|
||||
out << @contents
|
||||
end
|
||||
|
||||
def node_type
|
||||
:attlistdecl
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,205 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "namespace"
|
||||
require_relative 'text'
|
||||
|
||||
module REXML
|
||||
# Defines an Element Attribute; IE, a attribute=value pair, as in:
|
||||
# <element attribute="value"/>. Attributes can be in their own
|
||||
# namespaces. General users of REXML will not interact with the
|
||||
# Attribute class much.
|
||||
class Attribute
|
||||
include Node
|
||||
include Namespace
|
||||
|
||||
# The element to which this attribute belongs
|
||||
attr_reader :element
|
||||
# The normalized value of this attribute. That is, the attribute with
|
||||
# entities intact.
|
||||
attr_writer :normalized
|
||||
PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um
|
||||
|
||||
NEEDS_A_SECOND_CHECK = /(<|&((#{Entity::NAME});|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));)?)/um
|
||||
|
||||
# Constructor.
|
||||
# FIXME: The parser doesn't catch illegal characters in attributes
|
||||
#
|
||||
# first::
|
||||
# Either: an Attribute, which this new attribute will become a
|
||||
# clone of; or a String, which is the name of this attribute
|
||||
# second::
|
||||
# If +first+ is an Attribute, then this may be an Element, or nil.
|
||||
# If nil, then the Element parent of this attribute is the parent
|
||||
# of the +first+ Attribute. If the first argument is a String,
|
||||
# then this must also be a String, and is the content of the attribute.
|
||||
# If this is the content, it must be fully normalized (contain no
|
||||
# illegal characters).
|
||||
# parent::
|
||||
# Ignored unless +first+ is a String; otherwise, may be the Element
|
||||
# parent of this attribute, or nil.
|
||||
#
|
||||
#
|
||||
# Attribute.new( attribute_to_clone )
|
||||
# Attribute.new( attribute_to_clone, parent_element )
|
||||
# Attribute.new( "attr", "attr_value" )
|
||||
# Attribute.new( "attr", "attr_value", parent_element )
|
||||
def initialize( first, second=nil, parent=nil )
|
||||
@normalized = @unnormalized = @element = nil
|
||||
if first.kind_of? Attribute
|
||||
self.name = first.expanded_name
|
||||
@unnormalized = first.value
|
||||
if second.kind_of? Element
|
||||
@element = second
|
||||
else
|
||||
@element = first.element
|
||||
end
|
||||
elsif first.kind_of? String
|
||||
@element = parent
|
||||
self.name = first
|
||||
@normalized = second.to_s
|
||||
else
|
||||
raise "illegal argument #{first.class.name} to Attribute constructor"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the namespace of the attribute.
|
||||
#
|
||||
# e = Element.new( "elns:myelement" )
|
||||
# e.add_attribute( "nsa:a", "aval" )
|
||||
# e.add_attribute( "b", "bval" )
|
||||
# e.attributes.get_attribute( "a" ).prefix # -> "nsa"
|
||||
# e.attributes.get_attribute( "b" ).prefix # -> ""
|
||||
# a = Attribute.new( "x", "y" )
|
||||
# a.prefix # -> ""
|
||||
def prefix
|
||||
super
|
||||
end
|
||||
|
||||
# Returns the namespace URL, if defined, or nil otherwise
|
||||
#
|
||||
# e = Element.new("el")
|
||||
# e.add_namespace("ns", "http://url")
|
||||
# e.add_attribute("ns:a", "b")
|
||||
# e.add_attribute("nsx:a", "c")
|
||||
# e.attribute("ns:a").namespace # => "http://url"
|
||||
# e.attribute("nsx:a").namespace # => nil
|
||||
#
|
||||
# This method always returns "" for no namespace attribute. Because
|
||||
# the default namespace doesn't apply to attribute names.
|
||||
#
|
||||
# From https://www.w3.org/TR/xml-names/#uniqAttrs
|
||||
#
|
||||
# > the default namespace does not apply to attribute names
|
||||
#
|
||||
# e = REXML::Element.new("el")
|
||||
# e.add_namespace("", "http://example.com/")
|
||||
# e.namespace # => "http://example.com/"
|
||||
# e.add_attribute("a", "b")
|
||||
# e.attribute("a").namespace # => ""
|
||||
def namespace arg=nil
|
||||
arg = prefix if arg.nil?
|
||||
if arg == ""
|
||||
""
|
||||
else
|
||||
@element.namespace(arg)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if other is an Attribute and has the same name and value,
|
||||
# false otherwise.
|
||||
def ==( other )
|
||||
other.kind_of?(Attribute) and other.name==name and other.value==value
|
||||
end
|
||||
|
||||
# Creates (and returns) a hash from both the name and value
|
||||
def hash
|
||||
name.hash + value.hash
|
||||
end
|
||||
|
||||
# Returns this attribute out as XML source, expanding the name
|
||||
#
|
||||
# a = Attribute.new( "x", "y" )
|
||||
# a.to_string # -> "x='y'"
|
||||
# b = Attribute.new( "ns:x", "y" )
|
||||
# b.to_string # -> "ns:x='y'"
|
||||
def to_string
|
||||
if @element and @element.context and @element.context[:attribute_quote] == :quote
|
||||
%Q^#@expanded_name="#{to_s().gsub(/"/, '"')}"^
|
||||
else
|
||||
"#@expanded_name='#{to_s().gsub(/'/, ''')}'"
|
||||
end
|
||||
end
|
||||
|
||||
def doctype
|
||||
if @element
|
||||
doc = @element.document
|
||||
doc.doctype if doc
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the attribute value, with entities replaced
|
||||
def to_s
|
||||
return @normalized if @normalized
|
||||
|
||||
@normalized = Text::normalize( @unnormalized, doctype )
|
||||
@unnormalized = nil
|
||||
@normalized
|
||||
end
|
||||
|
||||
# Returns the UNNORMALIZED value of this attribute. That is, entities
|
||||
# have been expanded to their values
|
||||
def value
|
||||
return @unnormalized if @unnormalized
|
||||
@unnormalized = Text::unnormalize( @normalized, doctype )
|
||||
@normalized = nil
|
||||
@unnormalized
|
||||
end
|
||||
|
||||
# Returns a copy of this attribute
|
||||
def clone
|
||||
Attribute.new self
|
||||
end
|
||||
|
||||
# Sets the element of which this object is an attribute. Normally, this
|
||||
# is not directly called.
|
||||
#
|
||||
# Returns this attribute
|
||||
def element=( element )
|
||||
@element = element
|
||||
|
||||
if @normalized
|
||||
Text.check( @normalized, NEEDS_A_SECOND_CHECK, doctype )
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
# Removes this Attribute from the tree, and returns true if successful
|
||||
#
|
||||
# This method is usually not called directly.
|
||||
def remove
|
||||
@element.attributes.delete self.name unless @element.nil?
|
||||
end
|
||||
|
||||
# Writes this attribute (EG, puts 'key="value"' to the output)
|
||||
def write( output, indent=-1 )
|
||||
output << to_string
|
||||
end
|
||||
|
||||
def node_type
|
||||
:attribute
|
||||
end
|
||||
|
||||
def inspect
|
||||
rv = ""
|
||||
write( rv )
|
||||
rv
|
||||
end
|
||||
|
||||
def xpath
|
||||
path = @element.xpath
|
||||
path += "/@#{self.expanded_name}"
|
||||
return path
|
||||
end
|
||||
end
|
||||
end
|
||||
#vim:ts=2 sw=2 noexpandtab:
|
|
@ -1,68 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "text"
|
||||
|
||||
module REXML
|
||||
class CData < Text
|
||||
START = '<![CDATA['
|
||||
STOP = ']]>'
|
||||
ILLEGAL = /(\]\]>)/
|
||||
|
||||
# Constructor. CData is data between <![CDATA[ ... ]]>
|
||||
#
|
||||
# _Examples_
|
||||
# CData.new( source )
|
||||
# CData.new( "Here is some CDATA" )
|
||||
# CData.new( "Some unprocessed data", respect_whitespace_TF, parent_element )
|
||||
def initialize( first, whitespace=true, parent=nil )
|
||||
super( first, whitespace, parent, false, true, ILLEGAL )
|
||||
end
|
||||
|
||||
# Make a copy of this object
|
||||
#
|
||||
# _Examples_
|
||||
# c = CData.new( "Some text" )
|
||||
# d = c.clone
|
||||
# d.to_s # -> "Some text"
|
||||
def clone
|
||||
CData.new self
|
||||
end
|
||||
|
||||
# Returns the content of this CData object
|
||||
#
|
||||
# _Examples_
|
||||
# c = CData.new( "Some text" )
|
||||
# c.to_s # -> "Some text"
|
||||
def to_s
|
||||
@string
|
||||
end
|
||||
|
||||
def value
|
||||
@string
|
||||
end
|
||||
|
||||
# == DEPRECATED
|
||||
# See the rexml/formatters package
|
||||
#
|
||||
# Generates XML output of this object
|
||||
#
|
||||
# output::
|
||||
# Where to write the string. Defaults to $stdout
|
||||
# indent::
|
||||
# The amount to indent this node by
|
||||
# transitive::
|
||||
# Ignored
|
||||
# ie_hack::
|
||||
# Ignored
|
||||
#
|
||||
# _Examples_
|
||||
# c = CData.new( " Some text " )
|
||||
# c.write( $stdout ) #-> <![CDATA[ Some text ]]>
|
||||
def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
|
||||
Kernel.warn( "#{self.class.name}.write is deprecated", uplevel: 1)
|
||||
indent( output, indent )
|
||||
output << START
|
||||
output << @string
|
||||
output << STOP
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,97 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "node"
|
||||
|
||||
module REXML
|
||||
##
|
||||
# A Child object is something contained by a parent, and this class
|
||||
# contains methods to support that. Most user code will not use this
|
||||
# class directly.
|
||||
class Child
|
||||
include Node
|
||||
attr_reader :parent # The Parent of this object
|
||||
|
||||
# Constructor. Any inheritors of this class should call super to make
|
||||
# sure this method is called.
|
||||
# parent::
|
||||
# if supplied, the parent of this child will be set to the
|
||||
# supplied value, and self will be added to the parent
|
||||
def initialize( parent = nil )
|
||||
@parent = nil
|
||||
# Declare @parent, but don't define it. The next line sets the
|
||||
# parent.
|
||||
parent.add( self ) if parent
|
||||
end
|
||||
|
||||
# Replaces this object with another object. Basically, calls
|
||||
# Parent.replace_child
|
||||
#
|
||||
# Returns:: self
|
||||
def replace_with( child )
|
||||
@parent.replace_child( self, child )
|
||||
self
|
||||
end
|
||||
|
||||
# Removes this child from the parent.
|
||||
#
|
||||
# Returns:: self
|
||||
def remove
|
||||
unless @parent.nil?
|
||||
@parent.delete self
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Sets the parent of this child to the supplied argument.
|
||||
#
|
||||
# other::
|
||||
# Must be a Parent object. If this object is the same object as the
|
||||
# existing parent of this child, no action is taken. Otherwise, this
|
||||
# child is removed from the current parent (if one exists), and is added
|
||||
# to the new parent.
|
||||
# Returns:: The parent added
|
||||
def parent=( other )
|
||||
return @parent if @parent == other
|
||||
@parent.delete self if defined? @parent and @parent
|
||||
@parent = other
|
||||
end
|
||||
|
||||
alias :next_sibling :next_sibling_node
|
||||
alias :previous_sibling :previous_sibling_node
|
||||
|
||||
# Sets the next sibling of this child. This can be used to insert a child
|
||||
# after some other child.
|
||||
# a = Element.new("a")
|
||||
# b = a.add_element("b")
|
||||
# c = Element.new("c")
|
||||
# b.next_sibling = c
|
||||
# # => <a><b/><c/></a>
|
||||
def next_sibling=( other )
|
||||
parent.insert_after self, other
|
||||
end
|
||||
|
||||
# Sets the previous sibling of this child. This can be used to insert a
|
||||
# child before some other child.
|
||||
# a = Element.new("a")
|
||||
# b = a.add_element("b")
|
||||
# c = Element.new("c")
|
||||
# b.previous_sibling = c
|
||||
# # => <a><b/><c/></a>
|
||||
def previous_sibling=(other)
|
||||
parent.insert_before self, other
|
||||
end
|
||||
|
||||
# Returns:: the document this child belongs to, or nil if this child
|
||||
# belongs to no document
|
||||
def document
|
||||
return parent.document unless parent.nil?
|
||||
nil
|
||||
end
|
||||
|
||||
# This doesn't yet handle encodings
|
||||
def bytes
|
||||
document.encoding
|
||||
|
||||
to_s
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,80 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "child"
|
||||
|
||||
module REXML
|
||||
##
|
||||
# Represents an XML comment; that is, text between \<!-- ... -->
|
||||
class Comment < Child
|
||||
include Comparable
|
||||
START = "<!--"
|
||||
STOP = "-->"
|
||||
|
||||
# The content text
|
||||
|
||||
attr_accessor :string
|
||||
|
||||
##
|
||||
# Constructor. The first argument can be one of three types:
|
||||
# @param first If String, the contents of this comment are set to the
|
||||
# argument. If Comment, the argument is duplicated. If
|
||||
# Source, the argument is scanned for a comment.
|
||||
# @param second If the first argument is a Source, this argument
|
||||
# should be nil, not supplied, or a Parent to be set as the parent
|
||||
# of this object
|
||||
def initialize( first, second = nil )
|
||||
super(second)
|
||||
if first.kind_of? String
|
||||
@string = first
|
||||
elsif first.kind_of? Comment
|
||||
@string = first.string
|
||||
end
|
||||
end
|
||||
|
||||
def clone
|
||||
Comment.new self
|
||||
end
|
||||
|
||||
# == DEPRECATED
|
||||
# See REXML::Formatters
|
||||
#
|
||||
# output::
|
||||
# Where to write the string
|
||||
# indent::
|
||||
# An integer. If -1, no indenting will be used; otherwise, the
|
||||
# indentation will be this number of spaces, and children will be
|
||||
# indented an additional amount.
|
||||
# transitive::
|
||||
# Ignored by this class. The contents of comments are never modified.
|
||||
# ie_hack::
|
||||
# Needed for conformity to the child API, but not used by this class.
|
||||
def write( output, indent=-1, transitive=false, ie_hack=false )
|
||||
Kernel.warn("Comment.write is deprecated. See REXML::Formatters", uplevel: 1)
|
||||
indent( output, indent )
|
||||
output << START
|
||||
output << @string
|
||||
output << STOP
|
||||
end
|
||||
|
||||
alias :to_s :string
|
||||
|
||||
##
|
||||
# Compares this Comment to another; the contents of the comment are used
|
||||
# in the comparison.
|
||||
def <=>(other)
|
||||
other.to_s <=> @string
|
||||
end
|
||||
|
||||
##
|
||||
# Compares this Comment to another; the contents of the comment are used
|
||||
# in the comparison.
|
||||
def ==( other )
|
||||
other.kind_of? Comment and
|
||||
(other <=> self) == 0
|
||||
end
|
||||
|
||||
def node_type
|
||||
:comment
|
||||
end
|
||||
end
|
||||
end
|
||||
#vim:ts=2 sw=2 noexpandtab:
|
|
@ -1,287 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "parent"
|
||||
require_relative "parseexception"
|
||||
require_relative "namespace"
|
||||
require_relative 'entity'
|
||||
require_relative 'attlistdecl'
|
||||
require_relative 'xmltokens'
|
||||
|
||||
module REXML
|
||||
# Represents an XML DOCTYPE declaration; that is, the contents of <!DOCTYPE
|
||||
# ... >. DOCTYPES can be used to declare the DTD of a document, as well as
|
||||
# being used to declare entities used in the document.
|
||||
class DocType < Parent
|
||||
include XMLTokens
|
||||
START = "<!DOCTYPE"
|
||||
STOP = ">"
|
||||
SYSTEM = "SYSTEM"
|
||||
PUBLIC = "PUBLIC"
|
||||
DEFAULT_ENTITIES = {
|
||||
'gt'=>EntityConst::GT,
|
||||
'lt'=>EntityConst::LT,
|
||||
'quot'=>EntityConst::QUOT,
|
||||
"apos"=>EntityConst::APOS
|
||||
}
|
||||
|
||||
# name is the name of the doctype
|
||||
# external_id is the referenced DTD, if given
|
||||
attr_reader :name, :external_id, :entities, :namespaces
|
||||
|
||||
# Constructor
|
||||
#
|
||||
# dt = DocType.new( 'foo', '-//I/Hate/External/IDs' )
|
||||
# # <!DOCTYPE foo '-//I/Hate/External/IDs'>
|
||||
# dt = DocType.new( doctype_to_clone )
|
||||
# # Incomplete. Shallow clone of doctype
|
||||
#
|
||||
# +Note+ that the constructor:
|
||||
#
|
||||
# Doctype.new( Source.new( "<!DOCTYPE foo 'bar'>" ) )
|
||||
#
|
||||
# is _deprecated_. Do not use it. It will probably disappear.
|
||||
def initialize( first, parent=nil )
|
||||
@entities = DEFAULT_ENTITIES
|
||||
@long_name = @uri = nil
|
||||
if first.kind_of? String
|
||||
super()
|
||||
@name = first
|
||||
@external_id = parent
|
||||
elsif first.kind_of? DocType
|
||||
super( parent )
|
||||
@name = first.name
|
||||
@external_id = first.external_id
|
||||
elsif first.kind_of? Array
|
||||
super( parent )
|
||||
@name = first[0]
|
||||
@external_id = first[1]
|
||||
@long_name = first[2]
|
||||
@uri = first[3]
|
||||
elsif first.kind_of? Source
|
||||
super( parent )
|
||||
parser = Parsers::BaseParser.new( first )
|
||||
event = parser.pull
|
||||
if event[0] == :start_doctype
|
||||
@name, @external_id, @long_name, @uri, = event[1..-1]
|
||||
end
|
||||
else
|
||||
super()
|
||||
end
|
||||
end
|
||||
|
||||
def node_type
|
||||
:doctype
|
||||
end
|
||||
|
||||
def attributes_of element
|
||||
rv = []
|
||||
each do |child|
|
||||
child.each do |key,val|
|
||||
rv << Attribute.new(key,val)
|
||||
end if child.kind_of? AttlistDecl and child.element_name == element
|
||||
end
|
||||
rv
|
||||
end
|
||||
|
||||
def attribute_of element, attribute
|
||||
att_decl = find do |child|
|
||||
child.kind_of? AttlistDecl and
|
||||
child.element_name == element and
|
||||
child.include? attribute
|
||||
end
|
||||
return nil unless att_decl
|
||||
att_decl[attribute]
|
||||
end
|
||||
|
||||
def clone
|
||||
DocType.new self
|
||||
end
|
||||
|
||||
# output::
|
||||
# Where to write the string
|
||||
# indent::
|
||||
# An integer. If -1, no indentation will be used; otherwise, the
|
||||
# indentation will be this number of spaces, and children will be
|
||||
# indented an additional amount.
|
||||
# transitive::
|
||||
# Ignored
|
||||
# ie_hack::
|
||||
# Ignored
|
||||
def write( output, indent=0, transitive=false, ie_hack=false )
|
||||
f = REXML::Formatters::Default.new
|
||||
c = context
|
||||
if c and c[:prologue_quote] == :apostrophe
|
||||
quote = "'"
|
||||
else
|
||||
quote = "\""
|
||||
end
|
||||
indent( output, indent )
|
||||
output << START
|
||||
output << ' '
|
||||
output << @name
|
||||
output << " #{@external_id}" if @external_id
|
||||
output << " #{quote}#{@long_name}#{quote}" if @long_name
|
||||
output << " #{quote}#{@uri}#{quote}" if @uri
|
||||
unless @children.empty?
|
||||
output << ' ['
|
||||
@children.each { |child|
|
||||
output << "\n"
|
||||
f.write( child, output )
|
||||
}
|
||||
output << "\n]"
|
||||
end
|
||||
output << STOP
|
||||
end
|
||||
|
||||
def context
|
||||
if @parent
|
||||
@parent.context
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def entity( name )
|
||||
@entities[name].unnormalized if @entities[name]
|
||||
end
|
||||
|
||||
def add child
|
||||
super(child)
|
||||
@entities = DEFAULT_ENTITIES.clone if @entities == DEFAULT_ENTITIES
|
||||
@entities[ child.name ] = child if child.kind_of? Entity
|
||||
end
|
||||
|
||||
# This method retrieves the public identifier identifying the document's
|
||||
# DTD.
|
||||
#
|
||||
# Method contributed by Henrik Martensson
|
||||
def public
|
||||
case @external_id
|
||||
when "SYSTEM"
|
||||
nil
|
||||
when "PUBLIC"
|
||||
strip_quotes(@long_name)
|
||||
end
|
||||
end
|
||||
|
||||
# This method retrieves the system identifier identifying the document's DTD
|
||||
#
|
||||
# Method contributed by Henrik Martensson
|
||||
def system
|
||||
case @external_id
|
||||
when "SYSTEM"
|
||||
strip_quotes(@long_name)
|
||||
when "PUBLIC"
|
||||
@uri.kind_of?(String) ? strip_quotes(@uri) : nil
|
||||
end
|
||||
end
|
||||
|
||||
# This method returns a list of notations that have been declared in the
|
||||
# _internal_ DTD subset. Notations in the external DTD subset are not
|
||||
# listed.
|
||||
#
|
||||
# Method contributed by Henrik Martensson
|
||||
def notations
|
||||
children().select {|node| node.kind_of?(REXML::NotationDecl)}
|
||||
end
|
||||
|
||||
# Retrieves a named notation. Only notations declared in the internal
|
||||
# DTD subset can be retrieved.
|
||||
#
|
||||
# Method contributed by Henrik Martensson
|
||||
def notation(name)
|
||||
notations.find { |notation_decl|
|
||||
notation_decl.name == name
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Method contributed by Henrik Martensson
|
||||
def strip_quotes(quoted_string)
|
||||
quoted_string =~ /^[\'\"].*[\'\"]$/ ?
|
||||
quoted_string[1, quoted_string.length-2] :
|
||||
quoted_string
|
||||
end
|
||||
end
|
||||
|
||||
# We don't really handle any of these since we're not a validating
|
||||
# parser, so we can be pretty dumb about them. All we need to be able
|
||||
# to do is spew them back out on a write()
|
||||
|
||||
# This is an abstract class. You never use this directly; it serves as a
|
||||
# parent class for the specific declarations.
|
||||
class Declaration < Child
|
||||
def initialize src
|
||||
super()
|
||||
@string = src
|
||||
end
|
||||
|
||||
def to_s
|
||||
@string+'>'
|
||||
end
|
||||
|
||||
# == DEPRECATED
|
||||
# See REXML::Formatters
|
||||
#
|
||||
def write( output, indent )
|
||||
output << to_s
|
||||
end
|
||||
end
|
||||
|
||||
public
|
||||
class ElementDecl < Declaration
|
||||
def initialize( src )
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
class ExternalEntity < Child
|
||||
def initialize( src )
|
||||
super()
|
||||
@entity = src
|
||||
end
|
||||
def to_s
|
||||
@entity
|
||||
end
|
||||
def write( output, indent )
|
||||
output << @entity
|
||||
end
|
||||
end
|
||||
|
||||
class NotationDecl < Child
|
||||
attr_accessor :public, :system
|
||||
def initialize name, middle, pub, sys
|
||||
super(nil)
|
||||
@name = name
|
||||
@middle = middle
|
||||
@public = pub
|
||||
@system = sys
|
||||
end
|
||||
|
||||
def to_s
|
||||
c = nil
|
||||
c = parent.context if parent
|
||||
if c and c[:prologue_quote] == :apostrophe
|
||||
quote = "'"
|
||||
else
|
||||
quote = "\""
|
||||
end
|
||||
notation = "<!NOTATION #{@name} #{@middle}"
|
||||
notation << " #{quote}#{@public}#{quote}" if @public
|
||||
notation << " #{quote}#{@system}#{quote}" if @system
|
||||
notation << ">"
|
||||
notation
|
||||
end
|
||||
|
||||
def write( output, indent=-1 )
|
||||
output << to_s
|
||||
end
|
||||
|
||||
# This method retrieves the name of the notation.
|
||||
#
|
||||
# Method contributed by Henrik Martensson
|
||||
def name
|
||||
@name
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,291 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "security"
|
||||
require_relative "element"
|
||||
require_relative "xmldecl"
|
||||
require_relative "source"
|
||||
require_relative "comment"
|
||||
require_relative "doctype"
|
||||
require_relative "instruction"
|
||||
require_relative "rexml"
|
||||
require_relative "parseexception"
|
||||
require_relative "output"
|
||||
require_relative "parsers/baseparser"
|
||||
require_relative "parsers/streamparser"
|
||||
require_relative "parsers/treeparser"
|
||||
|
||||
module REXML
|
||||
# Represents a full XML document, including PIs, a doctype, etc. A
|
||||
# Document has a single child that can be accessed by root().
|
||||
# Note that if you want to have an XML declaration written for a document
|
||||
# you create, you must add one; REXML documents do not write a default
|
||||
# declaration for you. See |DECLARATION| and |write|.
|
||||
class Document < Element
|
||||
# A convenient default XML declaration. If you want an XML declaration,
|
||||
# the easiest way to add one is mydoc << Document::DECLARATION
|
||||
# +DEPRECATED+
|
||||
# Use: mydoc << XMLDecl.default
|
||||
DECLARATION = XMLDecl.default
|
||||
|
||||
# Constructor
|
||||
# @param source if supplied, must be a Document, String, or IO.
|
||||
# Documents have their context and Element attributes cloned.
|
||||
# Strings are expected to be valid XML documents. IOs are expected
|
||||
# to be sources of valid XML documents.
|
||||
# @param context if supplied, contains the context of the document;
|
||||
# this should be a Hash.
|
||||
def initialize( source = nil, context = {} )
|
||||
@entity_expansion_count = 0
|
||||
super()
|
||||
@context = context
|
||||
return if source.nil?
|
||||
if source.kind_of? Document
|
||||
@context = source.context
|
||||
super source
|
||||
else
|
||||
build( source )
|
||||
end
|
||||
end
|
||||
|
||||
def node_type
|
||||
:document
|
||||
end
|
||||
|
||||
# Should be obvious
|
||||
def clone
|
||||
Document.new self
|
||||
end
|
||||
|
||||
# According to the XML spec, a root node has no expanded name
|
||||
def expanded_name
|
||||
''
|
||||
#d = doc_type
|
||||
#d ? d.name : "UNDEFINED"
|
||||
end
|
||||
|
||||
alias :name :expanded_name
|
||||
|
||||
# We override this, because XMLDecls and DocTypes must go at the start
|
||||
# of the document
|
||||
def add( child )
|
||||
if child.kind_of? XMLDecl
|
||||
if @children[0].kind_of? XMLDecl
|
||||
@children[0] = child
|
||||
else
|
||||
@children.unshift child
|
||||
end
|
||||
child.parent = self
|
||||
elsif child.kind_of? DocType
|
||||
# Find first Element or DocType node and insert the decl right
|
||||
# before it. If there is no such node, just insert the child at the
|
||||
# end. If there is a child and it is an DocType, then replace it.
|
||||
insert_before_index = @children.find_index { |x|
|
||||
x.kind_of?(Element) || x.kind_of?(DocType)
|
||||
}
|
||||
if insert_before_index # Not null = not end of list
|
||||
if @children[ insert_before_index ].kind_of? DocType
|
||||
@children[ insert_before_index ] = child
|
||||
else
|
||||
@children[ insert_before_index-1, 0 ] = child
|
||||
end
|
||||
else # Insert at end of list
|
||||
@children << child
|
||||
end
|
||||
child.parent = self
|
||||
else
|
||||
rv = super
|
||||
raise "attempted adding second root element to document" if @elements.size > 1
|
||||
rv
|
||||
end
|
||||
end
|
||||
alias :<< :add
|
||||
|
||||
def add_element(arg=nil, arg2=nil)
|
||||
rv = super
|
||||
raise "attempted adding second root element to document" if @elements.size > 1
|
||||
rv
|
||||
end
|
||||
|
||||
# @return the root Element of the document, or nil if this document
|
||||
# has no children.
|
||||
def root
|
||||
elements[1]
|
||||
#self
|
||||
#@children.find { |item| item.kind_of? Element }
|
||||
end
|
||||
|
||||
# @return the DocType child of the document, if one exists,
|
||||
# and nil otherwise.
|
||||
def doctype
|
||||
@children.find { |item| item.kind_of? DocType }
|
||||
end
|
||||
|
||||
# @return the XMLDecl of this document; if no XMLDecl has been
|
||||
# set, the default declaration is returned.
|
||||
def xml_decl
|
||||
rv = @children[0]
|
||||
return rv if rv.kind_of? XMLDecl
|
||||
@children.unshift(XMLDecl.default)[0]
|
||||
end
|
||||
|
||||
# @return the XMLDecl version of this document as a String.
|
||||
# If no XMLDecl has been set, returns the default version.
|
||||
def version
|
||||
xml_decl().version
|
||||
end
|
||||
|
||||
# @return the XMLDecl encoding of this document as an
|
||||
# Encoding object.
|
||||
# If no XMLDecl has been set, returns the default encoding.
|
||||
def encoding
|
||||
xml_decl().encoding
|
||||
end
|
||||
|
||||
# @return the XMLDecl standalone value of this document as a String.
|
||||
# If no XMLDecl has been set, returns the default setting.
|
||||
def stand_alone?
|
||||
xml_decl().stand_alone?
|
||||
end
|
||||
|
||||
# :call-seq:
|
||||
# doc.write(output=$stdout, indent=-1, transtive=false, ie_hack=false, encoding=nil)
|
||||
# doc.write(options={:output => $stdout, :indent => -1, :transtive => false, :ie_hack => false, :encoding => nil})
|
||||
#
|
||||
# Write the XML tree out, optionally with indent. This writes out the
|
||||
# entire XML document, including XML declarations, doctype declarations,
|
||||
# and processing instructions (if any are given).
|
||||
#
|
||||
# A controversial point is whether Document should always write the XML
|
||||
# declaration (<?xml version='1.0'?>) whether or not one is given by the
|
||||
# user (or source document). REXML does not write one if one was not
|
||||
# specified, because it adds unnecessary bandwidth to applications such
|
||||
# as XML-RPC.
|
||||
#
|
||||
# Accept Nth argument style and options Hash style as argument.
|
||||
# The recommended style is options Hash style for one or more
|
||||
# arguments case.
|
||||
#
|
||||
# _Examples_
|
||||
# Document.new("<a><b/></a>").write
|
||||
#
|
||||
# output = ""
|
||||
# Document.new("<a><b/></a>").write(output)
|
||||
#
|
||||
# output = ""
|
||||
# Document.new("<a><b/></a>").write(:output => output, :indent => 2)
|
||||
#
|
||||
# See also the classes in the rexml/formatters package for the proper way
|
||||
# to change the default formatting of XML output.
|
||||
#
|
||||
# _Examples_
|
||||
#
|
||||
# output = ""
|
||||
# tr = Transitive.new
|
||||
# tr.write(Document.new("<a><b/></a>"), output)
|
||||
#
|
||||
# output::
|
||||
# output an object which supports '<< string'; this is where the
|
||||
# document will be written.
|
||||
# indent::
|
||||
# An integer. If -1, no indenting will be used; otherwise, the
|
||||
# indentation will be twice this number of spaces, and children will be
|
||||
# indented an additional amount. For a value of 3, every item will be
|
||||
# indented 3 more levels, or 6 more spaces (2 * 3). Defaults to -1
|
||||
# transitive::
|
||||
# If transitive is true and indent is >= 0, then the output will be
|
||||
# pretty-printed in such a way that the added whitespace does not affect
|
||||
# the absolute *value* of the document -- that is, it leaves the value
|
||||
# and number of Text nodes in the document unchanged.
|
||||
# ie_hack::
|
||||
# This hack inserts a space before the /> on empty tags to address
|
||||
# a limitation of Internet Explorer. Defaults to false
|
||||
# encoding::
|
||||
# Encoding name as String. Change output encoding to specified encoding
|
||||
# instead of encoding in XML declaration.
|
||||
# Defaults to nil. It means encoding in XML declaration is used.
|
||||
def write(*arguments)
|
||||
if arguments.size == 1 and arguments[0].class == Hash
|
||||
options = arguments[0]
|
||||
|
||||
output = options[:output]
|
||||
indent = options[:indent]
|
||||
transitive = options[:transitive]
|
||||
ie_hack = options[:ie_hack]
|
||||
encoding = options[:encoding]
|
||||
else
|
||||
output, indent, transitive, ie_hack, encoding, = *arguments
|
||||
end
|
||||
|
||||
output ||= $stdout
|
||||
indent ||= -1
|
||||
transitive = false if transitive.nil?
|
||||
ie_hack = false if ie_hack.nil?
|
||||
encoding ||= xml_decl.encoding
|
||||
|
||||
if encoding != 'UTF-8' && !output.kind_of?(Output)
|
||||
output = Output.new( output, encoding )
|
||||
end
|
||||
formatter = if indent > -1
|
||||
if transitive
|
||||
require_relative "formatters/transitive"
|
||||
REXML::Formatters::Transitive.new( indent, ie_hack )
|
||||
else
|
||||
REXML::Formatters::Pretty.new( indent, ie_hack )
|
||||
end
|
||||
else
|
||||
REXML::Formatters::Default.new( ie_hack )
|
||||
end
|
||||
formatter.write( self, output )
|
||||
end
|
||||
|
||||
|
||||
def Document::parse_stream( source, listener )
|
||||
Parsers::StreamParser.new( source, listener ).parse
|
||||
end
|
||||
|
||||
# Set the entity expansion limit. By default the limit is set to 10000.
|
||||
#
|
||||
# Deprecated. Use REXML::Security.entity_expansion_limit= instead.
|
||||
def Document::entity_expansion_limit=( val )
|
||||
Security.entity_expansion_limit = val
|
||||
end
|
||||
|
||||
# Get the entity expansion limit. By default the limit is set to 10000.
|
||||
#
|
||||
# Deprecated. Use REXML::Security.entity_expansion_limit= instead.
|
||||
def Document::entity_expansion_limit
|
||||
return Security.entity_expansion_limit
|
||||
end
|
||||
|
||||
# Set the entity expansion limit. By default the limit is set to 10240.
|
||||
#
|
||||
# Deprecated. Use REXML::Security.entity_expansion_text_limit= instead.
|
||||
def Document::entity_expansion_text_limit=( val )
|
||||
Security.entity_expansion_text_limit = val
|
||||
end
|
||||
|
||||
# Get the entity expansion limit. By default the limit is set to 10240.
|
||||
#
|
||||
# Deprecated. Use REXML::Security.entity_expansion_text_limit instead.
|
||||
def Document::entity_expansion_text_limit
|
||||
return Security.entity_expansion_text_limit
|
||||
end
|
||||
|
||||
attr_reader :entity_expansion_count
|
||||
|
||||
def record_entity_expansion
|
||||
@entity_expansion_count += 1
|
||||
if @entity_expansion_count > Security.entity_expansion_limit
|
||||
raise "number of entity expansions exceeded, processing aborted."
|
||||
end
|
||||
end
|
||||
|
||||
def document
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
def build( source )
|
||||
Parsers::TreeParser.new( source, self ).parse
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "../child"
|
||||
module REXML
|
||||
module DTD
|
||||
class AttlistDecl < Child
|
||||
START = "<!ATTLIST"
|
||||
START_RE = /^\s*#{START}/um
|
||||
PATTERN_RE = /\s*(#{START}.*?>)/um
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,47 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "elementdecl"
|
||||
require_relative "entitydecl"
|
||||
require_relative "../comment"
|
||||
require_relative "notationdecl"
|
||||
require_relative "attlistdecl"
|
||||
require_relative "../parent"
|
||||
|
||||
module REXML
|
||||
module DTD
|
||||
class Parser
|
||||
def Parser.parse( input )
|
||||
case input
|
||||
when String
|
||||
parse_helper input
|
||||
when File
|
||||
parse_helper input.read
|
||||
end
|
||||
end
|
||||
|
||||
# Takes a String and parses it out
|
||||
def Parser.parse_helper( input )
|
||||
contents = Parent.new
|
||||
while input.size > 0
|
||||
case input
|
||||
when ElementDecl.PATTERN_RE
|
||||
match = $&
|
||||
contents << ElementDecl.new( match )
|
||||
when AttlistDecl.PATTERN_RE
|
||||
matchdata = $~
|
||||
contents << AttlistDecl.new( matchdata )
|
||||
when EntityDecl.PATTERN_RE
|
||||
matchdata = $~
|
||||
contents << EntityDecl.new( matchdata )
|
||||
when Comment.PATTERN_RE
|
||||
matchdata = $~
|
||||
contents << Comment.new( matchdata )
|
||||
when NotationDecl.PATTERN_RE
|
||||
matchdata = $~
|
||||
contents << NotationDecl.new( matchdata )
|
||||
end
|
||||
end
|
||||
contents
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,18 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "../child"
|
||||
module REXML
|
||||
module DTD
|
||||
class ElementDecl < Child
|
||||
START = "<!ELEMENT"
|
||||
START_RE = /^\s*#{START}/um
|
||||
# PATTERN_RE = /^\s*(#{START}.*?)>/um
|
||||
PATTERN_RE = /^\s*#{START}\s+((?:[:\w][-\.\w]*:)?[-!\*\.\w]*)(.*?)>/
|
||||
#\s*((((["']).*?\5)|[^\/'">]*)*?)(\/)?>/um, true)
|
||||
|
||||
def initialize match
|
||||
@name = match[1]
|
||||
@rest = match[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "../child"
|
||||
module REXML
|
||||
module DTD
|
||||
class EntityDecl < Child
|
||||
START = "<!ENTITY"
|
||||
START_RE = /^\s*#{START}/um
|
||||
PUBLIC = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+PUBLIC\s+((["']).*?\3)\s+((["']).*?\5)\s*>/um
|
||||
SYSTEM = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+SYSTEM\s+((["']).*?\3)(?:\s+NDATA\s+\w+)?\s*>/um
|
||||
PLAIN = /^\s*#{START}\s+(\w+)\s+((["']).*?\3)\s*>/um
|
||||
PERCENT = /^\s*#{START}\s+%\s+(\w+)\s+((["']).*?\3)\s*>/um
|
||||
# <!ENTITY name SYSTEM "...">
|
||||
# <!ENTITY name "...">
|
||||
def initialize src
|
||||
super()
|
||||
md = nil
|
||||
if src.match( PUBLIC )
|
||||
md = src.match( PUBLIC, true )
|
||||
@middle = "PUBLIC"
|
||||
@content = "#{md[2]} #{md[4]}"
|
||||
elsif src.match( SYSTEM )
|
||||
md = src.match( SYSTEM, true )
|
||||
@middle = "SYSTEM"
|
||||
@content = md[2]
|
||||
elsif src.match( PLAIN )
|
||||
md = src.match( PLAIN, true )
|
||||
@middle = ""
|
||||
@content = md[2]
|
||||
elsif src.match( PERCENT )
|
||||
md = src.match( PERCENT, true )
|
||||
@middle = ""
|
||||
@content = md[2]
|
||||
end
|
||||
raise ParseException.new("failed Entity match", src) if md.nil?
|
||||
@name = md[1]
|
||||
end
|
||||
|
||||
def to_s
|
||||
rv = "<!ENTITY #@name "
|
||||
rv << "#@middle " if @middle.size > 0
|
||||
rv << @content
|
||||
rv
|
||||
end
|
||||
|
||||
def write( output, indent )
|
||||
indent( output, indent )
|
||||
output << to_s
|
||||
end
|
||||
|
||||
def EntityDecl.parse_source source, listener
|
||||
md = source.match( PATTERN_RE, true )
|
||||
thing = md[0].squeeze(" \t\n\r")
|
||||
listener.send inspect.downcase, thing
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,40 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "../child"
|
||||
module REXML
|
||||
module DTD
|
||||
class NotationDecl < Child
|
||||
START = "<!NOTATION"
|
||||
START_RE = /^\s*#{START}/um
|
||||
PUBLIC = /^\s*#{START}\s+(\w[\w-]*)\s+(PUBLIC)\s+((["']).*?\4)\s*>/um
|
||||
SYSTEM = /^\s*#{START}\s+(\w[\w-]*)\s+(SYSTEM)\s+((["']).*?\4)\s*>/um
|
||||
def initialize src
|
||||
super()
|
||||
if src.match( PUBLIC )
|
||||
md = src.match( PUBLIC, true )
|
||||
elsif src.match( SYSTEM )
|
||||
md = src.match( SYSTEM, true )
|
||||
else
|
||||
raise ParseException.new( "error parsing notation: no matching pattern", src )
|
||||
end
|
||||
@name = md[1]
|
||||
@middle = md[2]
|
||||
@rest = md[3]
|
||||
end
|
||||
|
||||
def to_s
|
||||
"<!NOTATION #@name #@middle #@rest>"
|
||||
end
|
||||
|
||||
def write( output, indent )
|
||||
indent( output, indent )
|
||||
output << to_s
|
||||
end
|
||||
|
||||
def NotationDecl.parse_source source, listener
|
||||
md = source.match( PATTERN_RE, true )
|
||||
thing = md[0].squeeze(" \t\n\r")
|
||||
listener.send inspect.downcase, thing
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
1269
lib/rexml/element.rb
1269
lib/rexml/element.rb
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,51 +0,0 @@
|
|||
# coding: US-ASCII
|
||||
# frozen_string_literal: false
|
||||
module REXML
|
||||
module Encoding
|
||||
# ID ---> Encoding name
|
||||
attr_reader :encoding
|
||||
def encoding=(encoding)
|
||||
encoding = encoding.name if encoding.is_a?(Encoding)
|
||||
if encoding.is_a?(String)
|
||||
original_encoding = encoding
|
||||
encoding = find_encoding(encoding)
|
||||
unless encoding
|
||||
raise ArgumentError, "Bad encoding name #{original_encoding}"
|
||||
end
|
||||
end
|
||||
return false if defined?(@encoding) and encoding == @encoding
|
||||
if encoding
|
||||
@encoding = encoding.upcase
|
||||
else
|
||||
@encoding = 'UTF-8'
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def encode(string)
|
||||
string.encode(@encoding)
|
||||
end
|
||||
|
||||
def decode(string)
|
||||
string.encode(::Encoding::UTF_8, @encoding)
|
||||
end
|
||||
|
||||
private
|
||||
def find_encoding(name)
|
||||
case name
|
||||
when /\Ashift-jis\z/i
|
||||
return "SHIFT_JIS"
|
||||
when /\ACP-(\d+)\z/
|
||||
name = "CP#{$1}"
|
||||
when /\AUTF-8\z/i
|
||||
return name
|
||||
end
|
||||
begin
|
||||
::Encoding::Converter.search_convpath(name, 'UTF-8')
|
||||
rescue ::Encoding::ConverterNotFoundError
|
||||
return nil
|
||||
end
|
||||
name
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,171 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'child'
|
||||
require_relative 'source'
|
||||
require_relative 'xmltokens'
|
||||
|
||||
module REXML
|
||||
class Entity < Child
|
||||
include XMLTokens
|
||||
PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#"
|
||||
SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))}
|
||||
PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')}
|
||||
EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))"
|
||||
NDATADECL = "\\s+NDATA\\s+#{NAME}"
|
||||
PEREFERENCE = "%#{NAME};"
|
||||
ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))}
|
||||
PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})"
|
||||
ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))"
|
||||
PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
|
||||
GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
|
||||
ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
|
||||
|
||||
attr_reader :name, :external, :ref, :ndata, :pubid
|
||||
|
||||
# Create a new entity. Simple entities can be constructed by passing a
|
||||
# name, value to the constructor; this creates a generic, plain entity
|
||||
# reference. For anything more complicated, you have to pass a Source to
|
||||
# the constructor with the entity definition, or use the accessor methods.
|
||||
# +WARNING+: There is no validation of entity state except when the entity
|
||||
# is read from a stream. If you start poking around with the accessors,
|
||||
# you can easily create a non-conformant Entity.
|
||||
#
|
||||
# e = Entity.new( 'amp', '&' )
|
||||
def initialize stream, value=nil, parent=nil, reference=false
|
||||
super(parent)
|
||||
@ndata = @pubid = @value = @external = nil
|
||||
if stream.kind_of? Array
|
||||
@name = stream[1]
|
||||
if stream[-1] == '%'
|
||||
@reference = true
|
||||
stream.pop
|
||||
else
|
||||
@reference = false
|
||||
end
|
||||
if stream[2] =~ /SYSTEM|PUBLIC/
|
||||
@external = stream[2]
|
||||
if @external == 'SYSTEM'
|
||||
@ref = stream[3]
|
||||
@ndata = stream[4] if stream.size == 5
|
||||
else
|
||||
@pubid = stream[3]
|
||||
@ref = stream[4]
|
||||
end
|
||||
else
|
||||
@value = stream[2]
|
||||
end
|
||||
else
|
||||
@reference = reference
|
||||
@external = nil
|
||||
@name = stream
|
||||
@value = value
|
||||
end
|
||||
end
|
||||
|
||||
# Evaluates whether the given string matches an entity definition,
|
||||
# returning true if so, and false otherwise.
|
||||
def Entity::matches? string
|
||||
(ENTITYDECL =~ string) == 0
|
||||
end
|
||||
|
||||
# Evaluates to the unnormalized value of this entity; that is, replacing
|
||||
# all entities -- both %ent; and &ent; entities. This differs from
|
||||
# +value()+ in that +value+ only replaces %ent; entities.
|
||||
def unnormalized
|
||||
document.record_entity_expansion unless document.nil?
|
||||
v = value()
|
||||
return nil if v.nil?
|
||||
@unnormalized = Text::unnormalize(v, parent)
|
||||
@unnormalized
|
||||
end
|
||||
|
||||
#once :unnormalized
|
||||
|
||||
# Returns the value of this entity unprocessed -- raw. This is the
|
||||
# normalized value; that is, with all %ent; and &ent; entities intact
|
||||
def normalized
|
||||
@value
|
||||
end
|
||||
|
||||
# Write out a fully formed, correct entity definition (assuming the Entity
|
||||
# object itself is valid.)
|
||||
#
|
||||
# out::
|
||||
# An object implementing <TT><<</TT> to which the entity will be
|
||||
# output
|
||||
# indent::
|
||||
# *DEPRECATED* and ignored
|
||||
def write out, indent=-1
|
||||
out << '<!ENTITY '
|
||||
out << '% ' if @reference
|
||||
out << @name
|
||||
out << ' '
|
||||
if @external
|
||||
out << @external << ' '
|
||||
if @pubid
|
||||
q = @pubid.include?('"')?"'":'"'
|
||||
out << q << @pubid << q << ' '
|
||||
end
|
||||
q = @ref.include?('"')?"'":'"'
|
||||
out << q << @ref << q
|
||||
out << ' NDATA ' << @ndata if @ndata
|
||||
else
|
||||
q = @value.include?('"')?"'":'"'
|
||||
out << q << @value << q
|
||||
end
|
||||
out << '>'
|
||||
end
|
||||
|
||||
# Returns this entity as a string. See write().
|
||||
def to_s
|
||||
rv = ''
|
||||
write rv
|
||||
rv
|
||||
end
|
||||
|
||||
PEREFERENCE_RE = /#{PEREFERENCE}/um
|
||||
# Returns the value of this entity. At the moment, only internal entities
|
||||
# are processed. If the value contains internal references (IE,
|
||||
# %blah;), those are replaced with their values. IE, if the doctype
|
||||
# contains:
|
||||
# <!ENTITY % foo "bar">
|
||||
# <!ENTITY yada "nanoo %foo; nanoo>
|
||||
# then:
|
||||
# doctype.entity('yada').value #-> "nanoo bar nanoo"
|
||||
def value
|
||||
if @value
|
||||
matches = @value.scan(PEREFERENCE_RE)
|
||||
rv = @value.clone
|
||||
if @parent
|
||||
sum = 0
|
||||
matches.each do |entity_reference|
|
||||
entity_value = @parent.entity( entity_reference[0] )
|
||||
if sum + entity_value.bytesize > Security.entity_expansion_text_limit
|
||||
raise "entity expansion has grown too large"
|
||||
else
|
||||
sum += entity_value.bytesize
|
||||
end
|
||||
rv.gsub!( /%#{entity_reference.join};/um, entity_value )
|
||||
end
|
||||
end
|
||||
return rv
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# This is a set of entity constants -- the ones defined in the XML
|
||||
# specification. These are +gt+, +lt+, +amp+, +quot+ and +apos+.
|
||||
# CAUTION: these entities does not have parent and document
|
||||
module EntityConst
|
||||
# +>+
|
||||
GT = Entity.new( 'gt', '>' )
|
||||
# +<+
|
||||
LT = Entity.new( 'lt', '<' )
|
||||
# +&+
|
||||
AMP = Entity.new( 'amp', '&' )
|
||||
# +"+
|
||||
QUOT = Entity.new( 'quot', '"' )
|
||||
# +'+
|
||||
APOS = Entity.new( 'apos', "'" )
|
||||
end
|
||||
end
|
|
@ -1,116 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
|
||||
module REXML
|
||||
module Formatters
|
||||
class Default
|
||||
# Prints out the XML document with no formatting -- except if ie_hack is
|
||||
# set.
|
||||
#
|
||||
# ie_hack::
|
||||
# If set to true, then inserts whitespace before the close of an empty
|
||||
# tag, so that IE's bad XML parser doesn't choke.
|
||||
def initialize( ie_hack=false )
|
||||
@ie_hack = ie_hack
|
||||
end
|
||||
|
||||
# Writes the node to some output.
|
||||
#
|
||||
# node::
|
||||
# The node to write
|
||||
# output::
|
||||
# A class implementing <TT><<</TT>. Pass in an Output object to
|
||||
# change the output encoding.
|
||||
def write( node, output )
|
||||
case node
|
||||
|
||||
when Document
|
||||
if node.xml_decl.encoding != 'UTF-8' && !output.kind_of?(Output)
|
||||
output = Output.new( output, node.xml_decl.encoding )
|
||||
end
|
||||
write_document( node, output )
|
||||
|
||||
when Element
|
||||
write_element( node, output )
|
||||
|
||||
when Declaration, ElementDecl, NotationDecl, ExternalEntity, Entity,
|
||||
Attribute, AttlistDecl
|
||||
node.write( output,-1 )
|
||||
|
||||
when Instruction
|
||||
write_instruction( node, output )
|
||||
|
||||
when DocType, XMLDecl
|
||||
node.write( output )
|
||||
|
||||
when Comment
|
||||
write_comment( node, output )
|
||||
|
||||
when CData
|
||||
write_cdata( node, output )
|
||||
|
||||
when Text
|
||||
write_text( node, output )
|
||||
|
||||
else
|
||||
raise Exception.new("XML FORMATTING ERROR")
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def write_document( node, output )
|
||||
node.children.each { |child| write( child, output ) }
|
||||
end
|
||||
|
||||
def write_element( node, output )
|
||||
output << "<#{node.expanded_name}"
|
||||
|
||||
node.attributes.to_a.map { |a|
|
||||
Hash === a ? a.values : a
|
||||
}.flatten.sort_by {|attr| attr.name}.each do |attr|
|
||||
output << " "
|
||||
attr.write( output )
|
||||
end unless node.attributes.empty?
|
||||
|
||||
if node.children.empty?
|
||||
output << " " if @ie_hack
|
||||
output << "/"
|
||||
else
|
||||
output << ">"
|
||||
node.children.each { |child|
|
||||
write( child, output )
|
||||
}
|
||||
output << "</#{node.expanded_name}"
|
||||
end
|
||||
output << ">"
|
||||
end
|
||||
|
||||
def write_text( node, output )
|
||||
output << node.to_s()
|
||||
end
|
||||
|
||||
def write_comment( node, output )
|
||||
output << Comment::START
|
||||
output << node.to_s
|
||||
output << Comment::STOP
|
||||
end
|
||||
|
||||
def write_cdata( node, output )
|
||||
output << CData::START
|
||||
output << node.to_s
|
||||
output << CData::STOP
|
||||
end
|
||||
|
||||
def write_instruction( node, output )
|
||||
output << Instruction::START
|
||||
output << node.target
|
||||
content = node.content
|
||||
if content
|
||||
output << ' '
|
||||
output << content
|
||||
end
|
||||
output << Instruction::STOP
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,142 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'default'
|
||||
|
||||
module REXML
|
||||
module Formatters
|
||||
# Pretty-prints an XML document. This destroys whitespace in text nodes
|
||||
# and will insert carriage returns and indentations.
|
||||
#
|
||||
# TODO: Add an option to print attributes on new lines
|
||||
class Pretty < Default
|
||||
|
||||
# If compact is set to true, then the formatter will attempt to use as
|
||||
# little space as possible
|
||||
attr_accessor :compact
|
||||
# The width of a page. Used for formatting text
|
||||
attr_accessor :width
|
||||
|
||||
# Create a new pretty printer.
|
||||
#
|
||||
# output::
|
||||
# An object implementing '<<(String)', to which the output will be written.
|
||||
# indentation::
|
||||
# An integer greater than 0. The indentation of each level will be
|
||||
# this number of spaces. If this is < 1, the behavior of this object
|
||||
# is undefined. Defaults to 2.
|
||||
# ie_hack::
|
||||
# If true, the printer will insert whitespace before closing empty
|
||||
# tags, thereby allowing Internet Explorer's XML parser to
|
||||
# function. Defaults to false.
|
||||
def initialize( indentation=2, ie_hack=false )
|
||||
@indentation = indentation
|
||||
@level = 0
|
||||
@ie_hack = ie_hack
|
||||
@width = 80
|
||||
@compact = false
|
||||
end
|
||||
|
||||
protected
|
||||
def write_element(node, output)
|
||||
output << ' '*@level
|
||||
output << "<#{node.expanded_name}"
|
||||
|
||||
node.attributes.each_attribute do |attr|
|
||||
output << " "
|
||||
attr.write( output )
|
||||
end unless node.attributes.empty?
|
||||
|
||||
if node.children.empty?
|
||||
if @ie_hack
|
||||
output << " "
|
||||
end
|
||||
output << "/"
|
||||
else
|
||||
output << ">"
|
||||
# If compact and all children are text, and if the formatted output
|
||||
# is less than the specified width, then try to print everything on
|
||||
# one line
|
||||
skip = false
|
||||
if compact
|
||||
if node.children.inject(true) {|s,c| s & c.kind_of?(Text)}
|
||||
string = ""
|
||||
old_level = @level
|
||||
@level = 0
|
||||
node.children.each { |child| write( child, string ) }
|
||||
@level = old_level
|
||||
if string.length < @width
|
||||
output << string
|
||||
skip = true
|
||||
end
|
||||
end
|
||||
end
|
||||
unless skip
|
||||
output << "\n"
|
||||
@level += @indentation
|
||||
node.children.each { |child|
|
||||
next if child.kind_of?(Text) and child.to_s.strip.length == 0
|
||||
write( child, output )
|
||||
output << "\n"
|
||||
}
|
||||
@level -= @indentation
|
||||
output << ' '*@level
|
||||
end
|
||||
output << "</#{node.expanded_name}"
|
||||
end
|
||||
output << ">"
|
||||
end
|
||||
|
||||
def write_text( node, output )
|
||||
s = node.to_s()
|
||||
s.gsub!(/\s/,' ')
|
||||
s.squeeze!(" ")
|
||||
s = wrap(s, @width - @level)
|
||||
s = indent_text(s, @level, " ", true)
|
||||
output << (' '*@level + s)
|
||||
end
|
||||
|
||||
def write_comment( node, output)
|
||||
output << ' ' * @level
|
||||
super
|
||||
end
|
||||
|
||||
def write_cdata( node, output)
|
||||
output << ' ' * @level
|
||||
super
|
||||
end
|
||||
|
||||
def write_document( node, output )
|
||||
# Ok, this is a bit odd. All XML documents have an XML declaration,
|
||||
# but it may not write itself if the user didn't specifically add it,
|
||||
# either through the API or in the input document. If it doesn't write
|
||||
# itself, then we don't need a carriage return... which makes this
|
||||
# logic more complex.
|
||||
node.children.each { |child|
|
||||
next if child == node.children[-1] and child.instance_of?(Text)
|
||||
unless child == node.children[0] or child.instance_of?(Text) or
|
||||
(child == node.children[1] and !node.children[0].writethis)
|
||||
output << "\n"
|
||||
end
|
||||
write( child, output )
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
def indent_text(string, level=1, style="\t", indentfirstline=true)
|
||||
return string if level < 0
|
||||
string.gsub(/\n/, "\n#{style*level}")
|
||||
end
|
||||
|
||||
def wrap(string, width)
|
||||
parts = []
|
||||
while string.length > width and place = string.rindex(' ', width)
|
||||
parts << string[0...place]
|
||||
string = string[place+1..-1]
|
||||
end
|
||||
parts << string
|
||||
parts.join("\n")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'pretty'
|
||||
|
||||
module REXML
|
||||
module Formatters
|
||||
# The Transitive formatter writes an XML document that parses to an
|
||||
# identical document as the source document. This means that no extra
|
||||
# whitespace nodes are inserted, and whitespace within text nodes is
|
||||
# preserved. Within these constraints, the document is pretty-printed,
|
||||
# with whitespace inserted into the metadata to introduce formatting.
|
||||
#
|
||||
# Note that this is only useful if the original XML is not already
|
||||
# formatted. Since this formatter does not alter whitespace nodes, the
|
||||
# results of formatting already formatted XML will be odd.
|
||||
class Transitive < Default
|
||||
def initialize( indentation=2, ie_hack=false )
|
||||
@indentation = indentation
|
||||
@level = 0
|
||||
@ie_hack = ie_hack
|
||||
end
|
||||
|
||||
protected
|
||||
def write_element( node, output )
|
||||
output << "<#{node.expanded_name}"
|
||||
|
||||
node.attributes.each_attribute do |attr|
|
||||
output << " "
|
||||
attr.write( output )
|
||||
end unless node.attributes.empty?
|
||||
|
||||
output << "\n"
|
||||
output << ' '*@level
|
||||
if node.children.empty?
|
||||
output << " " if @ie_hack
|
||||
output << "/"
|
||||
else
|
||||
output << ">"
|
||||
# If compact and all children are text, and if the formatted output
|
||||
# is less than the specified width, then try to print everything on
|
||||
# one line
|
||||
@level += @indentation
|
||||
node.children.each { |child|
|
||||
write( child, output )
|
||||
}
|
||||
@level -= @indentation
|
||||
output << "</#{node.expanded_name}"
|
||||
output << "\n"
|
||||
output << ' '*@level
|
||||
end
|
||||
output << ">"
|
||||
end
|
||||
|
||||
def write_text( node, output )
|
||||
output << node.to_s()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,447 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
module REXML
|
||||
# If you add a method, keep in mind two things:
|
||||
# (1) the first argument will always be a list of nodes from which to
|
||||
# filter. In the case of context methods (such as position), the function
|
||||
# should return an array with a value for each child in the array.
|
||||
# (2) all method calls from XML will have "-" replaced with "_".
|
||||
# Therefore, in XML, "local-name()" is identical (and actually becomes)
|
||||
# "local_name()"
|
||||
module Functions
|
||||
@@available_functions = {}
|
||||
@@context = nil
|
||||
@@namespace_context = {}
|
||||
@@variables = {}
|
||||
|
||||
INTERNAL_METHODS = [
|
||||
:namespace_context,
|
||||
:namespace_context=,
|
||||
:variables,
|
||||
:variables=,
|
||||
:context=,
|
||||
:get_namespace,
|
||||
:send,
|
||||
]
|
||||
class << self
|
||||
def singleton_method_added(name)
|
||||
unless INTERNAL_METHODS.include?(name)
|
||||
@@available_functions[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def Functions::namespace_context=(x) ; @@namespace_context=x ; end
|
||||
def Functions::variables=(x) ; @@variables=x ; end
|
||||
def Functions::namespace_context ; @@namespace_context ; end
|
||||
def Functions::variables ; @@variables ; end
|
||||
|
||||
def Functions::context=(value); @@context = value; end
|
||||
|
||||
def Functions::text( )
|
||||
if @@context[:node].node_type == :element
|
||||
return @@context[:node].find_all{|n| n.node_type == :text}.collect{|n| n.value}
|
||||
elsif @@context[:node].node_type == :text
|
||||
return @@context[:node].value
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the last node of the given list of nodes.
|
||||
def Functions::last( )
|
||||
@@context[:size]
|
||||
end
|
||||
|
||||
def Functions::position( )
|
||||
@@context[:index]
|
||||
end
|
||||
|
||||
# Returns the size of the given list of nodes.
|
||||
def Functions::count( node_set )
|
||||
node_set.size
|
||||
end
|
||||
|
||||
# Since REXML is non-validating, this method is not implemented as it
|
||||
# requires a DTD
|
||||
def Functions::id( object )
|
||||
end
|
||||
|
||||
def Functions::local_name(node_set=nil)
|
||||
get_namespace(node_set) do |node|
|
||||
return node.local_name
|
||||
end
|
||||
""
|
||||
end
|
||||
|
||||
def Functions::namespace_uri( node_set=nil )
|
||||
get_namespace( node_set ) {|node| node.namespace}
|
||||
end
|
||||
|
||||
def Functions::name( node_set=nil )
|
||||
get_namespace( node_set ) do |node|
|
||||
node.expanded_name
|
||||
end
|
||||
end
|
||||
|
||||
# Helper method.
|
||||
def Functions::get_namespace( node_set = nil )
|
||||
if node_set == nil
|
||||
yield @@context[:node] if @@context[:node].respond_to?(:namespace)
|
||||
else
|
||||
if node_set.respond_to? :each
|
||||
result = []
|
||||
node_set.each do |node|
|
||||
result << yield(node) if node.respond_to?(:namespace)
|
||||
end
|
||||
result
|
||||
elsif node_set.respond_to? :namespace
|
||||
yield node_set
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# A node-set is converted to a string by returning the string-value of the
|
||||
# node in the node-set that is first in document order. If the node-set is
|
||||
# empty, an empty string is returned.
|
||||
#
|
||||
# A number is converted to a string as follows
|
||||
#
|
||||
# NaN is converted to the string NaN
|
||||
#
|
||||
# positive zero is converted to the string 0
|
||||
#
|
||||
# negative zero is converted to the string 0
|
||||
#
|
||||
# positive infinity is converted to the string Infinity
|
||||
#
|
||||
# negative infinity is converted to the string -Infinity
|
||||
#
|
||||
# if the number is an integer, the number is represented in decimal form
|
||||
# as a Number with no decimal point and no leading zeros, preceded by a
|
||||
# minus sign (-) if the number is negative
|
||||
#
|
||||
# otherwise, the number is represented in decimal form as a Number
|
||||
# including a decimal point with at least one digit before the decimal
|
||||
# point and at least one digit after the decimal point, preceded by a
|
||||
# minus sign (-) if the number is negative; there must be no leading zeros
|
||||
# before the decimal point apart possibly from the one required digit
|
||||
# immediately before the decimal point; beyond the one required digit
|
||||
# after the decimal point there must be as many, but only as many, more
|
||||
# digits as are needed to uniquely distinguish the number from all other
|
||||
# IEEE 754 numeric values.
|
||||
#
|
||||
# The boolean false value is converted to the string false. The boolean
|
||||
# true value is converted to the string true.
|
||||
#
|
||||
# An object of a type other than the four basic types is converted to a
|
||||
# string in a way that is dependent on that type.
|
||||
def Functions::string( object=@@context[:node] )
|
||||
if object.respond_to?(:node_type)
|
||||
case object.node_type
|
||||
when :attribute
|
||||
object.value
|
||||
when :element
|
||||
string_value(object)
|
||||
when :document
|
||||
string_value(object.root)
|
||||
when :processing_instruction
|
||||
object.content
|
||||
else
|
||||
object.to_s
|
||||
end
|
||||
else
|
||||
case object
|
||||
when Array
|
||||
string(object[0])
|
||||
when Float
|
||||
if object.nan?
|
||||
"NaN"
|
||||
else
|
||||
integer = object.to_i
|
||||
if object == integer
|
||||
"%d" % integer
|
||||
else
|
||||
object.to_s
|
||||
end
|
||||
end
|
||||
else
|
||||
object.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# A node-set is converted to a string by
|
||||
# returning the concatenation of the string-value
|
||||
# of each of the children of the node in the
|
||||
# node-set that is first in document order.
|
||||
# If the node-set is empty, an empty string is returned.
|
||||
def Functions::string_value( o )
|
||||
rv = ""
|
||||
o.children.each { |e|
|
||||
if e.node_type == :text
|
||||
rv << e.to_s
|
||||
elsif e.node_type == :element
|
||||
rv << string_value( e )
|
||||
end
|
||||
}
|
||||
rv
|
||||
end
|
||||
|
||||
def Functions::concat( *objects )
|
||||
concatenated = ""
|
||||
objects.each do |object|
|
||||
concatenated << string(object)
|
||||
end
|
||||
concatenated
|
||||
end
|
||||
|
||||
# Fixed by Mike Stok
|
||||
def Functions::starts_with( string, test )
|
||||
string(string).index(string(test)) == 0
|
||||
end
|
||||
|
||||
# Fixed by Mike Stok
|
||||
def Functions::contains( string, test )
|
||||
string(string).include?(string(test))
|
||||
end
|
||||
|
||||
# Kouhei fixed this
|
||||
def Functions::substring_before( string, test )
|
||||
ruby_string = string(string)
|
||||
ruby_index = ruby_string.index(string(test))
|
||||
if ruby_index.nil?
|
||||
""
|
||||
else
|
||||
ruby_string[ 0...ruby_index ]
|
||||
end
|
||||
end
|
||||
|
||||
# Kouhei fixed this too
|
||||
def Functions::substring_after( string, test )
|
||||
ruby_string = string(string)
|
||||
return $1 if ruby_string =~ /#{test}(.*)/
|
||||
""
|
||||
end
|
||||
|
||||
# Take equal portions of Mike Stok and Sean Russell; mix
|
||||
# vigorously, and pour into a tall, chilled glass. Serves 10,000.
|
||||
def Functions::substring( string, start, length=nil )
|
||||
ruby_string = string(string)
|
||||
ruby_length = if length.nil?
|
||||
ruby_string.length.to_f
|
||||
else
|
||||
number(length)
|
||||
end
|
||||
ruby_start = number(start)
|
||||
|
||||
# Handle the special cases
|
||||
return '' if (
|
||||
ruby_length.nan? or
|
||||
ruby_start.nan? or
|
||||
ruby_start.infinite?
|
||||
)
|
||||
|
||||
infinite_length = ruby_length.infinite? == 1
|
||||
ruby_length = ruby_string.length if infinite_length
|
||||
|
||||
# Now, get the bounds. The XPath bounds are 1..length; the ruby bounds
|
||||
# are 0..length. Therefore, we have to offset the bounds by one.
|
||||
ruby_start = round(ruby_start) - 1
|
||||
ruby_length = round(ruby_length)
|
||||
|
||||
if ruby_start < 0
|
||||
ruby_length += ruby_start unless infinite_length
|
||||
ruby_start = 0
|
||||
end
|
||||
return '' if ruby_length <= 0
|
||||
ruby_string[ruby_start,ruby_length]
|
||||
end
|
||||
|
||||
# UNTESTED
|
||||
def Functions::string_length( string )
|
||||
string(string).length
|
||||
end
|
||||
|
||||
# UNTESTED
|
||||
def Functions::normalize_space( string=nil )
|
||||
string = string(@@context[:node]) if string.nil?
|
||||
if string.kind_of? Array
|
||||
string.collect{|x| string.to_s.strip.gsub(/\s+/um, ' ') if string}
|
||||
else
|
||||
string.to_s.strip.gsub(/\s+/um, ' ')
|
||||
end
|
||||
end
|
||||
|
||||
# This is entirely Mike Stok's beast
|
||||
def Functions::translate( string, tr1, tr2 )
|
||||
from = string(tr1)
|
||||
to = string(tr2)
|
||||
|
||||
# the map is our translation table.
|
||||
#
|
||||
# if a character occurs more than once in the
|
||||
# from string then we ignore the second &
|
||||
# subsequent mappings
|
||||
#
|
||||
# if a character maps to nil then we delete it
|
||||
# in the output. This happens if the from
|
||||
# string is longer than the to string
|
||||
#
|
||||
# there's nothing about - or ^ being special in
|
||||
# http://www.w3.org/TR/xpath#function-translate
|
||||
# so we don't build ranges or negated classes
|
||||
|
||||
map = Hash.new
|
||||
0.upto(from.length - 1) { |pos|
|
||||
from_char = from[pos]
|
||||
unless map.has_key? from_char
|
||||
map[from_char] =
|
||||
if pos < to.length
|
||||
to[pos]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
if ''.respond_to? :chars
|
||||
string(string).chars.collect { |c|
|
||||
if map.has_key? c then map[c] else c end
|
||||
}.compact.join
|
||||
else
|
||||
string(string).unpack('U*').collect { |c|
|
||||
if map.has_key? c then map[c] else c end
|
||||
}.compact.pack('U*')
|
||||
end
|
||||
end
|
||||
|
||||
def Functions::boolean(object=@@context[:node])
|
||||
case object
|
||||
when true, false
|
||||
object
|
||||
when Float
|
||||
return false if object.zero?
|
||||
return false if object.nan?
|
||||
true
|
||||
when Numeric
|
||||
not object.zero?
|
||||
when String
|
||||
not object.empty?
|
||||
when Array
|
||||
not object.empty?
|
||||
else
|
||||
object ? true : false
|
||||
end
|
||||
end
|
||||
|
||||
# UNTESTED
|
||||
def Functions::not( object )
|
||||
not boolean( object )
|
||||
end
|
||||
|
||||
# UNTESTED
|
||||
def Functions::true( )
|
||||
true
|
||||
end
|
||||
|
||||
# UNTESTED
|
||||
def Functions::false( )
|
||||
false
|
||||
end
|
||||
|
||||
# UNTESTED
|
||||
def Functions::lang( language )
|
||||
lang = false
|
||||
node = @@context[:node]
|
||||
attr = nil
|
||||
until node.nil?
|
||||
if node.node_type == :element
|
||||
attr = node.attributes["xml:lang"]
|
||||
unless attr.nil?
|
||||
lang = compare_language(string(language), attr)
|
||||
break
|
||||
else
|
||||
end
|
||||
end
|
||||
node = node.parent
|
||||
end
|
||||
lang
|
||||
end
|
||||
|
||||
def Functions::compare_language lang1, lang2
|
||||
lang2.downcase.index(lang1.downcase) == 0
|
||||
end
|
||||
|
||||
# a string that consists of optional whitespace followed by an optional
|
||||
# minus sign followed by a Number followed by whitespace is converted to
|
||||
# the IEEE 754 number that is nearest (according to the IEEE 754
|
||||
# round-to-nearest rule) to the mathematical value represented by the
|
||||
# string; any other string is converted to NaN
|
||||
#
|
||||
# boolean true is converted to 1; boolean false is converted to 0
|
||||
#
|
||||
# a node-set is first converted to a string as if by a call to the string
|
||||
# function and then converted in the same way as a string argument
|
||||
#
|
||||
# an object of a type other than the four basic types is converted to a
|
||||
# number in a way that is dependent on that type
|
||||
def Functions::number(object=@@context[:node])
|
||||
case object
|
||||
when true
|
||||
Float(1)
|
||||
when false
|
||||
Float(0)
|
||||
when Array
|
||||
number(string(object))
|
||||
when Numeric
|
||||
object.to_f
|
||||
else
|
||||
str = string(object)
|
||||
case str.strip
|
||||
when /\A\s*(-?(?:\d+(?:\.\d*)?|\.\d+))\s*\z/
|
||||
$1.to_f
|
||||
else
|
||||
Float::NAN
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def Functions::sum( nodes )
|
||||
nodes = [nodes] unless nodes.kind_of? Array
|
||||
nodes.inject(0) { |r,n| r + number(string(n)) }
|
||||
end
|
||||
|
||||
def Functions::floor( number )
|
||||
number(number).floor
|
||||
end
|
||||
|
||||
def Functions::ceiling( number )
|
||||
number(number).ceil
|
||||
end
|
||||
|
||||
def Functions::round( number )
|
||||
number = number(number)
|
||||
begin
|
||||
neg = number.negative?
|
||||
number = number.abs.round
|
||||
neg ? -number : number
|
||||
rescue FloatDomainError
|
||||
number
|
||||
end
|
||||
end
|
||||
|
||||
def Functions::processing_instruction( node )
|
||||
node.node_type == :processing_instruction
|
||||
end
|
||||
|
||||
def Functions::send(name, *args)
|
||||
if @@available_functions[name.to_sym]
|
||||
super
|
||||
else
|
||||
# TODO: Maybe, this is not XPath spec behavior.
|
||||
# This behavior must be reconsidered.
|
||||
XPath.match(@@context[:node], name.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,79 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
|
||||
require_relative "child"
|
||||
require_relative "source"
|
||||
|
||||
module REXML
|
||||
# Represents an XML Instruction; IE, <? ... ?>
|
||||
# TODO: Add parent arg (3rd arg) to constructor
|
||||
class Instruction < Child
|
||||
START = "<?"
|
||||
STOP = "?>"
|
||||
|
||||
# target is the "name" of the Instruction; IE, the "tag" in <?tag ...?>
|
||||
# content is everything else.
|
||||
attr_accessor :target, :content
|
||||
|
||||
# Constructs a new Instruction
|
||||
# @param target can be one of a number of things. If String, then
|
||||
# the target of this instruction is set to this. If an Instruction,
|
||||
# then the Instruction is shallowly cloned (target and content are
|
||||
# copied).
|
||||
# @param content Must be either a String, or a Parent. Can only
|
||||
# be a Parent if the target argument is a Source. Otherwise, this
|
||||
# String is set as the content of this instruction.
|
||||
def initialize(target, content=nil)
|
||||
case target
|
||||
when String
|
||||
super()
|
||||
@target = target
|
||||
@content = content
|
||||
when Instruction
|
||||
super(content)
|
||||
@target = target.target
|
||||
@content = target.content
|
||||
else
|
||||
message =
|
||||
"processing instruction target must be String or REXML::Instruction: "
|
||||
message << "<#{target.inspect}>"
|
||||
raise ArgumentError, message
|
||||
end
|
||||
@content.strip! if @content
|
||||
end
|
||||
|
||||
def clone
|
||||
Instruction.new self
|
||||
end
|
||||
|
||||
# == DEPRECATED
|
||||
# See the rexml/formatters package
|
||||
#
|
||||
def write writer, indent=-1, transitive=false, ie_hack=false
|
||||
Kernel.warn( "#{self.class.name}.write is deprecated", uplevel: 1)
|
||||
indent(writer, indent)
|
||||
writer << START
|
||||
writer << @target
|
||||
if @content
|
||||
writer << ' '
|
||||
writer << @content
|
||||
end
|
||||
writer << STOP
|
||||
end
|
||||
|
||||
# @return true if other is an Instruction, and the content and target
|
||||
# of the other matches the target and content of this object.
|
||||
def ==( other )
|
||||
other.kind_of? Instruction and
|
||||
other.target == @target and
|
||||
other.content == @content
|
||||
end
|
||||
|
||||
def node_type
|
||||
:processing_instruction
|
||||
end
|
||||
|
||||
def inspect
|
||||
"<?p-i #{target} ...?>"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,196 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative '../xmltokens'
|
||||
|
||||
# [ :element, parent, name, attributes, children* ]
|
||||
# a = Node.new
|
||||
# a << "B" # => <a>B</a>
|
||||
# a.b # => <a>B<b/></a>
|
||||
# a.b[1] # => <a>B<b/><b/><a>
|
||||
# a.b[1]["x"] = "y" # => <a>B<b/><b x="y"/></a>
|
||||
# a.b[0].c # => <a>B<b><c/></b><b x="y"/></a>
|
||||
# a.b.c << "D" # => <a>B<b><c>D</c></b><b x="y"/></a>
|
||||
module REXML
|
||||
module Light
|
||||
# Represents a tagged XML element. Elements are characterized by
|
||||
# having children, attributes, and names, and can themselves be
|
||||
# children.
|
||||
class Node
|
||||
NAMESPLIT = /^(?:(#{XMLTokens::NCNAME_STR}):)?(#{XMLTokens::NCNAME_STR})/u
|
||||
PARENTS = [ :element, :document, :doctype ]
|
||||
# Create a new element.
|
||||
def initialize node=nil
|
||||
@node = node
|
||||
if node.kind_of? String
|
||||
node = [ :text, node ]
|
||||
elsif node.nil?
|
||||
node = [ :document, nil, nil ]
|
||||
elsif node[0] == :start_element
|
||||
node[0] = :element
|
||||
elsif node[0] == :start_doctype
|
||||
node[0] = :doctype
|
||||
elsif node[0] == :start_document
|
||||
node[0] = :document
|
||||
end
|
||||
end
|
||||
|
||||
def size
|
||||
if PARENTS.include? @node[0]
|
||||
@node[-1].size
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def each
|
||||
size.times { |x| yield( at(x+4) ) }
|
||||
end
|
||||
|
||||
def name
|
||||
at(2)
|
||||
end
|
||||
|
||||
def name=( name_str, ns=nil )
|
||||
pfx = ''
|
||||
pfx = "#{prefix(ns)}:" if ns
|
||||
_old_put(2, "#{pfx}#{name_str}")
|
||||
end
|
||||
|
||||
def parent=( node )
|
||||
_old_put(1,node)
|
||||
end
|
||||
|
||||
def local_name
|
||||
namesplit
|
||||
@name
|
||||
end
|
||||
|
||||
def local_name=( name_str )
|
||||
_old_put( 1, "#@prefix:#{name_str}" )
|
||||
end
|
||||
|
||||
def prefix( namespace=nil )
|
||||
prefix_of( self, namespace )
|
||||
end
|
||||
|
||||
def namespace( prefix=prefix() )
|
||||
namespace_of( self, prefix )
|
||||
end
|
||||
|
||||
def namespace=( namespace )
|
||||
@prefix = prefix( namespace )
|
||||
pfx = ''
|
||||
pfx = "#@prefix:" if @prefix.size > 0
|
||||
_old_put(1, "#{pfx}#@name")
|
||||
end
|
||||
|
||||
def []( reference, ns=nil )
|
||||
if reference.kind_of? String
|
||||
pfx = ''
|
||||
pfx = "#{prefix(ns)}:" if ns
|
||||
at(3)["#{pfx}#{reference}"]
|
||||
elsif reference.kind_of? Range
|
||||
_old_get( Range.new(4+reference.begin, reference.end, reference.exclude_end?) )
|
||||
else
|
||||
_old_get( 4+reference )
|
||||
end
|
||||
end
|
||||
|
||||
def =~( path )
|
||||
XPath.match( self, path )
|
||||
end
|
||||
|
||||
# Doesn't handle namespaces yet
|
||||
def []=( reference, ns, value=nil )
|
||||
if reference.kind_of? String
|
||||
value = ns unless value
|
||||
at( 3 )[reference] = value
|
||||
elsif reference.kind_of? Range
|
||||
_old_put( Range.new(3+reference.begin, reference.end, reference.exclude_end?), ns )
|
||||
else
|
||||
if value
|
||||
_old_put( 4+reference, ns, value )
|
||||
else
|
||||
_old_put( 4+reference, ns )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Append a child to this element, optionally under a provided namespace.
|
||||
# The namespace argument is ignored if the element argument is an Element
|
||||
# object. Otherwise, the element argument is a string, the namespace (if
|
||||
# provided) is the namespace the element is created in.
|
||||
def << element
|
||||
if node_type() == :text
|
||||
at(-1) << element
|
||||
else
|
||||
newnode = Node.new( element )
|
||||
newnode.parent = self
|
||||
self.push( newnode )
|
||||
end
|
||||
at(-1)
|
||||
end
|
||||
|
||||
def node_type
|
||||
_old_get(0)
|
||||
end
|
||||
|
||||
def text=( foo )
|
||||
replace = at(4).kind_of?(String)? 1 : 0
|
||||
self._old_put(4,replace, normalizefoo)
|
||||
end
|
||||
|
||||
def root
|
||||
context = self
|
||||
context = context.at(1) while context.at(1)
|
||||
end
|
||||
|
||||
def has_name?( name, namespace = '' )
|
||||
at(3) == name and namespace() == namespace
|
||||
end
|
||||
|
||||
def children
|
||||
self
|
||||
end
|
||||
|
||||
def parent
|
||||
at(1)
|
||||
end
|
||||
|
||||
def to_s
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def namesplit
|
||||
return if @name.defined?
|
||||
at(2) =~ NAMESPLIT
|
||||
@prefix = '' || $1
|
||||
@name = $2
|
||||
end
|
||||
|
||||
def namespace_of( node, prefix=nil )
|
||||
if not prefix
|
||||
name = at(2)
|
||||
name =~ NAMESPLIT
|
||||
prefix = $1
|
||||
end
|
||||
to_find = 'xmlns'
|
||||
to_find = "xmlns:#{prefix}" if not prefix.nil?
|
||||
ns = at(3)[ to_find ]
|
||||
ns ? ns : namespace_of( @node[0], prefix )
|
||||
end
|
||||
|
||||
def prefix_of( node, namespace=nil )
|
||||
if not namespace
|
||||
name = node.name
|
||||
name =~ NAMESPLIT
|
||||
$1
|
||||
else
|
||||
ns = at(3).find { |k,v| v == namespace }
|
||||
ns ? ns : prefix_of( node.parent, namespace )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,59 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
|
||||
require_relative 'xmltokens'
|
||||
|
||||
module REXML
|
||||
# Adds named attributes to an object.
|
||||
module Namespace
|
||||
# The name of the object, valid if set
|
||||
attr_reader :name, :expanded_name
|
||||
# The expanded name of the object, valid if name is set
|
||||
attr_accessor :prefix
|
||||
include XMLTokens
|
||||
NAMESPLIT = /^(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})/u
|
||||
|
||||
# Sets the name and the expanded name
|
||||
def name=( name )
|
||||
@expanded_name = name
|
||||
case name
|
||||
when NAMESPLIT
|
||||
if $1
|
||||
@prefix = $1
|
||||
else
|
||||
@prefix = ""
|
||||
@namespace = ""
|
||||
end
|
||||
@name = $2
|
||||
when ""
|
||||
@prefix = nil
|
||||
@namespace = nil
|
||||
@name = nil
|
||||
else
|
||||
message = "name must be \#{PREFIX}:\#{LOCAL_NAME} or \#{LOCAL_NAME}: "
|
||||
message += "<#{name.inspect}>"
|
||||
raise ArgumentError, message
|
||||
end
|
||||
end
|
||||
|
||||
# Compares names optionally WITH namespaces
|
||||
def has_name?( other, ns=nil )
|
||||
if ns
|
||||
return (namespace() == ns and name() == other)
|
||||
elsif other.include? ":"
|
||||
return fully_expanded_name == other
|
||||
else
|
||||
return name == other
|
||||
end
|
||||
end
|
||||
|
||||
alias :local_name :name
|
||||
|
||||
# Fully expand the name, even if the prefix wasn't specified in the
|
||||
# source file.
|
||||
def fully_expanded_name
|
||||
ns = prefix
|
||||
return "#{ns}:#@name" if ns.size > 0
|
||||
return @name
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,76 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "parseexception"
|
||||
require_relative "formatters/pretty"
|
||||
require_relative "formatters/default"
|
||||
|
||||
module REXML
|
||||
# Represents a node in the tree. Nodes are never encountered except as
|
||||
# superclasses of other objects. Nodes have siblings.
|
||||
module Node
|
||||
# @return the next sibling (nil if unset)
|
||||
def next_sibling_node
|
||||
return nil if @parent.nil?
|
||||
@parent[ @parent.index(self) + 1 ]
|
||||
end
|
||||
|
||||
# @return the previous sibling (nil if unset)
|
||||
def previous_sibling_node
|
||||
return nil if @parent.nil?
|
||||
ind = @parent.index(self)
|
||||
return nil if ind == 0
|
||||
@parent[ ind - 1 ]
|
||||
end
|
||||
|
||||
# indent::
|
||||
# *DEPRECATED* This parameter is now ignored. See the formatters in the
|
||||
# REXML::Formatters package for changing the output style.
|
||||
def to_s indent=nil
|
||||
unless indent.nil?
|
||||
Kernel.warn( "#{self.class.name}.to_s(indent) parameter is deprecated", uplevel: 1)
|
||||
f = REXML::Formatters::Pretty.new( indent )
|
||||
f.write( self, rv = "" )
|
||||
else
|
||||
f = REXML::Formatters::Default.new
|
||||
f.write( self, rv = "" )
|
||||
end
|
||||
return rv
|
||||
end
|
||||
|
||||
def indent to, ind
|
||||
if @parent and @parent.context and not @parent.context[:indentstyle].nil? then
|
||||
indentstyle = @parent.context[:indentstyle]
|
||||
else
|
||||
indentstyle = ' '
|
||||
end
|
||||
to << indentstyle*ind unless ind<1
|
||||
end
|
||||
|
||||
def parent?
|
||||
false;
|
||||
end
|
||||
|
||||
|
||||
# Visit all subnodes of +self+ recursively
|
||||
def each_recursive(&block) # :yields: node
|
||||
self.elements.each {|node|
|
||||
block.call(node)
|
||||
node.each_recursive(&block)
|
||||
}
|
||||
end
|
||||
|
||||
# Find (and return) first subnode (recursively) for which the block
|
||||
# evaluates to true. Returns +nil+ if none was found.
|
||||
def find_first_recursive(&block) # :yields: node
|
||||
each_recursive {|node|
|
||||
return node if block.call(node)
|
||||
}
|
||||
return nil
|
||||
end
|
||||
|
||||
# Returns the position that +self+ holds in its parent's array, indexed
|
||||
# from 1.
|
||||
def index_in_parent
|
||||
parent.index(self)+1
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,30 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'encoding'
|
||||
|
||||
module REXML
|
||||
class Output
|
||||
include Encoding
|
||||
|
||||
attr_reader :encoding
|
||||
|
||||
def initialize real_IO, encd="iso-8859-1"
|
||||
@output = real_IO
|
||||
self.encoding = encd
|
||||
|
||||
@to_utf = encoding != 'UTF-8'
|
||||
|
||||
if encoding == "UTF-16"
|
||||
@output << "\ufeff".encode("UTF-16BE")
|
||||
self.encoding = "UTF-16BE"
|
||||
end
|
||||
end
|
||||
|
||||
def <<( content )
|
||||
@output << (@to_utf ? self.encode(content) : content)
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Output[#{encoding}]"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,166 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "child"
|
||||
|
||||
module REXML
|
||||
# A parent has children, and has methods for accessing them. The Parent
|
||||
# class is never encountered except as the superclass for some other
|
||||
# object.
|
||||
class Parent < Child
|
||||
include Enumerable
|
||||
|
||||
# Constructor
|
||||
# @param parent if supplied, will be set as the parent of this object
|
||||
def initialize parent=nil
|
||||
super(parent)
|
||||
@children = []
|
||||
end
|
||||
|
||||
def add( object )
|
||||
object.parent = self
|
||||
@children << object
|
||||
object
|
||||
end
|
||||
|
||||
alias :push :add
|
||||
alias :<< :push
|
||||
|
||||
def unshift( object )
|
||||
object.parent = self
|
||||
@children.unshift object
|
||||
end
|
||||
|
||||
def delete( object )
|
||||
found = false
|
||||
@children.delete_if {|c| c.equal?(object) and found = true }
|
||||
object.parent = nil if found
|
||||
found ? object : nil
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
@children.each(&block)
|
||||
end
|
||||
|
||||
def delete_if( &block )
|
||||
@children.delete_if(&block)
|
||||
end
|
||||
|
||||
def delete_at( index )
|
||||
@children.delete_at index
|
||||
end
|
||||
|
||||
def each_index( &block )
|
||||
@children.each_index(&block)
|
||||
end
|
||||
|
||||
# Fetches a child at a given index
|
||||
# @param index the Integer index of the child to fetch
|
||||
def []( index )
|
||||
@children[index]
|
||||
end
|
||||
|
||||
alias :each_child :each
|
||||
|
||||
|
||||
|
||||
# Set an index entry. See Array.[]=
|
||||
# @param index the index of the element to set
|
||||
# @param opt either the object to set, or an Integer length
|
||||
# @param child if opt is an Integer, this is the child to set
|
||||
# @return the parent (self)
|
||||
def []=( *args )
|
||||
args[-1].parent = self
|
||||
@children[*args[0..-2]] = args[-1]
|
||||
end
|
||||
|
||||
# Inserts an child before another child
|
||||
# @param child1 this is either an xpath or an Element. If an Element,
|
||||
# child2 will be inserted before child1 in the child list of the parent.
|
||||
# If an xpath, child2 will be inserted before the first child to match
|
||||
# the xpath.
|
||||
# @param child2 the child to insert
|
||||
# @return the parent (self)
|
||||
def insert_before( child1, child2 )
|
||||
if child1.kind_of? String
|
||||
child1 = XPath.first( self, child1 )
|
||||
child1.parent.insert_before child1, child2
|
||||
else
|
||||
ind = index(child1)
|
||||
child2.parent.delete(child2) if child2.parent
|
||||
@children[ind,0] = child2
|
||||
child2.parent = self
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Inserts an child after another child
|
||||
# @param child1 this is either an xpath or an Element. If an Element,
|
||||
# child2 will be inserted after child1 in the child list of the parent.
|
||||
# If an xpath, child2 will be inserted after the first child to match
|
||||
# the xpath.
|
||||
# @param child2 the child to insert
|
||||
# @return the parent (self)
|
||||
def insert_after( child1, child2 )
|
||||
if child1.kind_of? String
|
||||
child1 = XPath.first( self, child1 )
|
||||
child1.parent.insert_after child1, child2
|
||||
else
|
||||
ind = index(child1)+1
|
||||
child2.parent.delete(child2) if child2.parent
|
||||
@children[ind,0] = child2
|
||||
child2.parent = self
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def to_a
|
||||
@children.dup
|
||||
end
|
||||
|
||||
# Fetches the index of a given child
|
||||
# @param child the child to get the index of
|
||||
# @return the index of the child, or nil if the object is not a child
|
||||
# of this parent.
|
||||
def index( child )
|
||||
count = -1
|
||||
@children.find { |i| count += 1 ; i.hash == child.hash }
|
||||
count
|
||||
end
|
||||
|
||||
# @return the number of children of this parent
|
||||
def size
|
||||
@children.size
|
||||
end
|
||||
|
||||
alias :length :size
|
||||
|
||||
# Replaces one child with another, making sure the nodelist is correct
|
||||
# @param to_replace the child to replace (must be a Child)
|
||||
# @param replacement the child to insert into the nodelist (must be a
|
||||
# Child)
|
||||
def replace_child( to_replace, replacement )
|
||||
@children.map! {|c| c.equal?( to_replace ) ? replacement : c }
|
||||
to_replace.parent = nil
|
||||
replacement.parent = self
|
||||
end
|
||||
|
||||
# Deeply clones this object. This creates a complete duplicate of this
|
||||
# Parent, including all descendants.
|
||||
def deep_clone
|
||||
cl = clone()
|
||||
each do |child|
|
||||
if child.kind_of? Parent
|
||||
cl << child.deep_clone
|
||||
else
|
||||
cl << child.clone
|
||||
end
|
||||
end
|
||||
cl
|
||||
end
|
||||
|
||||
alias :children :to_a
|
||||
|
||||
def parent?
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,52 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
module REXML
|
||||
class ParseException < RuntimeError
|
||||
attr_accessor :source, :parser, :continued_exception
|
||||
|
||||
def initialize( message, source=nil, parser=nil, exception=nil )
|
||||
super(message)
|
||||
@source = source
|
||||
@parser = parser
|
||||
@continued_exception = exception
|
||||
end
|
||||
|
||||
def to_s
|
||||
# Quote the original exception, if there was one
|
||||
if @continued_exception
|
||||
err = @continued_exception.inspect
|
||||
err << "\n"
|
||||
err << @continued_exception.backtrace.join("\n")
|
||||
err << "\n...\n"
|
||||
else
|
||||
err = ""
|
||||
end
|
||||
|
||||
# Get the stack trace and error message
|
||||
err << super
|
||||
|
||||
# Add contextual information
|
||||
if @source
|
||||
err << "\nLine: #{line}\n"
|
||||
err << "Position: #{position}\n"
|
||||
err << "Last 80 unconsumed characters:\n"
|
||||
err << @source.buffer[0..80].force_encoding("ASCII-8BIT").gsub(/\n/, ' ')
|
||||
end
|
||||
|
||||
err
|
||||
end
|
||||
|
||||
def position
|
||||
@source.current_line[0] if @source and defined? @source.current_line and
|
||||
@source.current_line
|
||||
end
|
||||
|
||||
def line
|
||||
@source.current_line[2] if @source and defined? @source.current_line and
|
||||
@source.current_line
|
||||
end
|
||||
|
||||
def context
|
||||
@source.current_line
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,594 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative '../parseexception'
|
||||
require_relative '../undefinednamespaceexception'
|
||||
require_relative '../source'
|
||||
require 'set'
|
||||
require "strscan"
|
||||
|
||||
module REXML
|
||||
module Parsers
|
||||
# = Using the Pull Parser
|
||||
# <em>This API is experimental, and subject to change.</em>
|
||||
# parser = PullParser.new( "<a>text<b att='val'/>txet</a>" )
|
||||
# while parser.has_next?
|
||||
# res = parser.next
|
||||
# puts res[1]['att'] if res.start_tag? and res[0] == 'b'
|
||||
# end
|
||||
# See the PullEvent class for information on the content of the results.
|
||||
# The data is identical to the arguments passed for the various events to
|
||||
# the StreamListener API.
|
||||
#
|
||||
# Notice that:
|
||||
# parser = PullParser.new( "<a>BAD DOCUMENT" )
|
||||
# while parser.has_next?
|
||||
# res = parser.next
|
||||
# raise res[1] if res.error?
|
||||
# end
|
||||
#
|
||||
# Nat Price gave me some good ideas for the API.
|
||||
class BaseParser
|
||||
LETTER = '[:alpha:]'
|
||||
DIGIT = '[:digit:]'
|
||||
|
||||
COMBININGCHAR = '' # TODO
|
||||
EXTENDER = '' # TODO
|
||||
|
||||
NCNAME_STR= "[#{LETTER}_][-[:alnum:]._#{COMBININGCHAR}#{EXTENDER}]*"
|
||||
QNAME_STR= "(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})"
|
||||
QNAME = /(#{QNAME_STR})/
|
||||
|
||||
# Just for backward compatibility. For example, kramdown uses this.
|
||||
# It's not used in REXML.
|
||||
UNAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
|
||||
|
||||
NAMECHAR = '[\-\w\.:]'
|
||||
NAME = "([\\w:]#{NAMECHAR}*)"
|
||||
NMTOKEN = "(?:#{NAMECHAR})+"
|
||||
NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*"
|
||||
REFERENCE = "&(?:#{NAME};|#\\d+;|#x[0-9a-fA-F]+;)"
|
||||
REFERENCE_RE = /#{REFERENCE}/
|
||||
|
||||
DOCTYPE_START = /\A\s*<!DOCTYPE\s/um
|
||||
DOCTYPE_END = /\A\s*\]\s*>/um
|
||||
DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
|
||||
ATTRIBUTE_PATTERN = /\s*(#{QNAME_STR})\s*=\s*(["'])(.*?)\4/um
|
||||
COMMENT_START = /\A<!--/u
|
||||
COMMENT_PATTERN = /<!--(.*?)-->/um
|
||||
CDATA_START = /\A<!\[CDATA\[/u
|
||||
CDATA_END = /\A\s*\]\s*>/um
|
||||
CDATA_PATTERN = /<!\[CDATA\[(.*?)\]\]>/um
|
||||
XMLDECL_START = /\A<\?xml\s/u;
|
||||
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>/um
|
||||
INSTRUCTION_START = /\A<\?/u
|
||||
INSTRUCTION_PATTERN = /<\?#{NAME}(\s+.*?)?\?>/um
|
||||
TAG_MATCH = /^<((?>#{QNAME_STR}))/um
|
||||
CLOSE_MATCH = /^\s*<\/(#{QNAME_STR})\s*>/um
|
||||
|
||||
VERSION = /\bversion\s*=\s*["'](.*?)['"]/um
|
||||
ENCODING = /\bencoding\s*=\s*["'](.*?)['"]/um
|
||||
STANDALONE = /\bstandalone\s*=\s*["'](.*?)['"]/um
|
||||
|
||||
ENTITY_START = /\A\s*<!ENTITY/
|
||||
IDENTITY = /^([!\*\w\-]+)(\s+#{NCNAME_STR})?(\s+["'](.*?)['"])?(\s+['"](.*?)["'])?/u
|
||||
ELEMENTDECL_START = /\A\s*<!ELEMENT/um
|
||||
ELEMENTDECL_PATTERN = /\A\s*(<!ELEMENT.*?)>/um
|
||||
SYSTEMENTITY = /\A\s*(%.*?;)\s*$/um
|
||||
ENUMERATION = "\\(\\s*#{NMTOKEN}(?:\\s*\\|\\s*#{NMTOKEN})*\\s*\\)"
|
||||
NOTATIONTYPE = "NOTATION\\s+\\(\\s*#{NAME}(?:\\s*\\|\\s*#{NAME})*\\s*\\)"
|
||||
ENUMERATEDTYPE = "(?:(?:#{NOTATIONTYPE})|(?:#{ENUMERATION}))"
|
||||
ATTTYPE = "(CDATA|ID|IDREF|IDREFS|ENTITY|ENTITIES|NMTOKEN|NMTOKENS|#{ENUMERATEDTYPE})"
|
||||
ATTVALUE = "(?:\"((?:[^<&\"]|#{REFERENCE})*)\")|(?:'((?:[^<&']|#{REFERENCE})*)')"
|
||||
DEFAULTDECL = "(#REQUIRED|#IMPLIED|(?:(#FIXED\\s+)?#{ATTVALUE}))"
|
||||
ATTDEF = "\\s+#{NAME}\\s+#{ATTTYPE}\\s+#{DEFAULTDECL}"
|
||||
ATTDEF_RE = /#{ATTDEF}/
|
||||
ATTLISTDECL_START = /\A\s*<!ATTLIST/um
|
||||
ATTLISTDECL_PATTERN = /\A\s*<!ATTLIST\s+#{NAME}(?:#{ATTDEF})*\s*>/um
|
||||
NOTATIONDECL_START = /\A\s*<!NOTATION/um
|
||||
PUBLIC = /\A\s*<!NOTATION\s+(\w[\-\w]*)\s+(PUBLIC)\s+(["'])(.*?)\3(?:\s+(["'])(.*?)\5)?\s*>/um
|
||||
SYSTEM = /\A\s*<!NOTATION\s+(\w[\-\w]*)\s+(SYSTEM)\s+(["'])(.*?)\3\s*>/um
|
||||
|
||||
TEXT_PATTERN = /\A([^<]*)/um
|
||||
|
||||
# Entity constants
|
||||
PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#"
|
||||
SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))}
|
||||
PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')}
|
||||
EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))"
|
||||
NDATADECL = "\\s+NDATA\\s+#{NAME}"
|
||||
PEREFERENCE = "%#{NAME};"
|
||||
ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))}
|
||||
PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})"
|
||||
ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))"
|
||||
PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
|
||||
GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
|
||||
ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
|
||||
|
||||
EREFERENCE = /&(?!#{NAME};)/
|
||||
|
||||
DEFAULT_ENTITIES = {
|
||||
'gt' => [/>/, '>', '>', />/],
|
||||
'lt' => [/</, '<', '<', /</],
|
||||
'quot' => [/"/, '"', '"', /"/],
|
||||
"apos" => [/'/, "'", "'", /'/]
|
||||
}
|
||||
|
||||
def initialize( source )
|
||||
self.stream = source
|
||||
@listeners = []
|
||||
end
|
||||
|
||||
def add_listener( listener )
|
||||
@listeners << listener
|
||||
end
|
||||
|
||||
attr_reader :source
|
||||
|
||||
def stream=( source )
|
||||
@source = SourceFactory.create_from( source )
|
||||
@closed = nil
|
||||
@document_status = nil
|
||||
@tags = []
|
||||
@stack = []
|
||||
@entities = []
|
||||
@nsstack = []
|
||||
end
|
||||
|
||||
def position
|
||||
if @source.respond_to? :position
|
||||
@source.position
|
||||
else
|
||||
# FIXME
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if there are no more events
|
||||
def empty?
|
||||
return (@source.empty? and @stack.empty?)
|
||||
end
|
||||
|
||||
# Returns true if there are more events. Synonymous with !empty?
|
||||
def has_next?
|
||||
return !(@source.empty? and @stack.empty?)
|
||||
end
|
||||
|
||||
# Push an event back on the head of the stream. This method
|
||||
# has (theoretically) infinite depth.
|
||||
def unshift token
|
||||
@stack.unshift(token)
|
||||
end
|
||||
|
||||
# Peek at the +depth+ event in the stack. The first element on the stack
|
||||
# is at depth 0. If +depth+ is -1, will parse to the end of the input
|
||||
# stream and return the last event, which is always :end_document.
|
||||
# Be aware that this causes the stream to be parsed up to the +depth+
|
||||
# event, so you can effectively pre-parse the entire document (pull the
|
||||
# entire thing into memory) using this method.
|
||||
def peek depth=0
|
||||
raise %Q[Illegal argument "#{depth}"] if depth < -1
|
||||
temp = []
|
||||
if depth == -1
|
||||
temp.push(pull()) until empty?
|
||||
else
|
||||
while @stack.size+temp.size < depth+1
|
||||
temp.push(pull())
|
||||
end
|
||||
end
|
||||
@stack += temp if temp.size > 0
|
||||
@stack[depth]
|
||||
end
|
||||
|
||||
# Returns the next event. This is a +PullEvent+ object.
|
||||
def pull
|
||||
pull_event.tap do |event|
|
||||
@listeners.each do |listener|
|
||||
listener.receive event
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pull_event
|
||||
if @closed
|
||||
x, @closed = @closed, nil
|
||||
return [ :end_element, x ]
|
||||
end
|
||||
return [ :end_document ] if empty?
|
||||
return @stack.shift if @stack.size > 0
|
||||
#STDERR.puts @source.encoding
|
||||
@source.read if @source.buffer.size<2
|
||||
#STDERR.puts "BUFFER = #{@source.buffer.inspect}"
|
||||
if @document_status == nil
|
||||
#@source.consume( /^\s*/um )
|
||||
word = @source.match( /^((?:\s+)|(?:<[^>]*>))/um )
|
||||
word = word[1] unless word.nil?
|
||||
#STDERR.puts "WORD = #{word.inspect}"
|
||||
case word
|
||||
when COMMENT_START
|
||||
return [ :comment, @source.match( COMMENT_PATTERN, true )[1] ]
|
||||
when XMLDECL_START
|
||||
#STDERR.puts "XMLDECL"
|
||||
results = @source.match( XMLDECL_PATTERN, true )[1]
|
||||
version = VERSION.match( results )
|
||||
version = version[1] unless version.nil?
|
||||
encoding = ENCODING.match(results)
|
||||
encoding = encoding[1] unless encoding.nil?
|
||||
if need_source_encoding_update?(encoding)
|
||||
@source.encoding = encoding
|
||||
end
|
||||
if encoding.nil? and /\AUTF-16(?:BE|LE)\z/i =~ @source.encoding
|
||||
encoding = "UTF-16"
|
||||
end
|
||||
standalone = STANDALONE.match(results)
|
||||
standalone = standalone[1] unless standalone.nil?
|
||||
return [ :xmldecl, version, encoding, standalone ]
|
||||
when INSTRUCTION_START
|
||||
return process_instruction
|
||||
when DOCTYPE_START
|
||||
md = @source.match( DOCTYPE_PATTERN, true )
|
||||
@nsstack.unshift(curr_ns=Set.new)
|
||||
identity = md[1]
|
||||
close = md[2]
|
||||
identity =~ IDENTITY
|
||||
name = $1
|
||||
raise REXML::ParseException.new("DOCTYPE is missing a name") if name.nil?
|
||||
pub_sys = $2.nil? ? nil : $2.strip
|
||||
long_name = $4.nil? ? nil : $4.strip
|
||||
uri = $6.nil? ? nil : $6.strip
|
||||
args = [ :start_doctype, name, pub_sys, long_name, uri ]
|
||||
if close == ">"
|
||||
@document_status = :after_doctype
|
||||
@source.read if @source.buffer.size<2
|
||||
md = @source.match(/^\s*/um, true)
|
||||
@stack << [ :end_doctype ]
|
||||
else
|
||||
@document_status = :in_doctype
|
||||
end
|
||||
return args
|
||||
when /^\s+/
|
||||
else
|
||||
@document_status = :after_doctype
|
||||
@source.read if @source.buffer.size<2
|
||||
md = @source.match(/\s*/um, true)
|
||||
if @source.encoding == "UTF-8"
|
||||
@source.buffer.force_encoding(::Encoding::UTF_8)
|
||||
end
|
||||
end
|
||||
end
|
||||
if @document_status == :in_doctype
|
||||
md = @source.match(/\s*(.*?>)/um)
|
||||
case md[1]
|
||||
when SYSTEMENTITY
|
||||
match = @source.match( SYSTEMENTITY, true )[1]
|
||||
return [ :externalentity, match ]
|
||||
|
||||
when ELEMENTDECL_START
|
||||
return [ :elementdecl, @source.match( ELEMENTDECL_PATTERN, true )[1] ]
|
||||
|
||||
when ENTITY_START
|
||||
match = @source.match( ENTITYDECL, true ).to_a.compact
|
||||
match[0] = :entitydecl
|
||||
ref = false
|
||||
if match[1] == '%'
|
||||
ref = true
|
||||
match.delete_at 1
|
||||
end
|
||||
# Now we have to sort out what kind of entity reference this is
|
||||
if match[2] == 'SYSTEM'
|
||||
# External reference
|
||||
match[3] = match[3][1..-2] # PUBID
|
||||
match.delete_at(4) if match.size > 4 # Chop out NDATA decl
|
||||
# match is [ :entity, name, SYSTEM, pubid(, ndata)? ]
|
||||
elsif match[2] == 'PUBLIC'
|
||||
# External reference
|
||||
match[3] = match[3][1..-2] # PUBID
|
||||
match[4] = match[4][1..-2] # HREF
|
||||
match.delete_at(5) if match.size > 5 # Chop out NDATA decl
|
||||
# match is [ :entity, name, PUBLIC, pubid, href(, ndata)? ]
|
||||
else
|
||||
match[2] = match[2][1..-2]
|
||||
match.pop if match.size == 4
|
||||
# match is [ :entity, name, value ]
|
||||
end
|
||||
match << '%' if ref
|
||||
return match
|
||||
when ATTLISTDECL_START
|
||||
md = @source.match( ATTLISTDECL_PATTERN, true )
|
||||
raise REXML::ParseException.new( "Bad ATTLIST declaration!", @source ) if md.nil?
|
||||
element = md[1]
|
||||
contents = md[0]
|
||||
|
||||
pairs = {}
|
||||
values = md[0].scan( ATTDEF_RE )
|
||||
values.each do |attdef|
|
||||
unless attdef[3] == "#IMPLIED"
|
||||
attdef.compact!
|
||||
val = attdef[3]
|
||||
val = attdef[4] if val == "#FIXED "
|
||||
pairs[attdef[0]] = val
|
||||
if attdef[0] =~ /^xmlns:(.*)/
|
||||
@nsstack[0] << $1
|
||||
end
|
||||
end
|
||||
end
|
||||
return [ :attlistdecl, element, pairs, contents ]
|
||||
when NOTATIONDECL_START
|
||||
md = nil
|
||||
if @source.match( PUBLIC )
|
||||
md = @source.match( PUBLIC, true )
|
||||
vals = [md[1],md[2],md[4],md[6]]
|
||||
elsif @source.match( SYSTEM )
|
||||
md = @source.match( SYSTEM, true )
|
||||
vals = [md[1],md[2],nil,md[4]]
|
||||
else
|
||||
raise REXML::ParseException.new( "error parsing notation: no matching pattern", @source )
|
||||
end
|
||||
return [ :notationdecl, *vals ]
|
||||
when DOCTYPE_END
|
||||
@document_status = :after_doctype
|
||||
@source.match( DOCTYPE_END, true )
|
||||
return [ :end_doctype ]
|
||||
end
|
||||
end
|
||||
begin
|
||||
if @source.buffer[0] == ?<
|
||||
if @source.buffer[1] == ?/
|
||||
@nsstack.shift
|
||||
last_tag = @tags.pop
|
||||
md = @source.match( CLOSE_MATCH, true )
|
||||
if md and !last_tag
|
||||
message = "Unexpected top-level end tag (got '#{md[1]}')"
|
||||
raise REXML::ParseException.new(message, @source)
|
||||
end
|
||||
if md.nil? or last_tag != md[1]
|
||||
message = "Missing end tag for '#{last_tag}'"
|
||||
message << " (got '#{md[1]}')" if md
|
||||
raise REXML::ParseException.new(message, @source)
|
||||
end
|
||||
return [ :end_element, last_tag ]
|
||||
elsif @source.buffer[1] == ?!
|
||||
md = @source.match(/\A(\s*[^>]*>)/um)
|
||||
#STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}"
|
||||
raise REXML::ParseException.new("Malformed node", @source) unless md
|
||||
if md[0][2] == ?-
|
||||
md = @source.match( COMMENT_PATTERN, true )
|
||||
|
||||
case md[1]
|
||||
when /--/, /-\z/
|
||||
raise REXML::ParseException.new("Malformed comment", @source)
|
||||
end
|
||||
|
||||
return [ :comment, md[1] ] if md
|
||||
else
|
||||
md = @source.match( CDATA_PATTERN, true )
|
||||
return [ :cdata, md[1] ] if md
|
||||
end
|
||||
raise REXML::ParseException.new( "Declarations can only occur "+
|
||||
"in the doctype declaration.", @source)
|
||||
elsif @source.buffer[1] == ??
|
||||
return process_instruction
|
||||
else
|
||||
# Get the next tag
|
||||
md = @source.match(TAG_MATCH, true)
|
||||
unless md
|
||||
raise REXML::ParseException.new("malformed XML: missing tag start", @source)
|
||||
end
|
||||
prefixes = Set.new
|
||||
prefixes << md[2] if md[2]
|
||||
@nsstack.unshift(curr_ns=Set.new)
|
||||
attributes, closed = parse_attributes(prefixes, curr_ns)
|
||||
# Verify that all of the prefixes have been defined
|
||||
for prefix in prefixes
|
||||
unless @nsstack.find{|k| k.member?(prefix)}
|
||||
raise UndefinedNamespaceException.new(prefix,@source,self)
|
||||
end
|
||||
end
|
||||
|
||||
if closed
|
||||
@closed = md[1]
|
||||
@nsstack.shift
|
||||
else
|
||||
@tags.push( md[1] )
|
||||
end
|
||||
return [ :start_element, md[1], attributes ]
|
||||
end
|
||||
else
|
||||
md = @source.match( TEXT_PATTERN, true )
|
||||
if md[0].length == 0
|
||||
@source.match( /(\s+)/, true )
|
||||
end
|
||||
#STDERR.puts "GOT #{md[1].inspect}" unless md[0].length == 0
|
||||
#return [ :text, "" ] if md[0].length == 0
|
||||
# unnormalized = Text::unnormalize( md[1], self )
|
||||
# return PullEvent.new( :text, md[1], unnormalized )
|
||||
return [ :text, md[1] ]
|
||||
end
|
||||
rescue REXML::UndefinedNamespaceException
|
||||
raise
|
||||
rescue REXML::ParseException
|
||||
raise
|
||||
rescue => error
|
||||
raise REXML::ParseException.new( "Exception parsing",
|
||||
@source, self, (error ? error : $!) )
|
||||
end
|
||||
return [ :dummy ]
|
||||
end
|
||||
private :pull_event
|
||||
|
||||
def entity( reference, entities )
|
||||
value = nil
|
||||
value = entities[ reference ] if entities
|
||||
if not value
|
||||
value = DEFAULT_ENTITIES[ reference ]
|
||||
value = value[2] if value
|
||||
end
|
||||
unnormalize( value, entities ) if value
|
||||
end
|
||||
|
||||
# Escapes all possible entities
|
||||
def normalize( input, entities=nil, entity_filter=nil )
|
||||
copy = input.clone
|
||||
# Doing it like this rather than in a loop improves the speed
|
||||
copy.gsub!( EREFERENCE, '&' )
|
||||
entities.each do |key, value|
|
||||
copy.gsub!( value, "&#{key};" ) unless entity_filter and
|
||||
entity_filter.include?(entity)
|
||||
end if entities
|
||||
copy.gsub!( EREFERENCE, '&' )
|
||||
DEFAULT_ENTITIES.each do |key, value|
|
||||
copy.gsub!( value[3], value[1] )
|
||||
end
|
||||
copy
|
||||
end
|
||||
|
||||
# Unescapes all possible entities
|
||||
def unnormalize( string, entities=nil, filter=nil )
|
||||
rv = string.clone
|
||||
rv.gsub!( /\r\n?/, "\n" )
|
||||
matches = rv.scan( REFERENCE_RE )
|
||||
return rv if matches.size == 0
|
||||
rv.gsub!( /�*((?:\d+)|(?:x[a-fA-F0-9]+));/ ) {
|
||||
m=$1
|
||||
m = "0#{m}" if m[0] == ?x
|
||||
[Integer(m)].pack('U*')
|
||||
}
|
||||
matches.collect!{|x|x[0]}.compact!
|
||||
if matches.size > 0
|
||||
matches.each do |entity_reference|
|
||||
unless filter and filter.include?(entity_reference)
|
||||
entity_value = entity( entity_reference, entities )
|
||||
if entity_value
|
||||
re = /&#{entity_reference};/
|
||||
rv.gsub!( re, entity_value )
|
||||
else
|
||||
er = DEFAULT_ENTITIES[entity_reference]
|
||||
rv.gsub!( er[0], er[2] ) if er
|
||||
end
|
||||
end
|
||||
end
|
||||
rv.gsub!( /&/, '&' )
|
||||
end
|
||||
rv
|
||||
end
|
||||
|
||||
private
|
||||
def need_source_encoding_update?(xml_declaration_encoding)
|
||||
return false if xml_declaration_encoding.nil?
|
||||
return false if /\AUTF-16\z/i =~ xml_declaration_encoding
|
||||
true
|
||||
end
|
||||
|
||||
def process_instruction
|
||||
match_data = @source.match(INSTRUCTION_PATTERN, true)
|
||||
unless match_data
|
||||
message = "Invalid processing instruction node"
|
||||
raise REXML::ParseException.new(message, @source)
|
||||
end
|
||||
[:processing_instruction, match_data[1], match_data[2]]
|
||||
end
|
||||
|
||||
def parse_attributes(prefixes, curr_ns)
|
||||
attributes = {}
|
||||
closed = false
|
||||
match_data = @source.match(/^(.*?)(\/)?>/um, true)
|
||||
if match_data.nil?
|
||||
message = "Start tag isn't ended"
|
||||
raise REXML::ParseException.new(message, @source)
|
||||
end
|
||||
|
||||
raw_attributes = match_data[1]
|
||||
closed = !match_data[2].nil?
|
||||
return attributes, closed if raw_attributes.nil?
|
||||
return attributes, closed if raw_attributes.empty?
|
||||
|
||||
scanner = StringScanner.new(raw_attributes)
|
||||
until scanner.eos?
|
||||
if scanner.scan(/\s+/)
|
||||
break if scanner.eos?
|
||||
end
|
||||
|
||||
pos = scanner.pos
|
||||
loop do
|
||||
break if scanner.scan(ATTRIBUTE_PATTERN)
|
||||
unless scanner.scan(QNAME)
|
||||
message = "Invalid attribute name: <#{scanner.rest}>"
|
||||
raise REXML::ParseException.new(message, @source)
|
||||
end
|
||||
name = scanner[0]
|
||||
unless scanner.scan(/\s*=\s*/um)
|
||||
message = "Missing attribute equal: <#{name}>"
|
||||
raise REXML::ParseException.new(message, @source)
|
||||
end
|
||||
quote = scanner.scan(/['"]/)
|
||||
unless quote
|
||||
message = "Missing attribute value start quote: <#{name}>"
|
||||
raise REXML::ParseException.new(message, @source)
|
||||
end
|
||||
unless scanner.scan(/.*#{Regexp.escape(quote)}/um)
|
||||
match_data = @source.match(/^(.*?)(\/)?>/um, true)
|
||||
if match_data
|
||||
scanner << "/" if closed
|
||||
scanner << ">"
|
||||
scanner << match_data[1]
|
||||
scanner.pos = pos
|
||||
closed = !match_data[2].nil?
|
||||
next
|
||||
end
|
||||
message =
|
||||
"Missing attribute value end quote: <#{name}>: <#{quote}>"
|
||||
raise REXML::ParseException.new(message, @source)
|
||||
end
|
||||
end
|
||||
name = scanner[1]
|
||||
prefix = scanner[2]
|
||||
local_part = scanner[3]
|
||||
# quote = scanner[4]
|
||||
value = scanner[5]
|
||||
if prefix == "xmlns"
|
||||
if local_part == "xml"
|
||||
if value != "http://www.w3.org/XML/1998/namespace"
|
||||
msg = "The 'xml' prefix must not be bound to any other namespace "+
|
||||
"(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
|
||||
raise REXML::ParseException.new( msg, @source, self )
|
||||
end
|
||||
elsif local_part == "xmlns"
|
||||
msg = "The 'xmlns' prefix must not be declared "+
|
||||
"(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
|
||||
raise REXML::ParseException.new( msg, @source, self)
|
||||
end
|
||||
curr_ns << local_part
|
||||
elsif prefix
|
||||
prefixes << prefix unless prefix == "xml"
|
||||
end
|
||||
|
||||
if attributes.has_key?(name)
|
||||
msg = "Duplicate attribute #{name.inspect}"
|
||||
raise REXML::ParseException.new(msg, @source, self)
|
||||
end
|
||||
|
||||
attributes[name] = value
|
||||
end
|
||||
return attributes, closed
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
case event[0]
|
||||
when :start_element
|
||||
when :text
|
||||
when :end_element
|
||||
when :processing_instruction
|
||||
when :cdata
|
||||
when :comment
|
||||
when :xmldecl
|
||||
when :start_doctype
|
||||
when :end_doctype
|
||||
when :externalentity
|
||||
when :elementdecl
|
||||
when :entity
|
||||
when :attlistdecl
|
||||
when :notationdecl
|
||||
when :end_doctype
|
||||
end
|
||||
=end
|
|
@ -1,59 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'streamparser'
|
||||
require_relative 'baseparser'
|
||||
require_relative '../light/node'
|
||||
|
||||
module REXML
|
||||
module Parsers
|
||||
class LightParser
|
||||
def initialize stream
|
||||
@stream = stream
|
||||
@parser = REXML::Parsers::BaseParser.new( stream )
|
||||
end
|
||||
|
||||
def add_listener( listener )
|
||||
@parser.add_listener( listener )
|
||||
end
|
||||
|
||||
def rewind
|
||||
@stream.rewind
|
||||
@parser.stream = @stream
|
||||
end
|
||||
|
||||
def parse
|
||||
root = context = [ :document ]
|
||||
while true
|
||||
event = @parser.pull
|
||||
case event[0]
|
||||
when :end_document
|
||||
break
|
||||
when :start_element, :start_doctype
|
||||
new_node = event
|
||||
context << new_node
|
||||
new_node[1,0] = [context]
|
||||
context = new_node
|
||||
when :end_element, :end_doctype
|
||||
context = context[1]
|
||||
else
|
||||
new_node = event
|
||||
context << new_node
|
||||
new_node[1,0] = [context]
|
||||
end
|
||||
end
|
||||
root
|
||||
end
|
||||
end
|
||||
|
||||
# An element is an array. The array contains:
|
||||
# 0 The parent element
|
||||
# 1 The tag name
|
||||
# 2 A hash of attributes
|
||||
# 3..-1 The child elements
|
||||
# An element is an array of size > 3
|
||||
# Text is a String
|
||||
# PIs are [ :processing_instruction, target, data ]
|
||||
# Comments are [ :comment, data ]
|
||||
# DocTypes are DocType structs
|
||||
# The root is an array with XMLDecls, Text, DocType, Array, Text
|
||||
end
|
||||
end
|
|
@ -1,197 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require 'forwardable'
|
||||
|
||||
require_relative '../parseexception'
|
||||
require_relative 'baseparser'
|
||||
require_relative '../xmltokens'
|
||||
|
||||
module REXML
|
||||
module Parsers
|
||||
# = Using the Pull Parser
|
||||
# <em>This API is experimental, and subject to change.</em>
|
||||
# parser = PullParser.new( "<a>text<b att='val'/>txet</a>" )
|
||||
# while parser.has_next?
|
||||
# res = parser.next
|
||||
# puts res[1]['att'] if res.start_tag? and res[0] == 'b'
|
||||
# end
|
||||
# See the PullEvent class for information on the content of the results.
|
||||
# The data is identical to the arguments passed for the various events to
|
||||
# the StreamListener API.
|
||||
#
|
||||
# Notice that:
|
||||
# parser = PullParser.new( "<a>BAD DOCUMENT" )
|
||||
# while parser.has_next?
|
||||
# res = parser.next
|
||||
# raise res[1] if res.error?
|
||||
# end
|
||||
#
|
||||
# Nat Price gave me some good ideas for the API.
|
||||
class PullParser
|
||||
include XMLTokens
|
||||
extend Forwardable
|
||||
|
||||
def_delegators( :@parser, :has_next? )
|
||||
def_delegators( :@parser, :entity )
|
||||
def_delegators( :@parser, :empty? )
|
||||
def_delegators( :@parser, :source )
|
||||
|
||||
def initialize stream
|
||||
@entities = {}
|
||||
@listeners = nil
|
||||
@parser = BaseParser.new( stream )
|
||||
@my_stack = []
|
||||
end
|
||||
|
||||
def add_listener( listener )
|
||||
@listeners = [] unless @listeners
|
||||
@listeners << listener
|
||||
end
|
||||
|
||||
def each
|
||||
while has_next?
|
||||
yield self.pull
|
||||
end
|
||||
end
|
||||
|
||||
def peek depth=0
|
||||
if @my_stack.length <= depth
|
||||
(depth - @my_stack.length + 1).times {
|
||||
e = PullEvent.new(@parser.pull)
|
||||
@my_stack.push(e)
|
||||
}
|
||||
end
|
||||
@my_stack[depth]
|
||||
end
|
||||
|
||||
def pull
|
||||
return @my_stack.shift if @my_stack.length > 0
|
||||
|
||||
event = @parser.pull
|
||||
case event[0]
|
||||
when :entitydecl
|
||||
@entities[ event[1] ] =
|
||||
event[2] unless event[2] =~ /PUBLIC|SYSTEM/
|
||||
when :text
|
||||
unnormalized = @parser.unnormalize( event[1], @entities )
|
||||
event << unnormalized
|
||||
end
|
||||
PullEvent.new( event )
|
||||
end
|
||||
|
||||
def unshift token
|
||||
@my_stack.unshift token
|
||||
end
|
||||
end
|
||||
|
||||
# A parsing event. The contents of the event are accessed as an +Array?,
|
||||
# and the type is given either by the ...? methods, or by accessing the
|
||||
# +type+ accessor. The contents of this object vary from event to event,
|
||||
# but are identical to the arguments passed to +StreamListener+s for each
|
||||
# event.
|
||||
class PullEvent
|
||||
# The type of this event. Will be one of :tag_start, :tag_end, :text,
|
||||
# :processing_instruction, :comment, :doctype, :attlistdecl, :entitydecl,
|
||||
# :notationdecl, :entity, :cdata, :xmldecl, or :error.
|
||||
def initialize(arg)
|
||||
@contents = arg
|
||||
end
|
||||
|
||||
def []( start, endd=nil)
|
||||
if start.kind_of? Range
|
||||
@contents.slice( start.begin+1 .. start.end )
|
||||
elsif start.kind_of? Numeric
|
||||
if endd.nil?
|
||||
@contents.slice( start+1 )
|
||||
else
|
||||
@contents.slice( start+1, endd )
|
||||
end
|
||||
else
|
||||
raise "Illegal argument #{start.inspect} (#{start.class})"
|
||||
end
|
||||
end
|
||||
|
||||
def event_type
|
||||
@contents[0]
|
||||
end
|
||||
|
||||
# Content: [ String tag_name, Hash attributes ]
|
||||
def start_element?
|
||||
@contents[0] == :start_element
|
||||
end
|
||||
|
||||
# Content: [ String tag_name ]
|
||||
def end_element?
|
||||
@contents[0] == :end_element
|
||||
end
|
||||
|
||||
# Content: [ String raw_text, String unnormalized_text ]
|
||||
def text?
|
||||
@contents[0] == :text
|
||||
end
|
||||
|
||||
# Content: [ String text ]
|
||||
def instruction?
|
||||
@contents[0] == :processing_instruction
|
||||
end
|
||||
|
||||
# Content: [ String text ]
|
||||
def comment?
|
||||
@contents[0] == :comment
|
||||
end
|
||||
|
||||
# Content: [ String name, String pub_sys, String long_name, String uri ]
|
||||
def doctype?
|
||||
@contents[0] == :start_doctype
|
||||
end
|
||||
|
||||
# Content: [ String text ]
|
||||
def attlistdecl?
|
||||
@contents[0] == :attlistdecl
|
||||
end
|
||||
|
||||
# Content: [ String text ]
|
||||
def elementdecl?
|
||||
@contents[0] == :elementdecl
|
||||
end
|
||||
|
||||
# Due to the wonders of DTDs, an entity declaration can be just about
|
||||
# anything. There's no way to normalize it; you'll have to interpret the
|
||||
# content yourself. However, the following is true:
|
||||
#
|
||||
# * If the entity declaration is an internal entity:
|
||||
# [ String name, String value ]
|
||||
# Content: [ String text ]
|
||||
def entitydecl?
|
||||
@contents[0] == :entitydecl
|
||||
end
|
||||
|
||||
# Content: [ String text ]
|
||||
def notationdecl?
|
||||
@contents[0] == :notationdecl
|
||||
end
|
||||
|
||||
# Content: [ String text ]
|
||||
def entity?
|
||||
@contents[0] == :entity
|
||||
end
|
||||
|
||||
# Content: [ String text ]
|
||||
def cdata?
|
||||
@contents[0] == :cdata
|
||||
end
|
||||
|
||||
# Content: [ String version, String encoding, String standalone ]
|
||||
def xmldecl?
|
||||
@contents[0] == :xmldecl
|
||||
end
|
||||
|
||||
def error?
|
||||
@contents[0] == :error
|
||||
end
|
||||
|
||||
def inspect
|
||||
@contents[0].to_s + ": " + @contents[1..-1].inspect
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,273 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'baseparser'
|
||||
require_relative '../parseexception'
|
||||
require_relative '../namespace'
|
||||
require_relative '../text'
|
||||
|
||||
module REXML
|
||||
module Parsers
|
||||
# SAX2Parser
|
||||
class SAX2Parser
|
||||
def initialize source
|
||||
@parser = BaseParser.new(source)
|
||||
@listeners = []
|
||||
@procs = []
|
||||
@namespace_stack = []
|
||||
@has_listeners = false
|
||||
@tag_stack = []
|
||||
@entities = {}
|
||||
end
|
||||
|
||||
def source
|
||||
@parser.source
|
||||
end
|
||||
|
||||
def add_listener( listener )
|
||||
@parser.add_listener( listener )
|
||||
end
|
||||
|
||||
# Listen arguments:
|
||||
#
|
||||
# Symbol, Array, Block
|
||||
# Listen to Symbol events on Array elements
|
||||
# Symbol, Block
|
||||
# Listen to Symbol events
|
||||
# Array, Listener
|
||||
# Listen to all events on Array elements
|
||||
# Array, Block
|
||||
# Listen to :start_element events on Array elements
|
||||
# Listener
|
||||
# Listen to All events
|
||||
#
|
||||
# Symbol can be one of: :start_element, :end_element,
|
||||
# :start_prefix_mapping, :end_prefix_mapping, :characters,
|
||||
# :processing_instruction, :doctype, :attlistdecl, :elementdecl,
|
||||
# :entitydecl, :notationdecl, :cdata, :xmldecl, :comment
|
||||
#
|
||||
# There is an additional symbol that can be listened for: :progress.
|
||||
# This will be called for every event generated, passing in the current
|
||||
# stream position.
|
||||
#
|
||||
# Array contains regular expressions or strings which will be matched
|
||||
# against fully qualified element names.
|
||||
#
|
||||
# Listener must implement the methods in SAX2Listener
|
||||
#
|
||||
# Block will be passed the same arguments as a SAX2Listener method would
|
||||
# be, where the method name is the same as the matched Symbol.
|
||||
# See the SAX2Listener for more information.
|
||||
def listen( *args, &blok )
|
||||
if args[0].kind_of? Symbol
|
||||
if args.size == 2
|
||||
args[1].each { |match| @procs << [args[0], match, blok] }
|
||||
else
|
||||
add( [args[0], nil, blok] )
|
||||
end
|
||||
elsif args[0].kind_of? Array
|
||||
if args.size == 2
|
||||
args[0].each { |match| add( [nil, match, args[1]] ) }
|
||||
else
|
||||
args[0].each { |match| add( [ :start_element, match, blok ] ) }
|
||||
end
|
||||
else
|
||||
add([nil, nil, args[0]])
|
||||
end
|
||||
end
|
||||
|
||||
def deafen( listener=nil, &blok )
|
||||
if listener
|
||||
@listeners.delete_if {|item| item[-1] == listener }
|
||||
@has_listeners = false if @listeners.size == 0
|
||||
else
|
||||
@procs.delete_if {|item| item[-1] == blok }
|
||||
end
|
||||
end
|
||||
|
||||
def parse
|
||||
@procs.each { |sym,match,block| block.call if sym == :start_document }
|
||||
@listeners.each { |sym,match,block|
|
||||
block.start_document if sym == :start_document or sym.nil?
|
||||
}
|
||||
context = []
|
||||
while true
|
||||
event = @parser.pull
|
||||
case event[0]
|
||||
when :end_document
|
||||
handle( :end_document )
|
||||
break
|
||||
when :start_doctype
|
||||
handle( :doctype, *event[1..-1])
|
||||
when :end_doctype
|
||||
context = context[1]
|
||||
when :start_element
|
||||
@tag_stack.push(event[1])
|
||||
# find the observers for namespaces
|
||||
procs = get_procs( :start_prefix_mapping, event[1] )
|
||||
listeners = get_listeners( :start_prefix_mapping, event[1] )
|
||||
if procs or listeners
|
||||
# break out the namespace declarations
|
||||
# The attributes live in event[2]
|
||||
event[2].each {|n, v| event[2][n] = @parser.normalize(v)}
|
||||
nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ }
|
||||
nsdecl.collect! { |n, value| [ n[6..-1], value ] }
|
||||
@namespace_stack.push({})
|
||||
nsdecl.each do |n,v|
|
||||
@namespace_stack[-1][n] = v
|
||||
# notify observers of namespaces
|
||||
procs.each { |ob| ob.call( n, v ) } if procs
|
||||
listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners
|
||||
end
|
||||
end
|
||||
event[1] =~ Namespace::NAMESPLIT
|
||||
prefix = $1
|
||||
local = $2
|
||||
uri = get_namespace(prefix)
|
||||
# find the observers for start_element
|
||||
procs = get_procs( :start_element, event[1] )
|
||||
listeners = get_listeners( :start_element, event[1] )
|
||||
# notify observers
|
||||
procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs
|
||||
listeners.each { |ob|
|
||||
ob.start_element( uri, local, event[1], event[2] )
|
||||
} if listeners
|
||||
when :end_element
|
||||
@tag_stack.pop
|
||||
event[1] =~ Namespace::NAMESPLIT
|
||||
prefix = $1
|
||||
local = $2
|
||||
uri = get_namespace(prefix)
|
||||
# find the observers for start_element
|
||||
procs = get_procs( :end_element, event[1] )
|
||||
listeners = get_listeners( :end_element, event[1] )
|
||||
# notify observers
|
||||
procs.each { |ob| ob.call( uri, local, event[1] ) } if procs
|
||||
listeners.each { |ob|
|
||||
ob.end_element( uri, local, event[1] )
|
||||
} if listeners
|
||||
|
||||
namespace_mapping = @namespace_stack.pop
|
||||
# find the observers for namespaces
|
||||
procs = get_procs( :end_prefix_mapping, event[1] )
|
||||
listeners = get_listeners( :end_prefix_mapping, event[1] )
|
||||
if procs or listeners
|
||||
namespace_mapping.each do |ns_prefix, ns_uri|
|
||||
# notify observers of namespaces
|
||||
procs.each { |ob| ob.call( ns_prefix ) } if procs
|
||||
listeners.each { |ob| ob.end_prefix_mapping(ns_prefix) } if listeners
|
||||
end
|
||||
end
|
||||
when :text
|
||||
#normalized = @parser.normalize( event[1] )
|
||||
#handle( :characters, normalized )
|
||||
copy = event[1].clone
|
||||
|
||||
esub = proc { |match|
|
||||
if @entities.has_key?($1)
|
||||
@entities[$1].gsub(Text::REFERENCE, &esub)
|
||||
else
|
||||
match
|
||||
end
|
||||
}
|
||||
|
||||
copy.gsub!( Text::REFERENCE, &esub )
|
||||
copy.gsub!( Text::NUMERICENTITY ) {|m|
|
||||
m=$1
|
||||
m = "0#{m}" if m[0] == ?x
|
||||
[Integer(m)].pack('U*')
|
||||
}
|
||||
handle( :characters, copy )
|
||||
when :entitydecl
|
||||
handle_entitydecl( event )
|
||||
when :processing_instruction, :comment, :attlistdecl,
|
||||
:elementdecl, :cdata, :notationdecl, :xmldecl
|
||||
handle( *event )
|
||||
end
|
||||
handle( :progress, @parser.position )
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def handle( symbol, *arguments )
|
||||
tag = @tag_stack[-1]
|
||||
procs = get_procs( symbol, tag )
|
||||
listeners = get_listeners( symbol, tag )
|
||||
# notify observers
|
||||
procs.each { |ob| ob.call( *arguments ) } if procs
|
||||
listeners.each { |l|
|
||||
l.send( symbol.to_s, *arguments )
|
||||
} if listeners
|
||||
end
|
||||
|
||||
def handle_entitydecl( event )
|
||||
@entities[ event[1] ] = event[2] if event.size == 3
|
||||
parameter_reference_p = false
|
||||
case event[2]
|
||||
when "SYSTEM"
|
||||
if event.size == 5
|
||||
if event.last == "%"
|
||||
parameter_reference_p = true
|
||||
else
|
||||
event[4, 0] = "NDATA"
|
||||
end
|
||||
end
|
||||
when "PUBLIC"
|
||||
if event.size == 6
|
||||
if event.last == "%"
|
||||
parameter_reference_p = true
|
||||
else
|
||||
event[5, 0] = "NDATA"
|
||||
end
|
||||
end
|
||||
else
|
||||
parameter_reference_p = (event.size == 4)
|
||||
end
|
||||
event[1, 0] = event.pop if parameter_reference_p
|
||||
handle( event[0], event[1..-1] )
|
||||
end
|
||||
|
||||
# The following methods are duplicates, but it is faster than using
|
||||
# a helper
|
||||
def get_procs( symbol, name )
|
||||
return nil if @procs.size == 0
|
||||
@procs.find_all do |sym, match, block|
|
||||
(
|
||||
(sym.nil? or symbol == sym) and
|
||||
((name.nil? and match.nil?) or match.nil? or (
|
||||
(name == match) or
|
||||
(match.kind_of? Regexp and name =~ match)
|
||||
)
|
||||
)
|
||||
)
|
||||
end.collect{|x| x[-1]}
|
||||
end
|
||||
def get_listeners( symbol, name )
|
||||
return nil if @listeners.size == 0
|
||||
@listeners.find_all do |sym, match, block|
|
||||
(
|
||||
(sym.nil? or symbol == sym) and
|
||||
((name.nil? and match.nil?) or match.nil? or (
|
||||
(name == match) or
|
||||
(match.kind_of? Regexp and name =~ match)
|
||||
)
|
||||
)
|
||||
)
|
||||
end.collect{|x| x[-1]}
|
||||
end
|
||||
|
||||
def add( pair )
|
||||
if pair[-1].respond_to? :call
|
||||
@procs << pair unless @procs.include? pair
|
||||
else
|
||||
@listeners << pair unless @listeners.include? pair
|
||||
@has_listeners = true
|
||||
end
|
||||
end
|
||||
|
||||
def get_namespace( prefix )
|
||||
uris = (@namespace_stack.find_all { |ns| not ns[prefix].nil? }) ||
|
||||
(@namespace_stack.find { |ns| not ns[nil].nil? })
|
||||
uris[-1][prefix] unless uris.nil? or 0 == uris.size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,61 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "baseparser"
|
||||
|
||||
module REXML
|
||||
module Parsers
|
||||
class StreamParser
|
||||
def initialize source, listener
|
||||
@listener = listener
|
||||
@parser = BaseParser.new( source )
|
||||
@tag_stack = []
|
||||
end
|
||||
|
||||
def add_listener( listener )
|
||||
@parser.add_listener( listener )
|
||||
end
|
||||
|
||||
def parse
|
||||
# entity string
|
||||
while true
|
||||
event = @parser.pull
|
||||
case event[0]
|
||||
when :end_document
|
||||
unless @tag_stack.empty?
|
||||
tag_path = "/" + @tag_stack.join("/")
|
||||
raise ParseException.new("Missing end tag for '#{tag_path}'",
|
||||
@parser.source)
|
||||
end
|
||||
return
|
||||
when :start_element
|
||||
@tag_stack << event[1]
|
||||
attrs = event[2].each do |n, v|
|
||||
event[2][n] = @parser.unnormalize( v )
|
||||
end
|
||||
@listener.tag_start( event[1], attrs )
|
||||
when :end_element
|
||||
@listener.tag_end( event[1] )
|
||||
@tag_stack.pop
|
||||
when :text
|
||||
normalized = @parser.unnormalize( event[1] )
|
||||
@listener.text( normalized )
|
||||
when :processing_instruction
|
||||
@listener.instruction( *event[1,2] )
|
||||
when :start_doctype
|
||||
@listener.doctype( *event[1..-1] )
|
||||
when :end_doctype
|
||||
# FIXME: remove this condition for milestone:3.2
|
||||
@listener.doctype_end if @listener.respond_to? :doctype_end
|
||||
when :comment, :attlistdecl, :cdata, :xmldecl, :elementdecl
|
||||
@listener.send( event[0].to_s, *event[1..-1] )
|
||||
when :entitydecl, :notationdecl
|
||||
@listener.send( event[0].to_s, event[1..-1] )
|
||||
when :externalentity
|
||||
entity_reference = event[1]
|
||||
content = entity_reference.gsub(/\A%|;\z/, "")
|
||||
@listener.entity(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,101 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative '../validation/validationexception'
|
||||
require_relative '../undefinednamespaceexception'
|
||||
|
||||
module REXML
|
||||
module Parsers
|
||||
class TreeParser
|
||||
def initialize( source, build_context = Document.new )
|
||||
@build_context = build_context
|
||||
@parser = Parsers::BaseParser.new( source )
|
||||
end
|
||||
|
||||
def add_listener( listener )
|
||||
@parser.add_listener( listener )
|
||||
end
|
||||
|
||||
def parse
|
||||
tag_stack = []
|
||||
in_doctype = false
|
||||
entities = nil
|
||||
begin
|
||||
while true
|
||||
event = @parser.pull
|
||||
#STDERR.puts "TREEPARSER GOT #{event.inspect}"
|
||||
case event[0]
|
||||
when :end_document
|
||||
unless tag_stack.empty?
|
||||
raise ParseException.new("No close tag for #{@build_context.xpath}",
|
||||
@parser.source, @parser)
|
||||
end
|
||||
return
|
||||
when :start_element
|
||||
tag_stack.push(event[1])
|
||||
el = @build_context = @build_context.add_element( event[1] )
|
||||
event[2].each do |key, value|
|
||||
el.attributes[key]=Attribute.new(key,value,self)
|
||||
end
|
||||
when :end_element
|
||||
tag_stack.pop
|
||||
@build_context = @build_context.parent
|
||||
when :text
|
||||
if not in_doctype
|
||||
if @build_context[-1].instance_of? Text
|
||||
@build_context[-1] << event[1]
|
||||
else
|
||||
@build_context.add(
|
||||
Text.new(event[1], @build_context.whitespace, nil, true)
|
||||
) unless (
|
||||
@build_context.ignore_whitespace_nodes and
|
||||
event[1].strip.size==0
|
||||
)
|
||||
end
|
||||
end
|
||||
when :comment
|
||||
c = Comment.new( event[1] )
|
||||
@build_context.add( c )
|
||||
when :cdata
|
||||
c = CData.new( event[1] )
|
||||
@build_context.add( c )
|
||||
when :processing_instruction
|
||||
@build_context.add( Instruction.new( event[1], event[2] ) )
|
||||
when :end_doctype
|
||||
in_doctype = false
|
||||
entities.each { |k,v| entities[k] = @build_context.entities[k].value }
|
||||
@build_context = @build_context.parent
|
||||
when :start_doctype
|
||||
doctype = DocType.new( event[1..-1], @build_context )
|
||||
@build_context = doctype
|
||||
entities = {}
|
||||
in_doctype = true
|
||||
when :attlistdecl
|
||||
n = AttlistDecl.new( event[1..-1] )
|
||||
@build_context.add( n )
|
||||
when :externalentity
|
||||
n = ExternalEntity.new( event[1] )
|
||||
@build_context.add( n )
|
||||
when :elementdecl
|
||||
n = ElementDecl.new( event[1] )
|
||||
@build_context.add(n)
|
||||
when :entitydecl
|
||||
entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/
|
||||
@build_context.add(Entity.new(event))
|
||||
when :notationdecl
|
||||
n = NotationDecl.new( *event[1..-1] )
|
||||
@build_context.add( n )
|
||||
when :xmldecl
|
||||
x = XMLDecl.new( event[1], event[2], event[3] )
|
||||
@build_context.add( x )
|
||||
end
|
||||
end
|
||||
rescue REXML::Validation::ValidationException
|
||||
raise
|
||||
rescue REXML::ParseException
|
||||
raise
|
||||
rescue
|
||||
raise ParseException.new( $!.message, @parser.source, @parser, $! )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'streamparser'
|
||||
require_relative 'baseparser'
|
||||
|
||||
module REXML
|
||||
module Parsers
|
||||
class UltraLightParser
|
||||
def initialize stream
|
||||
@stream = stream
|
||||
@parser = REXML::Parsers::BaseParser.new( stream )
|
||||
end
|
||||
|
||||
def add_listener( listener )
|
||||
@parser.add_listener( listener )
|
||||
end
|
||||
|
||||
def rewind
|
||||
@stream.rewind
|
||||
@parser.stream = @stream
|
||||
end
|
||||
|
||||
def parse
|
||||
root = context = []
|
||||
while true
|
||||
event = @parser.pull
|
||||
case event[0]
|
||||
when :end_document
|
||||
break
|
||||
when :end_doctype
|
||||
context = context[1]
|
||||
when :start_element, :start_doctype
|
||||
context << event
|
||||
event[1,0] = [context]
|
||||
context = event
|
||||
when :end_element
|
||||
context = context[1]
|
||||
else
|
||||
context << event
|
||||
end
|
||||
end
|
||||
root
|
||||
end
|
||||
end
|
||||
|
||||
# An element is an array. The array contains:
|
||||
# 0 The parent element
|
||||
# 1 The tag name
|
||||
# 2 A hash of attributes
|
||||
# 3..-1 The child elements
|
||||
# An element is an array of size > 3
|
||||
# Text is a String
|
||||
# PIs are [ :processing_instruction, target, data ]
|
||||
# Comments are [ :comment, data ]
|
||||
# DocTypes are DocType structs
|
||||
# The root is an array with XMLDecls, Text, DocType, Array, Text
|
||||
end
|
||||
end
|
|
@ -1,675 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative '../namespace'
|
||||
require_relative '../xmltokens'
|
||||
|
||||
module REXML
|
||||
module Parsers
|
||||
# You don't want to use this class. Really. Use XPath, which is a wrapper
|
||||
# for this class. Believe me. You don't want to poke around in here.
|
||||
# There is strange, dark magic at work in this code. Beware. Go back! Go
|
||||
# back while you still can!
|
||||
class XPathParser
|
||||
include XMLTokens
|
||||
LITERAL = /^'([^']*)'|^"([^"]*)"/u
|
||||
|
||||
def namespaces=( namespaces )
|
||||
Functions::namespace_context = namespaces
|
||||
@namespaces = namespaces
|
||||
end
|
||||
|
||||
def parse path
|
||||
path = path.dup
|
||||
path.gsub!(/([\(\[])\s+/, '\1') # Strip ignorable spaces
|
||||
path.gsub!( /\s+([\]\)])/, '\1')
|
||||
parsed = []
|
||||
OrExpr(path, parsed)
|
||||
parsed
|
||||
end
|
||||
|
||||
def predicate path
|
||||
parsed = []
|
||||
Predicate( "[#{path}]", parsed )
|
||||
parsed
|
||||
end
|
||||
|
||||
def abbreviate( path )
|
||||
path = path.kind_of?(String) ? parse( path ) : path
|
||||
string = ""
|
||||
document = false
|
||||
while path.size > 0
|
||||
op = path.shift
|
||||
case op
|
||||
when :node
|
||||
when :attribute
|
||||
string << "/" if string.size > 0
|
||||
string << "@"
|
||||
when :child
|
||||
string << "/" if string.size > 0
|
||||
when :descendant_or_self
|
||||
string << "/"
|
||||
when :self
|
||||
string << "."
|
||||
when :parent
|
||||
string << ".."
|
||||
when :any
|
||||
string << "*"
|
||||
when :text
|
||||
string << "text()"
|
||||
when :following, :following_sibling,
|
||||
:ancestor, :ancestor_or_self, :descendant,
|
||||
:namespace, :preceding, :preceding_sibling
|
||||
string << "/" unless string.size == 0
|
||||
string << op.to_s.tr("_", "-")
|
||||
string << "::"
|
||||
when :qname
|
||||
prefix = path.shift
|
||||
name = path.shift
|
||||
string << prefix+":" if prefix.size > 0
|
||||
string << name
|
||||
when :predicate
|
||||
string << '['
|
||||
string << predicate_to_string( path.shift ) {|x| abbreviate( x ) }
|
||||
string << ']'
|
||||
when :document
|
||||
document = true
|
||||
when :function
|
||||
string << path.shift
|
||||
string << "( "
|
||||
string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )}
|
||||
string << " )"
|
||||
when :literal
|
||||
string << %Q{ "#{path.shift}" }
|
||||
else
|
||||
string << "/" unless string.size == 0
|
||||
string << "UNKNOWN("
|
||||
string << op.inspect
|
||||
string << ")"
|
||||
end
|
||||
end
|
||||
string = "/"+string if document
|
||||
return string
|
||||
end
|
||||
|
||||
def expand( path )
|
||||
path = path.kind_of?(String) ? parse( path ) : path
|
||||
string = ""
|
||||
document = false
|
||||
while path.size > 0
|
||||
op = path.shift
|
||||
case op
|
||||
when :node
|
||||
string << "node()"
|
||||
when :attribute, :child, :following, :following_sibling,
|
||||
:ancestor, :ancestor_or_self, :descendant, :descendant_or_self,
|
||||
:namespace, :preceding, :preceding_sibling, :self, :parent
|
||||
string << "/" unless string.size == 0
|
||||
string << op.to_s.tr("_", "-")
|
||||
string << "::"
|
||||
when :any
|
||||
string << "*"
|
||||
when :qname
|
||||
prefix = path.shift
|
||||
name = path.shift
|
||||
string << prefix+":" if prefix.size > 0
|
||||
string << name
|
||||
when :predicate
|
||||
string << '['
|
||||
string << predicate_to_string( path.shift ) { |x| expand(x) }
|
||||
string << ']'
|
||||
when :document
|
||||
document = true
|
||||
else
|
||||
string << "/" unless string.size == 0
|
||||
string << "UNKNOWN("
|
||||
string << op.inspect
|
||||
string << ")"
|
||||
end
|
||||
end
|
||||
string = "/"+string if document
|
||||
return string
|
||||
end
|
||||
|
||||
def predicate_to_string( path, &block )
|
||||
string = ""
|
||||
case path[0]
|
||||
when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :union
|
||||
op = path.shift
|
||||
case op
|
||||
when :eq
|
||||
op = "="
|
||||
when :lt
|
||||
op = "<"
|
||||
when :gt
|
||||
op = ">"
|
||||
when :lteq
|
||||
op = "<="
|
||||
when :gteq
|
||||
op = ">="
|
||||
when :neq
|
||||
op = "!="
|
||||
when :union
|
||||
op = "|"
|
||||
end
|
||||
left = predicate_to_string( path.shift, &block )
|
||||
right = predicate_to_string( path.shift, &block )
|
||||
string << " "
|
||||
string << left
|
||||
string << " "
|
||||
string << op.to_s
|
||||
string << " "
|
||||
string << right
|
||||
string << " "
|
||||
when :function
|
||||
path.shift
|
||||
name = path.shift
|
||||
string << name
|
||||
string << "( "
|
||||
string << predicate_to_string( path.shift, &block )
|
||||
string << " )"
|
||||
when :literal
|
||||
path.shift
|
||||
string << " "
|
||||
string << path.shift.inspect
|
||||
string << " "
|
||||
else
|
||||
string << " "
|
||||
string << yield( path )
|
||||
string << " "
|
||||
end
|
||||
return string.squeeze(" ")
|
||||
end
|
||||
|
||||
private
|
||||
#LocationPath
|
||||
# | RelativeLocationPath
|
||||
# | '/' RelativeLocationPath?
|
||||
# | '//' RelativeLocationPath
|
||||
def LocationPath path, parsed
|
||||
path = path.lstrip
|
||||
if path[0] == ?/
|
||||
parsed << :document
|
||||
if path[1] == ?/
|
||||
parsed << :descendant_or_self
|
||||
parsed << :node
|
||||
path = path[2..-1]
|
||||
else
|
||||
path = path[1..-1]
|
||||
end
|
||||
end
|
||||
return RelativeLocationPath( path, parsed ) if path.size > 0
|
||||
end
|
||||
|
||||
#RelativeLocationPath
|
||||
# | Step
|
||||
# | (AXIS_NAME '::' | '@' | '') AxisSpecifier
|
||||
# NodeTest
|
||||
# Predicate
|
||||
# | '.' | '..' AbbreviatedStep
|
||||
# | RelativeLocationPath '/' Step
|
||||
# | RelativeLocationPath '//' Step
|
||||
AXIS = /^(ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|namespace|parent|preceding|preceding-sibling|self)::/
|
||||
def RelativeLocationPath path, parsed
|
||||
loop do
|
||||
original_path = path
|
||||
path = path.lstrip
|
||||
|
||||
return original_path if path.empty?
|
||||
|
||||
# (axis or @ or <child::>) nodetest predicate >
|
||||
# OR > / Step
|
||||
# (. or ..) >
|
||||
if path[0] == ?.
|
||||
if path[1] == ?.
|
||||
parsed << :parent
|
||||
parsed << :node
|
||||
path = path[2..-1]
|
||||
else
|
||||
parsed << :self
|
||||
parsed << :node
|
||||
path = path[1..-1]
|
||||
end
|
||||
else
|
||||
if path[0] == ?@
|
||||
parsed << :attribute
|
||||
path = path[1..-1]
|
||||
# Goto Nodetest
|
||||
elsif path =~ AXIS
|
||||
parsed << $1.tr('-','_').intern
|
||||
path = $'
|
||||
# Goto Nodetest
|
||||
else
|
||||
parsed << :child
|
||||
end
|
||||
|
||||
n = []
|
||||
path = NodeTest( path, n)
|
||||
|
||||
path = Predicate( path, n )
|
||||
|
||||
parsed.concat(n)
|
||||
end
|
||||
|
||||
original_path = path
|
||||
path = path.lstrip
|
||||
return original_path if path.empty?
|
||||
|
||||
return original_path if path[0] != ?/
|
||||
|
||||
if path[1] == ?/
|
||||
parsed << :descendant_or_self
|
||||
parsed << :node
|
||||
path = path[2..-1]
|
||||
else
|
||||
path = path[1..-1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a 1-1 map of the nodeset
|
||||
# The contents of the resulting array are either:
|
||||
# true/false, if a positive match
|
||||
# String, if a name match
|
||||
#NodeTest
|
||||
# | ('*' | NCNAME ':' '*' | QNAME) NameTest
|
||||
# | '*' ':' NCNAME NameTest since XPath 2.0
|
||||
# | NODE_TYPE '(' ')' NodeType
|
||||
# | PI '(' LITERAL ')' PI
|
||||
# | '[' expr ']' Predicate
|
||||
PREFIX_WILDCARD = /^\*:(#{NCNAME_STR})/u
|
||||
LOCAL_NAME_WILDCARD = /^(#{NCNAME_STR}):\*/u
|
||||
QNAME = Namespace::NAMESPLIT
|
||||
NODE_TYPE = /^(comment|text|node)\(\s*\)/m
|
||||
PI = /^processing-instruction\(/
|
||||
def NodeTest path, parsed
|
||||
original_path = path
|
||||
path = path.lstrip
|
||||
case path
|
||||
when PREFIX_WILDCARD
|
||||
prefix = nil
|
||||
name = $1
|
||||
path = $'
|
||||
parsed << :qname
|
||||
parsed << prefix
|
||||
parsed << name
|
||||
when /^\*/
|
||||
path = $'
|
||||
parsed << :any
|
||||
when NODE_TYPE
|
||||
type = $1
|
||||
path = $'
|
||||
parsed << type.tr('-', '_').intern
|
||||
when PI
|
||||
path = $'
|
||||
literal = nil
|
||||
if path !~ /^\s*\)/
|
||||
path =~ LITERAL
|
||||
literal = $1
|
||||
path = $'
|
||||
raise ParseException.new("Missing ')' after processing instruction") if path[0] != ?)
|
||||
path = path[1..-1]
|
||||
end
|
||||
parsed << :processing_instruction
|
||||
parsed << (literal || '')
|
||||
when LOCAL_NAME_WILDCARD
|
||||
prefix = $1
|
||||
path = $'
|
||||
parsed << :namespace
|
||||
parsed << prefix
|
||||
when QNAME
|
||||
prefix = $1
|
||||
name = $2
|
||||
path = $'
|
||||
prefix = "" unless prefix
|
||||
parsed << :qname
|
||||
parsed << prefix
|
||||
parsed << name
|
||||
else
|
||||
path = original_path
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
# Filters the supplied nodeset on the predicate(s)
|
||||
def Predicate path, parsed
|
||||
original_path = path
|
||||
path = path.lstrip
|
||||
return original_path unless path[0] == ?[
|
||||
predicates = []
|
||||
while path[0] == ?[
|
||||
path, expr = get_group(path)
|
||||
predicates << expr[1..-2] if expr
|
||||
end
|
||||
predicates.each{ |pred|
|
||||
preds = []
|
||||
parsed << :predicate
|
||||
parsed << preds
|
||||
OrExpr(pred, preds)
|
||||
}
|
||||
path
|
||||
end
|
||||
|
||||
# The following return arrays of true/false, a 1-1 mapping of the
|
||||
# supplied nodeset, except for axe(), which returns a filtered
|
||||
# nodeset
|
||||
|
||||
#| OrExpr S 'or' S AndExpr
|
||||
#| AndExpr
|
||||
def OrExpr path, parsed
|
||||
n = []
|
||||
rest = AndExpr( path, n )
|
||||
if rest != path
|
||||
while rest =~ /^\s*( or )/
|
||||
n = [ :or, n, [] ]
|
||||
rest = AndExpr( $', n[-1] )
|
||||
end
|
||||
end
|
||||
if parsed.size == 0 and n.size != 0
|
||||
parsed.replace(n)
|
||||
elsif n.size > 0
|
||||
parsed << n
|
||||
end
|
||||
rest
|
||||
end
|
||||
|
||||
#| AndExpr S 'and' S EqualityExpr
|
||||
#| EqualityExpr
|
||||
def AndExpr path, parsed
|
||||
n = []
|
||||
rest = EqualityExpr( path, n )
|
||||
if rest != path
|
||||
while rest =~ /^\s*( and )/
|
||||
n = [ :and, n, [] ]
|
||||
rest = EqualityExpr( $', n[-1] )
|
||||
end
|
||||
end
|
||||
if parsed.size == 0 and n.size != 0
|
||||
parsed.replace(n)
|
||||
elsif n.size > 0
|
||||
parsed << n
|
||||
end
|
||||
rest
|
||||
end
|
||||
|
||||
#| EqualityExpr ('=' | '!=') RelationalExpr
|
||||
#| RelationalExpr
|
||||
def EqualityExpr path, parsed
|
||||
n = []
|
||||
rest = RelationalExpr( path, n )
|
||||
if rest != path
|
||||
while rest =~ /^\s*(!?=)\s*/
|
||||
if $1[0] == ?!
|
||||
n = [ :neq, n, [] ]
|
||||
else
|
||||
n = [ :eq, n, [] ]
|
||||
end
|
||||
rest = RelationalExpr( $', n[-1] )
|
||||
end
|
||||
end
|
||||
if parsed.size == 0 and n.size != 0
|
||||
parsed.replace(n)
|
||||
elsif n.size > 0
|
||||
parsed << n
|
||||
end
|
||||
rest
|
||||
end
|
||||
|
||||
#| RelationalExpr ('<' | '>' | '<=' | '>=') AdditiveExpr
|
||||
#| AdditiveExpr
|
||||
def RelationalExpr path, parsed
|
||||
n = []
|
||||
rest = AdditiveExpr( path, n )
|
||||
if rest != path
|
||||
while rest =~ /^\s*([<>]=?)\s*/
|
||||
if $1[0] == ?<
|
||||
sym = "lt"
|
||||
else
|
||||
sym = "gt"
|
||||
end
|
||||
sym << "eq" if $1[-1] == ?=
|
||||
n = [ sym.intern, n, [] ]
|
||||
rest = AdditiveExpr( $', n[-1] )
|
||||
end
|
||||
end
|
||||
if parsed.size == 0 and n.size != 0
|
||||
parsed.replace(n)
|
||||
elsif n.size > 0
|
||||
parsed << n
|
||||
end
|
||||
rest
|
||||
end
|
||||
|
||||
#| AdditiveExpr ('+' | '-') MultiplicativeExpr
|
||||
#| MultiplicativeExpr
|
||||
def AdditiveExpr path, parsed
|
||||
n = []
|
||||
rest = MultiplicativeExpr( path, n )
|
||||
if rest != path
|
||||
while rest =~ /^\s*(\+|-)\s*/
|
||||
if $1[0] == ?+
|
||||
n = [ :plus, n, [] ]
|
||||
else
|
||||
n = [ :minus, n, [] ]
|
||||
end
|
||||
rest = MultiplicativeExpr( $', n[-1] )
|
||||
end
|
||||
end
|
||||
if parsed.size == 0 and n.size != 0
|
||||
parsed.replace(n)
|
||||
elsif n.size > 0
|
||||
parsed << n
|
||||
end
|
||||
rest
|
||||
end
|
||||
|
||||
#| MultiplicativeExpr ('*' | S ('div' | 'mod') S) UnaryExpr
|
||||
#| UnaryExpr
|
||||
def MultiplicativeExpr path, parsed
|
||||
n = []
|
||||
rest = UnaryExpr( path, n )
|
||||
if rest != path
|
||||
while rest =~ /^\s*(\*| div | mod )\s*/
|
||||
if $1[0] == ?*
|
||||
n = [ :mult, n, [] ]
|
||||
elsif $1.include?( "div" )
|
||||
n = [ :div, n, [] ]
|
||||
else
|
||||
n = [ :mod, n, [] ]
|
||||
end
|
||||
rest = UnaryExpr( $', n[-1] )
|
||||
end
|
||||
end
|
||||
if parsed.size == 0 and n.size != 0
|
||||
parsed.replace(n)
|
||||
elsif n.size > 0
|
||||
parsed << n
|
||||
end
|
||||
rest
|
||||
end
|
||||
|
||||
#| '-' UnaryExpr
|
||||
#| UnionExpr
|
||||
def UnaryExpr path, parsed
|
||||
path =~ /^(\-*)/
|
||||
path = $'
|
||||
if $1 and (($1.size % 2) != 0)
|
||||
mult = -1
|
||||
else
|
||||
mult = 1
|
||||
end
|
||||
parsed << :neg if mult < 0
|
||||
|
||||
n = []
|
||||
path = UnionExpr( path, n )
|
||||
parsed.concat( n )
|
||||
path
|
||||
end
|
||||
|
||||
#| UnionExpr '|' PathExpr
|
||||
#| PathExpr
|
||||
def UnionExpr path, parsed
|
||||
n = []
|
||||
rest = PathExpr( path, n )
|
||||
if rest != path
|
||||
while rest =~ /^\s*(\|)\s*/
|
||||
n = [ :union, n, [] ]
|
||||
rest = PathExpr( $', n[-1] )
|
||||
end
|
||||
end
|
||||
if parsed.size == 0 and n.size != 0
|
||||
parsed.replace( n )
|
||||
elsif n.size > 0
|
||||
parsed << n
|
||||
end
|
||||
rest
|
||||
end
|
||||
|
||||
#| LocationPath
|
||||
#| FilterExpr ('/' | '//') RelativeLocationPath
|
||||
def PathExpr path, parsed
|
||||
path = path.lstrip
|
||||
n = []
|
||||
rest = FilterExpr( path, n )
|
||||
if rest != path
|
||||
if rest and rest[0] == ?/
|
||||
rest = RelativeLocationPath(rest, n)
|
||||
parsed.concat(n)
|
||||
return rest
|
||||
end
|
||||
end
|
||||
rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w*]/
|
||||
parsed.concat(n)
|
||||
return rest
|
||||
end
|
||||
|
||||
#| FilterExpr Predicate
|
||||
#| PrimaryExpr
|
||||
def FilterExpr path, parsed
|
||||
n = []
|
||||
path = PrimaryExpr( path, n )
|
||||
path = Predicate(path, n)
|
||||
parsed.concat(n)
|
||||
path
|
||||
end
|
||||
|
||||
#| VARIABLE_REFERENCE
|
||||
#| '(' expr ')'
|
||||
#| LITERAL
|
||||
#| NUMBER
|
||||
#| FunctionCall
|
||||
VARIABLE_REFERENCE = /^\$(#{NAME_STR})/u
|
||||
NUMBER = /^(\d*\.?\d+)/
|
||||
NT = /^comment|text|processing-instruction|node$/
|
||||
def PrimaryExpr path, parsed
|
||||
case path
|
||||
when VARIABLE_REFERENCE
|
||||
varname = $1
|
||||
path = $'
|
||||
parsed << :variable
|
||||
parsed << varname
|
||||
#arry << @variables[ varname ]
|
||||
when /^(\w[-\w]*)(?:\()/
|
||||
fname = $1
|
||||
tmp = $'
|
||||
return path if fname =~ NT
|
||||
path = tmp
|
||||
parsed << :function
|
||||
parsed << fname
|
||||
path = FunctionCall(path, parsed)
|
||||
when NUMBER
|
||||
varname = $1.nil? ? $2 : $1
|
||||
path = $'
|
||||
parsed << :literal
|
||||
parsed << (varname.include?('.') ? varname.to_f : varname.to_i)
|
||||
when LITERAL
|
||||
varname = $1.nil? ? $2 : $1
|
||||
path = $'
|
||||
parsed << :literal
|
||||
parsed << varname
|
||||
when /^\(/ #/
|
||||
path, contents = get_group(path)
|
||||
contents = contents[1..-2]
|
||||
n = []
|
||||
OrExpr( contents, n )
|
||||
parsed.concat(n)
|
||||
end
|
||||
path
|
||||
end
|
||||
|
||||
#| FUNCTION_NAME '(' ( expr ( ',' expr )* )? ')'
|
||||
def FunctionCall rest, parsed
|
||||
path, arguments = parse_args(rest)
|
||||
argset = []
|
||||
for argument in arguments
|
||||
args = []
|
||||
OrExpr( argument, args )
|
||||
argset << args
|
||||
end
|
||||
parsed << argset
|
||||
path
|
||||
end
|
||||
|
||||
# get_group( '[foo]bar' ) -> ['bar', '[foo]']
|
||||
def get_group string
|
||||
ind = 0
|
||||
depth = 0
|
||||
st = string[0,1]
|
||||
en = (st == "(" ? ")" : "]")
|
||||
begin
|
||||
case string[ind,1]
|
||||
when st
|
||||
depth += 1
|
||||
when en
|
||||
depth -= 1
|
||||
end
|
||||
ind += 1
|
||||
end while depth > 0 and ind < string.length
|
||||
return nil unless depth==0
|
||||
[string[ind..-1], string[0..ind-1]]
|
||||
end
|
||||
|
||||
def parse_args( string )
|
||||
arguments = []
|
||||
ind = 0
|
||||
inquot = false
|
||||
inapos = false
|
||||
depth = 1
|
||||
begin
|
||||
case string[ind]
|
||||
when ?"
|
||||
inquot = !inquot unless inapos
|
||||
when ?'
|
||||
inapos = !inapos unless inquot
|
||||
else
|
||||
unless inquot or inapos
|
||||
case string[ind]
|
||||
when ?(
|
||||
depth += 1
|
||||
if depth == 1
|
||||
string = string[1..-1]
|
||||
ind -= 1
|
||||
end
|
||||
when ?)
|
||||
depth -= 1
|
||||
if depth == 0
|
||||
s = string[0,ind].strip
|
||||
arguments << s unless s == ""
|
||||
string = string[ind+1..-1]
|
||||
end
|
||||
when ?,
|
||||
if depth == 1
|
||||
s = string[0,ind].strip
|
||||
arguments << s unless s == ""
|
||||
string = string[ind+1..-1]
|
||||
ind = -1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
ind += 1
|
||||
end while depth > 0 and ind < string.length
|
||||
return nil unless depth==0
|
||||
[string,arguments]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,266 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'functions'
|
||||
require_relative 'xmltokens'
|
||||
|
||||
module REXML
|
||||
class QuickPath
|
||||
include Functions
|
||||
include XMLTokens
|
||||
|
||||
# A base Hash object to be used when initializing a
|
||||
# default empty namespaces set.
|
||||
EMPTY_HASH = {}
|
||||
|
||||
def QuickPath::first element, path, namespaces=EMPTY_HASH
|
||||
match(element, path, namespaces)[0]
|
||||
end
|
||||
|
||||
def QuickPath::each element, path, namespaces=EMPTY_HASH, &block
|
||||
path = "*" unless path
|
||||
match(element, path, namespaces).each( &block )
|
||||
end
|
||||
|
||||
def QuickPath::match element, path, namespaces=EMPTY_HASH
|
||||
raise "nil is not a valid xpath" unless path
|
||||
results = nil
|
||||
Functions::namespace_context = namespaces
|
||||
case path
|
||||
when /^\/([^\/]|$)/u
|
||||
# match on root
|
||||
path = path[1..-1]
|
||||
return [element.root.parent] if path == ''
|
||||
results = filter([element.root], path)
|
||||
when /^[-\w]*::/u
|
||||
results = filter([element], path)
|
||||
when /^\*/u
|
||||
results = filter(element.to_a, path)
|
||||
when /^[\[!\w:]/u
|
||||
# match on child
|
||||
children = element.to_a
|
||||
results = filter(children, path)
|
||||
else
|
||||
results = filter([element], path)
|
||||
end
|
||||
return results
|
||||
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
|
||||
def QuickPath::filter elements, path
|
||||
return elements if path.nil? or path == '' or elements.size == 0
|
||||
case path
|
||||
when /^\/\//u # Descendant
|
||||
return axe( elements, "descendant-or-self", $' )
|
||||
when /^\/?\b(\w[-\w]*)\b::/u # Axe
|
||||
return axe( elements, $1, $' )
|
||||
when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child
|
||||
rest = $'
|
||||
results = []
|
||||
elements.each do |element|
|
||||
results |= filter( element.to_a, rest )
|
||||
end
|
||||
return results
|
||||
when /^\/?(\w[-\w]*)\(/u # / Function
|
||||
return function( elements, $1, $' )
|
||||
when Namespace::NAMESPLIT # Element name
|
||||
name = $2
|
||||
ns = $1
|
||||
rest = $'
|
||||
elements.delete_if do |element|
|
||||
!(element.kind_of? Element and
|
||||
(element.expanded_name == name or
|
||||
(element.name == name and
|
||||
element.namespace == Functions.namespace_context[ns])))
|
||||
end
|
||||
return filter( elements, rest )
|
||||
when /^\/\[/u
|
||||
matches = []
|
||||
elements.each do |element|
|
||||
matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element
|
||||
end
|
||||
return matches
|
||||
when /^\[/u # Predicate
|
||||
return predicate( elements, path )
|
||||
when /^\/?\.\.\./u # Ancestor
|
||||
return axe( elements, "ancestor", $' )
|
||||
when /^\/?\.\./u # Parent
|
||||
return filter( elements.collect{|e|e.parent}, $' )
|
||||
when /^\/?\./u # Self
|
||||
return filter( elements, $' )
|
||||
when /^\*/u # Any
|
||||
results = []
|
||||
elements.each do |element|
|
||||
results |= filter( [element], $' ) if element.kind_of? Element
|
||||
#if element.kind_of? Element
|
||||
# children = element.to_a
|
||||
# children.delete_if { |child| !child.kind_of?(Element) }
|
||||
# results |= filter( children, $' )
|
||||
#end
|
||||
end
|
||||
return results
|
||||
end
|
||||
return []
|
||||
end
|
||||
|
||||
def QuickPath::axe( elements, axe_name, rest )
|
||||
matches = []
|
||||
matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u
|
||||
case axe_name
|
||||
when /^descendant/u
|
||||
elements.each do |element|
|
||||
matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element
|
||||
end
|
||||
when /^ancestor/u
|
||||
elements.each do |element|
|
||||
while element.parent
|
||||
matches << element.parent
|
||||
element = element.parent
|
||||
end
|
||||
end
|
||||
matches = filter( matches, rest )
|
||||
when "self"
|
||||
matches = filter( elements, rest )
|
||||
when "child"
|
||||
elements.each do |element|
|
||||
matches |= filter( element.to_a, rest ) if element.kind_of? Element
|
||||
end
|
||||
when "attribute"
|
||||
elements.each do |element|
|
||||
matches << element.attributes[ rest ] if element.kind_of? Element
|
||||
end
|
||||
when "parent"
|
||||
matches = filter(elements.collect{|element| element.parent}.uniq, rest)
|
||||
when "following-sibling"
|
||||
matches = filter(elements.collect{|element| element.next_sibling}.uniq,
|
||||
rest)
|
||||
when "previous-sibling"
|
||||
matches = filter(elements.collect{|element|
|
||||
element.previous_sibling}.uniq, rest )
|
||||
end
|
||||
return matches.uniq
|
||||
end
|
||||
|
||||
OPERAND_ = '((?=(?:(?!and|or).)*[^\s<>=])[^\s<>=]+)'
|
||||
# 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].
|
||||
def QuickPath::predicate( elements, path )
|
||||
ind = 1
|
||||
bcount = 1
|
||||
while bcount > 0
|
||||
bcount += 1 if path[ind] == ?[
|
||||
bcount -= 1 if path[ind] == ?]
|
||||
ind += 1
|
||||
end
|
||||
ind -= 1
|
||||
predicate = path[1..ind-1]
|
||||
rest = path[ind+1..-1]
|
||||
|
||||
# have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c'
|
||||
#
|
||||
predicate.gsub!(
|
||||
/#{OPERAND_}\s*([<>=])\s*#{OPERAND_}\s*([<>=])\s*#{OPERAND_}/u,
|
||||
'\1 \2 \3 and \3 \4 \5' )
|
||||
# Let's do some Ruby trickery to avoid some work:
|
||||
predicate.gsub!( /&/u, "&&" )
|
||||
predicate.gsub!( /=/u, "==" )
|
||||
predicate.gsub!( /@(\w[-\w.]*)/u, 'attribute("\1")' )
|
||||
predicate.gsub!( /\bmod\b/u, "%" )
|
||||
predicate.gsub!( /\b(\w[-\w.]*\()/u ) {
|
||||
fname = $1
|
||||
fname.gsub( /-/u, "_" )
|
||||
}
|
||||
|
||||
Functions.pair = [ 0, elements.size ]
|
||||
results = []
|
||||
elements.each do |element|
|
||||
Functions.pair[0] += 1
|
||||
Functions.node = element
|
||||
res = eval( predicate )
|
||||
case res
|
||||
when true
|
||||
results << element
|
||||
when Integer
|
||||
results << element if Functions.pair[0] == res
|
||||
when String
|
||||
results << element
|
||||
end
|
||||
end
|
||||
return filter( results, rest )
|
||||
end
|
||||
|
||||
def QuickPath::attribute( name )
|
||||
return Functions.node.attributes[name] if Functions.node.kind_of? Element
|
||||
end
|
||||
|
||||
def QuickPath::name()
|
||||
return Functions.node.name if Functions.node.kind_of? Element
|
||||
end
|
||||
|
||||
def QuickPath::method_missing( id, *args )
|
||||
begin
|
||||
Functions.send( id.id2name, *args )
|
||||
rescue Exception
|
||||
raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}"
|
||||
end
|
||||
end
|
||||
|
||||
def QuickPath::function( elements, fname, rest )
|
||||
args = parse_args( elements, rest )
|
||||
Functions.pair = [0, elements.size]
|
||||
results = []
|
||||
elements.each do |element|
|
||||
Functions.pair[0] += 1
|
||||
Functions.node = element
|
||||
res = Functions.send( fname, *args )
|
||||
case res
|
||||
when true
|
||||
results << element
|
||||
when Integer
|
||||
results << element if Functions.pair[0] == res
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
def QuickPath::parse_args( element, string )
|
||||
# /.*?(?:\)|,)/
|
||||
arguments = []
|
||||
buffer = ""
|
||||
while string and string != ""
|
||||
c = string[0]
|
||||
string.sub!(/^./u, "")
|
||||
case c
|
||||
when ?,
|
||||
# if depth = 1, then we start a new argument
|
||||
arguments << evaluate( buffer )
|
||||
#arguments << evaluate( string[0..count] )
|
||||
when ?(
|
||||
# start a new method call
|
||||
function( element, buffer, string )
|
||||
buffer = ""
|
||||
when ?)
|
||||
# close the method call and return arguments
|
||||
return arguments
|
||||
else
|
||||
buffer << c
|
||||
end
|
||||
end
|
||||
""
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,84 +0,0 @@
|
|||
begin
|
||||
require_relative "lib/rexml/rexml"
|
||||
rescue LoadError
|
||||
# for Ruby core repository
|
||||
require_relative "rexml"
|
||||
end
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "rexml"
|
||||
spec.version = REXML::VERSION
|
||||
spec.authors = ["Kouhei Sutou"]
|
||||
spec.email = ["kou@cozmixng.org"]
|
||||
|
||||
spec.summary = %q{An XML toolkit for Ruby}
|
||||
spec.description = %q{An XML toolkit for Ruby}
|
||||
spec.homepage = "https://github.com/ruby/rexml"
|
||||
spec.license = "BSD-2-Clause"
|
||||
|
||||
spec.files = [
|
||||
".gitignore",
|
||||
".travis.yml",
|
||||
"Gemfile",
|
||||
"LICENSE.txt",
|
||||
"NEWS.md",
|
||||
"README.md",
|
||||
"Rakefile",
|
||||
"lib/rexml/attlistdecl.rb",
|
||||
"lib/rexml/attribute.rb",
|
||||
"lib/rexml/cdata.rb",
|
||||
"lib/rexml/child.rb",
|
||||
"lib/rexml/comment.rb",
|
||||
"lib/rexml/doctype.rb",
|
||||
"lib/rexml/document.rb",
|
||||
"lib/rexml/dtd/attlistdecl.rb",
|
||||
"lib/rexml/dtd/dtd.rb",
|
||||
"lib/rexml/dtd/elementdecl.rb",
|
||||
"lib/rexml/dtd/entitydecl.rb",
|
||||
"lib/rexml/dtd/notationdecl.rb",
|
||||
"lib/rexml/element.rb",
|
||||
"lib/rexml/encoding.rb",
|
||||
"lib/rexml/entity.rb",
|
||||
"lib/rexml/formatters/default.rb",
|
||||
"lib/rexml/formatters/pretty.rb",
|
||||
"lib/rexml/formatters/transitive.rb",
|
||||
"lib/rexml/functions.rb",
|
||||
"lib/rexml/instruction.rb",
|
||||
"lib/rexml/light/node.rb",
|
||||
"lib/rexml/namespace.rb",
|
||||
"lib/rexml/node.rb",
|
||||
"lib/rexml/output.rb",
|
||||
"lib/rexml/parent.rb",
|
||||
"lib/rexml/parseexception.rb",
|
||||
"lib/rexml/parsers/baseparser.rb",
|
||||
"lib/rexml/parsers/lightparser.rb",
|
||||
"lib/rexml/parsers/pullparser.rb",
|
||||
"lib/rexml/parsers/sax2parser.rb",
|
||||
"lib/rexml/parsers/streamparser.rb",
|
||||
"lib/rexml/parsers/treeparser.rb",
|
||||
"lib/rexml/parsers/ultralightparser.rb",
|
||||
"lib/rexml/parsers/xpathparser.rb",
|
||||
"lib/rexml/quickpath.rb",
|
||||
"lib/rexml/rexml.rb",
|
||||
"lib/rexml/sax2listener.rb",
|
||||
"lib/rexml/security.rb",
|
||||
"lib/rexml/source.rb",
|
||||
"lib/rexml/streamlistener.rb",
|
||||
"lib/rexml/text.rb",
|
||||
"lib/rexml/undefinednamespaceexception.rb",
|
||||
"lib/rexml/validation/relaxng.rb",
|
||||
"lib/rexml/validation/validation.rb",
|
||||
"lib/rexml/validation/validationexception.rb",
|
||||
"lib/rexml/xmldecl.rb",
|
||||
"lib/rexml/xmltokens.rb",
|
||||
"lib/rexml/xpath.rb",
|
||||
"lib/rexml/xpath_parser.rb",
|
||||
"rexml.gemspec",
|
||||
]
|
||||
spec.bindir = "exe"
|
||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
spec.add_development_dependency "bundler"
|
||||
spec.add_development_dependency "rake"
|
||||
end
|
|
@ -1,32 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# frozen_string_literal: false
|
||||
# REXML is an XML toolkit for Ruby[http://www.ruby-lang.org], in Ruby.
|
||||
#
|
||||
# REXML is a _pure_ Ruby, XML 1.0 conforming,
|
||||
# non-validating[http://www.w3.org/TR/2004/REC-xml-20040204/#sec-conformance]
|
||||
# toolkit with an intuitive API. REXML passes 100% of the non-validating Oasis
|
||||
# tests[http://www.oasis-open.org/committees/xml-conformance/xml-test-suite.shtml],
|
||||
# and provides tree, stream, SAX2, pull, and lightweight APIs. REXML also
|
||||
# includes a full XPath[http://www.w3c.org/tr/xpath] 1.0 implementation. Since
|
||||
# Ruby 1.8, REXML is included in the standard Ruby distribution.
|
||||
#
|
||||
# Main page:: http://www.germane-software.com/software/rexml
|
||||
# Author:: Sean Russell <serATgermaneHYPHENsoftwareDOTcom>
|
||||
# Date:: 2008/019
|
||||
# Version:: 3.1.7.3
|
||||
#
|
||||
# This API documentation can be downloaded from the REXML home page, or can
|
||||
# be accessed online[http://www.germane-software.com/software/rexml_doc]
|
||||
#
|
||||
# A tutorial is available in the REXML distribution in docs/tutorial.html,
|
||||
# or can be accessed
|
||||
# online[http://www.germane-software.com/software/rexml/docs/tutorial.html]
|
||||
module REXML
|
||||
COPYRIGHT = "Copyright © 2001-2008 Sean Russell <ser@germane-software.com>"
|
||||
DATE = "2008/019"
|
||||
VERSION = "3.2.3"
|
||||
REVISION = ""
|
||||
|
||||
Copyright = COPYRIGHT
|
||||
Version = VERSION
|
||||
end
|
|
@ -1,98 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
module REXML
|
||||
# A template for stream parser listeners.
|
||||
# Note that the declarations (attlistdecl, elementdecl, etc) are trivially
|
||||
# processed; REXML doesn't yet handle doctype entity declarations, so you
|
||||
# have to parse them out yourself.
|
||||
# === Missing methods from SAX2
|
||||
# ignorable_whitespace
|
||||
# === Methods extending SAX2
|
||||
# +WARNING+
|
||||
# These methods are certainly going to change, until DTDs are fully
|
||||
# supported. Be aware of this.
|
||||
# start_document
|
||||
# end_document
|
||||
# doctype
|
||||
# elementdecl
|
||||
# attlistdecl
|
||||
# entitydecl
|
||||
# notationdecl
|
||||
# cdata
|
||||
# xmldecl
|
||||
# comment
|
||||
module SAX2Listener
|
||||
def start_document
|
||||
end
|
||||
def end_document
|
||||
end
|
||||
def start_prefix_mapping prefix, uri
|
||||
end
|
||||
def end_prefix_mapping prefix
|
||||
end
|
||||
def start_element uri, localname, qname, attributes
|
||||
end
|
||||
def end_element uri, localname, qname
|
||||
end
|
||||
def characters text
|
||||
end
|
||||
def processing_instruction target, data
|
||||
end
|
||||
# Handles a doctype declaration. Any attributes of the doctype which are
|
||||
# not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar">
|
||||
# @p name the name of the doctype; EG, "me"
|
||||
# @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC"
|
||||
# @p long_name the supplied long name, or nil. EG, "foo"
|
||||
# @p uri the uri of the doctype, or nil. EG, "bar"
|
||||
def doctype name, pub_sys, long_name, uri
|
||||
end
|
||||
# If a doctype includes an ATTLIST declaration, it will cause this
|
||||
# method to be called. The content is the declaration itself, unparsed.
|
||||
# EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el
|
||||
# attr CDATA #REQUIRED". This is the same for all of the .*decl
|
||||
# methods.
|
||||
def attlistdecl(element, pairs, contents)
|
||||
end
|
||||
# <!ELEMENT ...>
|
||||
def elementdecl content
|
||||
end
|
||||
# <!ENTITY ...>
|
||||
# The argument passed to this method is an array of the entity
|
||||
# declaration. It can be in a number of formats, but in general it
|
||||
# returns (example, result):
|
||||
# <!ENTITY % YN '"Yes"'>
|
||||
# ["%", "YN", "\"Yes\""]
|
||||
# <!ENTITY % YN 'Yes'>
|
||||
# ["%", "YN", "Yes"]
|
||||
# <!ENTITY WhatHeSaid "He said %YN;">
|
||||
# ["WhatHeSaid", "He said %YN;"]
|
||||
# <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
|
||||
# ["open-hatch", "SYSTEM", "http://www.textuality.com/boilerplate/OpenHatch.xml"]
|
||||
# <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml">
|
||||
# ["open-hatch", "PUBLIC", "-//Textuality//TEXT Standard open-hatch boilerplate//EN", "http://www.textuality.com/boilerplate/OpenHatch.xml"]
|
||||
# <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif>
|
||||
# ["hatch-pic", "SYSTEM", "../grafix/OpenHatch.gif", "NDATA", "gif"]
|
||||
def entitydecl declaration
|
||||
end
|
||||
# <!NOTATION ...>
|
||||
def notationdecl name, public_or_system, public_id, system_id
|
||||
end
|
||||
# Called when <![CDATA[ ... ]]> is encountered in a document.
|
||||
# @p content "..."
|
||||
def cdata content
|
||||
end
|
||||
# Called when an XML PI is encountered in the document.
|
||||
# EG: <?xml version="1.0" encoding="utf"?>
|
||||
# @p version the version attribute value. EG, "1.0"
|
||||
# @p encoding the encoding attribute value, or nil. EG, "utf"
|
||||
# @p standalone the standalone attribute value, or nil. EG, nil
|
||||
# @p spaced the declaration is followed by a line break
|
||||
def xmldecl version, encoding, standalone
|
||||
end
|
||||
# Called when a comment is encountered.
|
||||
# @p comment The content of the comment
|
||||
def comment comment
|
||||
end
|
||||
def progress position
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
module REXML
|
||||
module Security
|
||||
@@entity_expansion_limit = 10_000
|
||||
|
||||
# Set the entity expansion limit. By default the limit is set to 10000.
|
||||
def self.entity_expansion_limit=( val )
|
||||
@@entity_expansion_limit = val
|
||||
end
|
||||
|
||||
# Get the entity expansion limit. By default the limit is set to 10000.
|
||||
def self.entity_expansion_limit
|
||||
return @@entity_expansion_limit
|
||||
end
|
||||
|
||||
@@entity_expansion_text_limit = 10_240
|
||||
|
||||
# Set the entity expansion limit. By default the limit is set to 10240.
|
||||
def self.entity_expansion_text_limit=( val )
|
||||
@@entity_expansion_text_limit = val
|
||||
end
|
||||
|
||||
# Get the entity expansion limit. By default the limit is set to 10240.
|
||||
def self.entity_expansion_text_limit
|
||||
return @@entity_expansion_text_limit
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,298 +0,0 @@
|
|||
# coding: US-ASCII
|
||||
# frozen_string_literal: false
|
||||
require_relative 'encoding'
|
||||
|
||||
module REXML
|
||||
# Generates Source-s. USE THIS CLASS.
|
||||
class SourceFactory
|
||||
# Generates a Source object
|
||||
# @param arg Either a String, or an IO
|
||||
# @return a Source, or nil if a bad argument was given
|
||||
def SourceFactory::create_from(arg)
|
||||
if arg.respond_to? :read and
|
||||
arg.respond_to? :readline and
|
||||
arg.respond_to? :nil? and
|
||||
arg.respond_to? :eof?
|
||||
IOSource.new(arg)
|
||||
elsif arg.respond_to? :to_str
|
||||
require 'stringio'
|
||||
IOSource.new(StringIO.new(arg))
|
||||
elsif arg.kind_of? Source
|
||||
arg
|
||||
else
|
||||
raise "#{arg.class} is not a valid input stream. It must walk \n"+
|
||||
"like either a String, an IO, or a Source."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# A Source can be searched for patterns, and wraps buffers and other
|
||||
# objects and provides consumption of text
|
||||
class Source
|
||||
include Encoding
|
||||
# The current buffer (what we're going to read next)
|
||||
attr_reader :buffer
|
||||
# The line number of the last consumed text
|
||||
attr_reader :line
|
||||
attr_reader :encoding
|
||||
|
||||
# Constructor
|
||||
# @param arg must be a String, and should be a valid XML document
|
||||
# @param encoding if non-null, sets the encoding of the source to this
|
||||
# value, overriding all encoding detection
|
||||
def initialize(arg, encoding=nil)
|
||||
@orig = @buffer = arg
|
||||
if encoding
|
||||
self.encoding = encoding
|
||||
else
|
||||
detect_encoding
|
||||
end
|
||||
@line = 0
|
||||
end
|
||||
|
||||
|
||||
# Inherited from Encoding
|
||||
# Overridden to support optimized en/decoding
|
||||
def encoding=(enc)
|
||||
return unless super
|
||||
encoding_updated
|
||||
end
|
||||
|
||||
# Scans the source for a given pattern. Note, that this is not your
|
||||
# usual scan() method. For one thing, the pattern argument has some
|
||||
# requirements; for another, the source can be consumed. You can easily
|
||||
# confuse this method. Originally, the patterns were easier
|
||||
# to construct and this method more robust, because this method
|
||||
# generated search regexps on the fly; however, this was
|
||||
# computationally expensive and slowed down the entire REXML package
|
||||
# considerably, since this is by far the most commonly called method.
|
||||
# @param pattern must be a Regexp, and must be in the form of
|
||||
# /^\s*(#{your pattern, with no groups})(.*)/. The first group
|
||||
# will be returned; the second group is used if the consume flag is
|
||||
# set.
|
||||
# @param consume if true, the pattern returned will be consumed, leaving
|
||||
# everything after it in the Source.
|
||||
# @return the pattern, if found, or nil if the Source is empty or the
|
||||
# pattern is not found.
|
||||
def scan(pattern, cons=false)
|
||||
return nil if @buffer.nil?
|
||||
rv = @buffer.scan(pattern)
|
||||
@buffer = $' if cons and rv.size>0
|
||||
rv
|
||||
end
|
||||
|
||||
def read
|
||||
end
|
||||
|
||||
def consume( pattern )
|
||||
@buffer = $' if pattern.match( @buffer )
|
||||
end
|
||||
|
||||
def match_to( char, pattern )
|
||||
return pattern.match(@buffer)
|
||||
end
|
||||
|
||||
def match_to_consume( char, pattern )
|
||||
md = pattern.match(@buffer)
|
||||
@buffer = $'
|
||||
return md
|
||||
end
|
||||
|
||||
def match(pattern, cons=false)
|
||||
md = pattern.match(@buffer)
|
||||
@buffer = $' if cons and md
|
||||
return md
|
||||
end
|
||||
|
||||
# @return true if the Source is exhausted
|
||||
def empty?
|
||||
@buffer == ""
|
||||
end
|
||||
|
||||
def position
|
||||
@orig.index( @buffer )
|
||||
end
|
||||
|
||||
# @return the current line in the source
|
||||
def current_line
|
||||
lines = @orig.split
|
||||
res = lines.grep @buffer[0..30]
|
||||
res = res[-1] if res.kind_of? Array
|
||||
lines.index( res ) if res
|
||||
end
|
||||
|
||||
private
|
||||
def detect_encoding
|
||||
buffer_encoding = @buffer.encoding
|
||||
detected_encoding = "UTF-8"
|
||||
begin
|
||||
@buffer.force_encoding("ASCII-8BIT")
|
||||
if @buffer[0, 2] == "\xfe\xff"
|
||||
@buffer[0, 2] = ""
|
||||
detected_encoding = "UTF-16BE"
|
||||
elsif @buffer[0, 2] == "\xff\xfe"
|
||||
@buffer[0, 2] = ""
|
||||
detected_encoding = "UTF-16LE"
|
||||
elsif @buffer[0, 3] == "\xef\xbb\xbf"
|
||||
@buffer[0, 3] = ""
|
||||
detected_encoding = "UTF-8"
|
||||
end
|
||||
ensure
|
||||
@buffer.force_encoding(buffer_encoding)
|
||||
end
|
||||
self.encoding = detected_encoding
|
||||
end
|
||||
|
||||
def encoding_updated
|
||||
if @encoding != 'UTF-8'
|
||||
@buffer = decode(@buffer)
|
||||
@to_utf = true
|
||||
else
|
||||
@to_utf = false
|
||||
@buffer.force_encoding ::Encoding::UTF_8
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# A Source that wraps an IO. See the Source class for method
|
||||
# documentation
|
||||
class IOSource < Source
|
||||
#attr_reader :block_size
|
||||
|
||||
# block_size has been deprecated
|
||||
def initialize(arg, block_size=500, encoding=nil)
|
||||
@er_source = @source = arg
|
||||
@to_utf = false
|
||||
@pending_buffer = nil
|
||||
|
||||
if encoding
|
||||
super("", encoding)
|
||||
else
|
||||
super(@source.read(3) || "")
|
||||
end
|
||||
|
||||
if !@to_utf and
|
||||
@buffer.respond_to?(:force_encoding) and
|
||||
@source.respond_to?(:external_encoding) and
|
||||
@source.external_encoding != ::Encoding::UTF_8
|
||||
@force_utf8 = true
|
||||
else
|
||||
@force_utf8 = false
|
||||
end
|
||||
end
|
||||
|
||||
def scan(pattern, cons=false)
|
||||
rv = super
|
||||
# You'll notice that this next section is very similar to the same
|
||||
# section in match(), but just a liiittle different. This is
|
||||
# because it is a touch faster to do it this way with scan()
|
||||
# than the way match() does it; enough faster to warrant duplicating
|
||||
# some code
|
||||
if rv.size == 0
|
||||
until @buffer =~ pattern or @source.nil?
|
||||
begin
|
||||
@buffer << readline
|
||||
rescue Iconv::IllegalSequence
|
||||
raise
|
||||
rescue
|
||||
@source = nil
|
||||
end
|
||||
end
|
||||
rv = super
|
||||
end
|
||||
rv.taint if RUBY_VERSION < '2.7'
|
||||
rv
|
||||
end
|
||||
|
||||
def read
|
||||
begin
|
||||
@buffer << readline
|
||||
rescue Exception, NameError
|
||||
@source = nil
|
||||
end
|
||||
end
|
||||
|
||||
def consume( pattern )
|
||||
match( pattern, true )
|
||||
end
|
||||
|
||||
def match( pattern, cons=false )
|
||||
rv = pattern.match(@buffer)
|
||||
@buffer = $' if cons and rv
|
||||
while !rv and @source
|
||||
begin
|
||||
@buffer << readline
|
||||
rv = pattern.match(@buffer)
|
||||
@buffer = $' if cons and rv
|
||||
rescue
|
||||
@source = nil
|
||||
end
|
||||
end
|
||||
rv.taint if RUBY_VERSION < '2.7'
|
||||
rv
|
||||
end
|
||||
|
||||
def empty?
|
||||
super and ( @source.nil? || @source.eof? )
|
||||
end
|
||||
|
||||
def position
|
||||
@er_source.pos rescue 0
|
||||
end
|
||||
|
||||
# @return the current line in the source
|
||||
def current_line
|
||||
begin
|
||||
pos = @er_source.pos # The byte position in the source
|
||||
lineno = @er_source.lineno # The XML < position in the source
|
||||
@er_source.rewind
|
||||
line = 0 # The \r\n position in the source
|
||||
begin
|
||||
while @er_source.pos < pos
|
||||
@er_source.readline
|
||||
line += 1
|
||||
end
|
||||
rescue
|
||||
end
|
||||
@er_source.seek(pos)
|
||||
rescue IOError
|
||||
pos = -1
|
||||
line = -1
|
||||
end
|
||||
[pos, lineno, line]
|
||||
end
|
||||
|
||||
private
|
||||
def readline
|
||||
str = @source.readline(@line_break)
|
||||
if @pending_buffer
|
||||
if str.nil?
|
||||
str = @pending_buffer
|
||||
else
|
||||
str = @pending_buffer + str
|
||||
end
|
||||
@pending_buffer = nil
|
||||
end
|
||||
return nil if str.nil?
|
||||
|
||||
if @to_utf
|
||||
decode(str)
|
||||
else
|
||||
str.force_encoding(::Encoding::UTF_8) if @force_utf8
|
||||
str
|
||||
end
|
||||
end
|
||||
|
||||
def encoding_updated
|
||||
case @encoding
|
||||
when "UTF-16BE", "UTF-16LE"
|
||||
@source.binmode
|
||||
@source.set_encoding(@encoding, @encoding)
|
||||
end
|
||||
@line_break = encode(">")
|
||||
@pending_buffer, @buffer = @buffer, ""
|
||||
@pending_buffer.force_encoding(@encoding)
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,93 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
module REXML
|
||||
# A template for stream parser listeners.
|
||||
# Note that the declarations (attlistdecl, elementdecl, etc) are trivially
|
||||
# processed; REXML doesn't yet handle doctype entity declarations, so you
|
||||
# have to parse them out yourself.
|
||||
module StreamListener
|
||||
# Called when a tag is encountered.
|
||||
# @p name the tag name
|
||||
# @p attrs an array of arrays of attribute/value pairs, suitable for
|
||||
# use with assoc or rassoc. IE, <tag attr1="value1" attr2="value2">
|
||||
# will result in
|
||||
# tag_start( "tag", # [["attr1","value1"],["attr2","value2"]])
|
||||
def tag_start name, attrs
|
||||
end
|
||||
# Called when the end tag is reached. In the case of <tag/>, tag_end
|
||||
# will be called immediately after tag_start
|
||||
# @p the name of the tag
|
||||
def tag_end name
|
||||
end
|
||||
# Called when text is encountered in the document
|
||||
# @p text the text content.
|
||||
def text text
|
||||
end
|
||||
# Called when an instruction is encountered. EG: <?xsl sheet='foo'?>
|
||||
# @p name the instruction name; in the example, "xsl"
|
||||
# @p instruction the rest of the instruction. In the example,
|
||||
# "sheet='foo'"
|
||||
def instruction name, instruction
|
||||
end
|
||||
# Called when a comment is encountered.
|
||||
# @p comment The content of the comment
|
||||
def comment comment
|
||||
end
|
||||
# Handles a doctype declaration. Any attributes of the doctype which are
|
||||
# not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar">
|
||||
# @p name the name of the doctype; EG, "me"
|
||||
# @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC"
|
||||
# @p long_name the supplied long name, or nil. EG, "foo"
|
||||
# @p uri the uri of the doctype, or nil. EG, "bar"
|
||||
def doctype name, pub_sys, long_name, uri
|
||||
end
|
||||
# Called when the doctype is done
|
||||
def doctype_end
|
||||
end
|
||||
# If a doctype includes an ATTLIST declaration, it will cause this
|
||||
# method to be called. The content is the declaration itself, unparsed.
|
||||
# EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el
|
||||
# attr CDATA #REQUIRED". This is the same for all of the .*decl
|
||||
# methods.
|
||||
def attlistdecl element_name, attributes, raw_content
|
||||
end
|
||||
# <!ELEMENT ...>
|
||||
def elementdecl content
|
||||
end
|
||||
# <!ENTITY ...>
|
||||
# The argument passed to this method is an array of the entity
|
||||
# declaration. It can be in a number of formats, but in general it
|
||||
# returns (example, result):
|
||||
# <!ENTITY % YN '"Yes"'>
|
||||
# ["YN", "\"Yes\"", "%"]
|
||||
# <!ENTITY % YN 'Yes'>
|
||||
# ["YN", "Yes", "%"]
|
||||
# <!ENTITY WhatHeSaid "He said %YN;">
|
||||
# ["WhatHeSaid", "He said %YN;"]
|
||||
# <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
|
||||
# ["open-hatch", "SYSTEM", "http://www.textuality.com/boilerplate/OpenHatch.xml"]
|
||||
# <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml">
|
||||
# ["open-hatch", "PUBLIC", "-//Textuality//TEXT Standard open-hatch boilerplate//EN", "http://www.textuality.com/boilerplate/OpenHatch.xml"]
|
||||
# <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif>
|
||||
# ["hatch-pic", "SYSTEM", "../grafix/OpenHatch.gif", "gif"]
|
||||
def entitydecl content
|
||||
end
|
||||
# <!NOTATION ...>
|
||||
def notationdecl content
|
||||
end
|
||||
# Called when %foo; is encountered in a doctype declaration.
|
||||
# @p content "foo"
|
||||
def entity content
|
||||
end
|
||||
# Called when <![CDATA[ ... ]]> is encountered in a document.
|
||||
# @p content "..."
|
||||
def cdata content
|
||||
end
|
||||
# Called when an XML PI is encountered in the document.
|
||||
# EG: <?xml version="1.0" encoding="utf"?>
|
||||
# @p version the version attribute value. EG, "1.0"
|
||||
# @p encoding the encoding attribute value, or nil. EG, "utf"
|
||||
# @p standalone the standalone attribute value, or nil. EG, nil
|
||||
def xmldecl version, encoding, standalone
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,424 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'security'
|
||||
require_relative 'entity'
|
||||
require_relative 'doctype'
|
||||
require_relative 'child'
|
||||
require_relative 'doctype'
|
||||
require_relative 'parseexception'
|
||||
|
||||
module REXML
|
||||
# Represents text nodes in an XML document
|
||||
class Text < Child
|
||||
include Comparable
|
||||
# The order in which the substitutions occur
|
||||
SPECIALS = [ /&(?!#?[\w-]+;)/u, /</u, />/u, /"/u, /'/u, /\r/u ]
|
||||
SUBSTITUTES = ['&', '<', '>', '"', ''', ' ']
|
||||
# Characters which are substituted in written strings
|
||||
SLAICEPS = [ '<', '>', '"', "'", '&' ]
|
||||
SETUTITSBUS = [ /</u, />/u, /"/u, /'/u, /&/u ]
|
||||
|
||||
# If +raw+ is true, then REXML leaves the value alone
|
||||
attr_accessor :raw
|
||||
|
||||
NEEDS_A_SECOND_CHECK = /(<|&((#{Entity::NAME});|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));)?)/um
|
||||
NUMERICENTITY = /�*((?:\d+)|(?:x[a-fA-F0-9]+));/
|
||||
VALID_CHAR = [
|
||||
0x9, 0xA, 0xD,
|
||||
(0x20..0xD7FF),
|
||||
(0xE000..0xFFFD),
|
||||
(0x10000..0x10FFFF)
|
||||
]
|
||||
|
||||
if String.method_defined? :encode
|
||||
VALID_XML_CHARS = Regexp.new('^['+
|
||||
VALID_CHAR.map { |item|
|
||||
case item
|
||||
when Integer
|
||||
[item].pack('U').force_encoding('utf-8')
|
||||
when Range
|
||||
[item.first, '-'.ord, item.last].pack('UUU').force_encoding('utf-8')
|
||||
end
|
||||
}.join +
|
||||
']*$')
|
||||
else
|
||||
VALID_XML_CHARS = /^(
|
||||
[\x09\x0A\x0D\x20-\x7E] # ASCII
|
||||
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|
||||
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|
||||
| [\xE1-\xEC\xEE][\x80-\xBF]{2} # straight 3-byte
|
||||
| \xEF[\x80-\xBE]{2} #
|
||||
| \xEF\xBF[\x80-\xBD] # excluding U+fffe and U+ffff
|
||||
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|
||||
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|
||||
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|
||||
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
|
||||
)*$/nx;
|
||||
end
|
||||
|
||||
# Constructor
|
||||
# +arg+ if a String, the content is set to the String. If a Text,
|
||||
# the object is shallowly cloned.
|
||||
#
|
||||
# +respect_whitespace+ (boolean, false) if true, whitespace is
|
||||
# respected
|
||||
#
|
||||
# +parent+ (nil) if this is a Parent object, the parent
|
||||
# will be set to this.
|
||||
#
|
||||
# +raw+ (nil) This argument can be given three values.
|
||||
# If true, then the value of used to construct this object is expected to
|
||||
# contain no unescaped XML markup, and REXML will not change the text. If
|
||||
# this value is false, the string may contain any characters, and REXML will
|
||||
# escape any and all defined entities whose values are contained in the
|
||||
# text. If this value is nil (the default), then the raw value of the
|
||||
# parent will be used as the raw value for this node. If there is no raw
|
||||
# value for the parent, and no value is supplied, the default is false.
|
||||
# Use this field if you have entities defined for some text, and you don't
|
||||
# want REXML to escape that text in output.
|
||||
# Text.new( "<&", false, nil, false ) #-> "<&"
|
||||
# Text.new( "<&", false, nil, false ) #-> "&lt;&amp;"
|
||||
# Text.new( "<&", false, nil, true ) #-> Parse exception
|
||||
# Text.new( "<&", false, nil, true ) #-> "<&"
|
||||
# # Assume that the entity "s" is defined to be "sean"
|
||||
# # and that the entity "r" is defined to be "russell"
|
||||
# Text.new( "sean russell" ) #-> "&s; &r;"
|
||||
# Text.new( "sean russell", false, nil, true ) #-> "sean russell"
|
||||
#
|
||||
# +entity_filter+ (nil) This can be an array of entities to match in the
|
||||
# supplied text. This argument is only useful if +raw+ is set to false.
|
||||
# Text.new( "sean russell", false, nil, false, ["s"] ) #-> "&s; russell"
|
||||
# Text.new( "sean russell", false, nil, true, ["s"] ) #-> "sean russell"
|
||||
# In the last example, the +entity_filter+ argument is ignored.
|
||||
#
|
||||
# +illegal+ INTERNAL USE ONLY
|
||||
def initialize(arg, respect_whitespace=false, parent=nil, raw=nil,
|
||||
entity_filter=nil, illegal=NEEDS_A_SECOND_CHECK )
|
||||
|
||||
@raw = false
|
||||
@parent = nil
|
||||
@entity_filter = nil
|
||||
|
||||
if parent
|
||||
super( parent )
|
||||
@raw = parent.raw
|
||||
end
|
||||
|
||||
if arg.kind_of? String
|
||||
@string = arg.dup
|
||||
elsif arg.kind_of? Text
|
||||
@string = arg.instance_variable_get(:@string).dup
|
||||
@raw = arg.raw
|
||||
@entity_filter = arg.instance_variable_get(:@entity_filter)
|
||||
else
|
||||
raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})"
|
||||
end
|
||||
|
||||
@string.squeeze!(" \n\t") unless respect_whitespace
|
||||
@string.gsub!(/\r\n?/, "\n")
|
||||
@raw = raw unless raw.nil?
|
||||
@entity_filter = entity_filter if entity_filter
|
||||
clear_cache
|
||||
|
||||
Text.check(@string, illegal, doctype) if @raw
|
||||
end
|
||||
|
||||
def parent= parent
|
||||
super(parent)
|
||||
Text.check(@string, NEEDS_A_SECOND_CHECK, doctype) if @raw and @parent
|
||||
end
|
||||
|
||||
# check for illegal characters
|
||||
def Text.check string, pattern, doctype
|
||||
|
||||
# illegal anywhere
|
||||
if string !~ VALID_XML_CHARS
|
||||
if String.method_defined? :encode
|
||||
string.chars.each do |c|
|
||||
case c.ord
|
||||
when *VALID_CHAR
|
||||
else
|
||||
raise "Illegal character #{c.inspect} in raw string #{string.inspect}"
|
||||
end
|
||||
end
|
||||
else
|
||||
string.scan(/[\x00-\x7F]|[\x80-\xBF][\xC0-\xF0]*|[\xC0-\xF0]/n) do |c|
|
||||
case c.unpack('U')
|
||||
when *VALID_CHAR
|
||||
else
|
||||
raise "Illegal character #{c.inspect} in raw string #{string.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# context sensitive
|
||||
string.scan(pattern) do
|
||||
if $1[-1] != ?;
|
||||
raise "Illegal character #{$1.inspect} in raw string #{string.inspect}"
|
||||
elsif $1[0] == ?&
|
||||
if $5 and $5[0] == ?#
|
||||
case ($5[1] == ?x ? $5[2..-1].to_i(16) : $5[1..-1].to_i)
|
||||
when *VALID_CHAR
|
||||
else
|
||||
raise "Illegal character #{$1.inspect} in raw string #{string.inspect}"
|
||||
end
|
||||
# FIXME: below can't work but this needs API change.
|
||||
# elsif @parent and $3 and !SUBSTITUTES.include?($1)
|
||||
# if !doctype or !doctype.entities.has_key?($3)
|
||||
# raise "Undeclared entity '#{$1}' in raw string \"#{string}\""
|
||||
# end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def node_type
|
||||
:text
|
||||
end
|
||||
|
||||
def empty?
|
||||
@string.size==0
|
||||
end
|
||||
|
||||
|
||||
def clone
|
||||
return Text.new(self, true)
|
||||
end
|
||||
|
||||
|
||||
# Appends text to this text node. The text is appended in the +raw+ mode
|
||||
# of this text node.
|
||||
#
|
||||
# +returns+ the text itself to enable method chain like
|
||||
# 'text << "XXX" << "YYY"'.
|
||||
def <<( to_append )
|
||||
@string << to_append.gsub( /\r\n?/, "\n" )
|
||||
clear_cache
|
||||
self
|
||||
end
|
||||
|
||||
|
||||
# +other+ a String or a Text
|
||||
# +returns+ the result of (to_s <=> arg.to_s)
|
||||
def <=>( other )
|
||||
to_s() <=> other.to_s
|
||||
end
|
||||
|
||||
def doctype
|
||||
if @parent
|
||||
doc = @parent.document
|
||||
doc.doctype if doc
|
||||
end
|
||||
end
|
||||
|
||||
REFERENCE = /#{Entity::REFERENCE}/
|
||||
# Returns the string value of this text node. This string is always
|
||||
# escaped, meaning that it is a valid XML text node string, and all
|
||||
# entities that can be escaped, have been inserted. This method respects
|
||||
# the entity filter set in the constructor.
|
||||
#
|
||||
# # Assume that the entity "s" is defined to be "sean", and that the
|
||||
# # entity "r" is defined to be "russell"
|
||||
# t = Text.new( "< & sean russell", false, nil, false, ['s'] )
|
||||
# t.to_s #-> "< & &s; russell"
|
||||
# t = Text.new( "< & &s; russell", false, nil, false )
|
||||
# t.to_s #-> "< & &s; russell"
|
||||
# u = Text.new( "sean russell", false, nil, true )
|
||||
# u.to_s #-> "sean russell"
|
||||
def to_s
|
||||
return @string if @raw
|
||||
@normalized ||= Text::normalize( @string, doctype, @entity_filter )
|
||||
end
|
||||
|
||||
def inspect
|
||||
@string.inspect
|
||||
end
|
||||
|
||||
# Returns the string value of this text. This is the text without
|
||||
# entities, as it might be used programmatically, or printed to the
|
||||
# console. This ignores the 'raw' attribute setting, and any
|
||||
# entity_filter.
|
||||
#
|
||||
# # Assume that the entity "s" is defined to be "sean", and that the
|
||||
# # entity "r" is defined to be "russell"
|
||||
# t = Text.new( "< & sean russell", false, nil, false, ['s'] )
|
||||
# t.value #-> "< & sean russell"
|
||||
# t = Text.new( "< & &s; russell", false, nil, false )
|
||||
# t.value #-> "< & sean russell"
|
||||
# u = Text.new( "sean russell", false, nil, true )
|
||||
# u.value #-> "sean russell"
|
||||
def value
|
||||
@unnormalized ||= Text::unnormalize( @string, doctype )
|
||||
end
|
||||
|
||||
# Sets the contents of this text node. This expects the text to be
|
||||
# unnormalized. It returns self.
|
||||
#
|
||||
# e = Element.new( "a" )
|
||||
# e.add_text( "foo" ) # <a>foo</a>
|
||||
# e[0].value = "bar" # <a>bar</a>
|
||||
# e[0].value = "<a>" # <a><a></a>
|
||||
def value=( val )
|
||||
@string = val.gsub( /\r\n?/, "\n" )
|
||||
clear_cache
|
||||
@raw = false
|
||||
end
|
||||
|
||||
def wrap(string, width, addnewline=false)
|
||||
# Recursively wrap string at width.
|
||||
return string if string.length <= width
|
||||
place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
|
||||
if addnewline then
|
||||
return "\n" + string[0,place] + "\n" + wrap(string[place+1..-1], width)
|
||||
else
|
||||
return string[0,place] + "\n" + wrap(string[place+1..-1], width)
|
||||
end
|
||||
end
|
||||
|
||||
def indent_text(string, level=1, style="\t", indentfirstline=true)
|
||||
return string if level < 0
|
||||
new_string = ''
|
||||
string.each_line { |line|
|
||||
indent_string = style * level
|
||||
new_line = (indent_string + line).sub(/[\s]+$/,'')
|
||||
new_string << new_line
|
||||
}
|
||||
new_string.strip! unless indentfirstline
|
||||
return new_string
|
||||
end
|
||||
|
||||
# == DEPRECATED
|
||||
# See REXML::Formatters
|
||||
#
|
||||
def write( writer, indent=-1, transitive=false, ie_hack=false )
|
||||
Kernel.warn("#{self.class.name}.write is deprecated. See REXML::Formatters", uplevel: 1)
|
||||
formatter = if indent > -1
|
||||
REXML::Formatters::Pretty.new( indent )
|
||||
else
|
||||
REXML::Formatters::Default.new
|
||||
end
|
||||
formatter.write( self, writer )
|
||||
end
|
||||
|
||||
# FIXME
|
||||
# This probably won't work properly
|
||||
def xpath
|
||||
path = @parent.xpath
|
||||
path += "/text()"
|
||||
return path
|
||||
end
|
||||
|
||||
# Writes out text, substituting special characters beforehand.
|
||||
# +out+ A String, IO, or any other object supporting <<( String )
|
||||
# +input+ the text to substitute and the write out
|
||||
#
|
||||
# z=utf8.unpack("U*")
|
||||
# ascOut=""
|
||||
# z.each{|r|
|
||||
# if r < 0x100
|
||||
# ascOut.concat(r.chr)
|
||||
# else
|
||||
# ascOut.concat(sprintf("&#x%x;", r))
|
||||
# end
|
||||
# }
|
||||
# puts ascOut
|
||||
def write_with_substitution out, input
|
||||
copy = input.clone
|
||||
# Doing it like this rather than in a loop improves the speed
|
||||
copy.gsub!( SPECIALS[0], SUBSTITUTES[0] )
|
||||
copy.gsub!( SPECIALS[1], SUBSTITUTES[1] )
|
||||
copy.gsub!( SPECIALS[2], SUBSTITUTES[2] )
|
||||
copy.gsub!( SPECIALS[3], SUBSTITUTES[3] )
|
||||
copy.gsub!( SPECIALS[4], SUBSTITUTES[4] )
|
||||
copy.gsub!( SPECIALS[5], SUBSTITUTES[5] )
|
||||
out << copy
|
||||
end
|
||||
|
||||
private
|
||||
def clear_cache
|
||||
@normalized = nil
|
||||
@unnormalized = nil
|
||||
end
|
||||
|
||||
# Reads text, substituting entities
|
||||
def Text::read_with_substitution( input, illegal=nil )
|
||||
copy = input.clone
|
||||
|
||||
if copy =~ illegal
|
||||
raise ParseException.new( "malformed text: Illegal character #$& in \"#{copy}\"" )
|
||||
end if illegal
|
||||
|
||||
copy.gsub!( /\r\n?/, "\n" )
|
||||
if copy.include? ?&
|
||||
copy.gsub!( SETUTITSBUS[0], SLAICEPS[0] )
|
||||
copy.gsub!( SETUTITSBUS[1], SLAICEPS[1] )
|
||||
copy.gsub!( SETUTITSBUS[2], SLAICEPS[2] )
|
||||
copy.gsub!( SETUTITSBUS[3], SLAICEPS[3] )
|
||||
copy.gsub!( SETUTITSBUS[4], SLAICEPS[4] )
|
||||
copy.gsub!( /�*((?:\d+)|(?:x[a-f0-9]+));/ ) {
|
||||
m=$1
|
||||
#m='0' if m==''
|
||||
m = "0#{m}" if m[0] == ?x
|
||||
[Integer(m)].pack('U*')
|
||||
}
|
||||
end
|
||||
copy
|
||||
end
|
||||
|
||||
EREFERENCE = /&(?!#{Entity::NAME};)/
|
||||
# Escapes all possible entities
|
||||
def Text::normalize( input, doctype=nil, entity_filter=nil )
|
||||
copy = input.to_s
|
||||
# Doing it like this rather than in a loop improves the speed
|
||||
#copy = copy.gsub( EREFERENCE, '&' )
|
||||
copy = copy.gsub( "&", "&" )
|
||||
if doctype
|
||||
# Replace all ampersands that aren't part of an entity
|
||||
doctype.entities.each_value do |entity|
|
||||
copy = copy.gsub( entity.value,
|
||||
"&#{entity.name};" ) if entity.value and
|
||||
not( entity_filter and entity_filter.include?(entity.name) )
|
||||
end
|
||||
else
|
||||
# Replace all ampersands that aren't part of an entity
|
||||
DocType::DEFAULT_ENTITIES.each_value do |entity|
|
||||
copy = copy.gsub(entity.value, "&#{entity.name};" )
|
||||
end
|
||||
end
|
||||
copy
|
||||
end
|
||||
|
||||
# Unescapes all possible entities
|
||||
def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil )
|
||||
sum = 0
|
||||
string.gsub( /\r\n?/, "\n" ).gsub( REFERENCE ) {
|
||||
s = Text.expand($&, doctype, filter)
|
||||
if sum + s.bytesize > Security.entity_expansion_text_limit
|
||||
raise "entity expansion has grown too large"
|
||||
else
|
||||
sum += s.bytesize
|
||||
end
|
||||
s
|
||||
}
|
||||
end
|
||||
|
||||
def Text.expand(ref, doctype, filter)
|
||||
if ref[1] == ?#
|
||||
if ref[2] == ?x
|
||||
[ref[3...-1].to_i(16)].pack('U*')
|
||||
else
|
||||
[ref[2...-1].to_i].pack('U*')
|
||||
end
|
||||
elsif ref == '&'
|
||||
'&'
|
||||
elsif filter and filter.include?( ref[1...-1] )
|
||||
ref
|
||||
elsif doctype
|
||||
doctype.entity( ref[1...-1] ) or ref
|
||||
else
|
||||
entity_value = DocType::DEFAULT_ENTITIES[ ref[1...-1] ]
|
||||
entity_value ? entity_value.value : ref
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'parseexception'
|
||||
module REXML
|
||||
class UndefinedNamespaceException < ParseException
|
||||
def initialize( prefix, source, parser )
|
||||
super( "Undefined prefix #{prefix} found" )
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,539 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative "validation"
|
||||
require_relative "../parsers/baseparser"
|
||||
|
||||
module REXML
|
||||
module Validation
|
||||
# Implemented:
|
||||
# * empty
|
||||
# * element
|
||||
# * attribute
|
||||
# * text
|
||||
# * optional
|
||||
# * choice
|
||||
# * oneOrMore
|
||||
# * zeroOrMore
|
||||
# * group
|
||||
# * value
|
||||
# * interleave
|
||||
# * mixed
|
||||
# * ref
|
||||
# * grammar
|
||||
# * start
|
||||
# * define
|
||||
#
|
||||
# Not implemented:
|
||||
# * data
|
||||
# * param
|
||||
# * include
|
||||
# * externalRef
|
||||
# * notAllowed
|
||||
# * anyName
|
||||
# * nsName
|
||||
# * except
|
||||
# * name
|
||||
class RelaxNG
|
||||
include Validator
|
||||
|
||||
INFINITY = 1.0 / 0.0
|
||||
EMPTY = Event.new( nil )
|
||||
TEXT = [:start_element, "text"]
|
||||
attr_accessor :current
|
||||
attr_accessor :count
|
||||
attr_reader :references
|
||||
|
||||
# FIXME: Namespaces
|
||||
def initialize source
|
||||
parser = REXML::Parsers::BaseParser.new( source )
|
||||
|
||||
@count = 0
|
||||
@references = {}
|
||||
@root = @current = Sequence.new(self)
|
||||
@root.previous = true
|
||||
states = [ @current ]
|
||||
begin
|
||||
event = parser.pull
|
||||
case event[0]
|
||||
when :start_element
|
||||
case event[1]
|
||||
when "empty"
|
||||
when "element", "attribute", "text", "value"
|
||||
states[-1] << event
|
||||
when "optional"
|
||||
states << Optional.new( self )
|
||||
states[-2] << states[-1]
|
||||
when "choice"
|
||||
states << Choice.new( self )
|
||||
states[-2] << states[-1]
|
||||
when "oneOrMore"
|
||||
states << OneOrMore.new( self )
|
||||
states[-2] << states[-1]
|
||||
when "zeroOrMore"
|
||||
states << ZeroOrMore.new( self )
|
||||
states[-2] << states[-1]
|
||||
when "group"
|
||||
states << Sequence.new( self )
|
||||
states[-2] << states[-1]
|
||||
when "interleave"
|
||||
states << Interleave.new( self )
|
||||
states[-2] << states[-1]
|
||||
when "mixed"
|
||||
states << Interleave.new( self )
|
||||
states[-2] << states[-1]
|
||||
states[-1] << TEXT
|
||||
when "define"
|
||||
states << [ event[2]["name"] ]
|
||||
when "ref"
|
||||
states[-1] << Ref.new( event[2]["name"] )
|
||||
when "anyName"
|
||||
states << AnyName.new( self )
|
||||
states[-2] << states[-1]
|
||||
when "nsName"
|
||||
when "except"
|
||||
when "name"
|
||||
when "data"
|
||||
when "param"
|
||||
when "include"
|
||||
when "grammar"
|
||||
when "start"
|
||||
when "externalRef"
|
||||
when "notAllowed"
|
||||
end
|
||||
when :end_element
|
||||
case event[1]
|
||||
when "element", "attribute"
|
||||
states[-1] << event
|
||||
when "zeroOrMore", "oneOrMore", "choice", "optional",
|
||||
"interleave", "group", "mixed"
|
||||
states.pop
|
||||
when "define"
|
||||
ref = states.pop
|
||||
@references[ ref.shift ] = ref
|
||||
#when "empty"
|
||||
end
|
||||
when :end_document
|
||||
states[-1] << event
|
||||
when :text
|
||||
states[-1] << event
|
||||
end
|
||||
end while event[0] != :end_document
|
||||
end
|
||||
|
||||
def receive event
|
||||
validate( event )
|
||||
end
|
||||
end
|
||||
|
||||
class State
|
||||
def initialize( context )
|
||||
@previous = []
|
||||
@events = []
|
||||
@current = 0
|
||||
@count = context.count += 1
|
||||
@references = context.references
|
||||
@value = false
|
||||
end
|
||||
|
||||
def reset
|
||||
return if @current == 0
|
||||
@current = 0
|
||||
@events.each {|s| s.reset if s.kind_of? State }
|
||||
end
|
||||
|
||||
def previous=( previous )
|
||||
@previous << previous
|
||||
end
|
||||
|
||||
def next( event )
|
||||
#print "In next with #{event.inspect}. "
|
||||
#p @previous
|
||||
return @previous.pop.next( event ) if @events[@current].nil?
|
||||
expand_ref_in( @events, @current ) if @events[@current].class == Ref
|
||||
if ( @events[@current].kind_of? State )
|
||||
@current += 1
|
||||
@events[@current-1].previous = self
|
||||
return @events[@current-1].next( event )
|
||||
end
|
||||
if ( @events[@current].matches?(event) )
|
||||
@current += 1
|
||||
if @events[@current].nil?
|
||||
return @previous.pop
|
||||
elsif @events[@current].kind_of? State
|
||||
@current += 1
|
||||
@events[@current-1].previous = self
|
||||
return @events[@current-1]
|
||||
else
|
||||
return self
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
# Abbreviated:
|
||||
self.class.name =~ /(?:::)(\w)\w+$/
|
||||
# Full:
|
||||
#self.class.name =~ /(?:::)(\w+)$/
|
||||
"#$1.#@count"
|
||||
end
|
||||
|
||||
def inspect
|
||||
"< #{to_s} #{@events.collect{|e|
|
||||
pre = e == @events[@current] ? '#' : ''
|
||||
pre + e.inspect unless self == e
|
||||
}.join(', ')} >"
|
||||
end
|
||||
|
||||
def expected
|
||||
return [@events[@current]]
|
||||
end
|
||||
|
||||
def <<( event )
|
||||
add_event_to_arry( @events, event )
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def expand_ref_in( arry, ind )
|
||||
new_events = []
|
||||
@references[ arry[ind].to_s ].each{ |evt|
|
||||
add_event_to_arry(new_events,evt)
|
||||
}
|
||||
arry[ind,1] = new_events
|
||||
end
|
||||
|
||||
def add_event_to_arry( arry, evt )
|
||||
evt = generate_event( evt )
|
||||
if evt.kind_of? String
|
||||
arry[-1].event_arg = evt if arry[-1].kind_of? Event and @value
|
||||
@value = false
|
||||
else
|
||||
arry << evt
|
||||
end
|
||||
end
|
||||
|
||||
def generate_event( event )
|
||||
return event if event.kind_of? State or event.class == Ref
|
||||
evt = nil
|
||||
arg = nil
|
||||
case event[0]
|
||||
when :start_element
|
||||
case event[1]
|
||||
when "element"
|
||||
evt = :start_element
|
||||
arg = event[2]["name"]
|
||||
when "attribute"
|
||||
evt = :start_attribute
|
||||
arg = event[2]["name"]
|
||||
when "text"
|
||||
evt = :text
|
||||
when "value"
|
||||
evt = :text
|
||||
@value = true
|
||||
end
|
||||
when :text
|
||||
return event[1]
|
||||
when :end_document
|
||||
return Event.new( event[0] )
|
||||
else # then :end_element
|
||||
case event[1]
|
||||
when "element"
|
||||
evt = :end_element
|
||||
when "attribute"
|
||||
evt = :end_attribute
|
||||
end
|
||||
end
|
||||
return Event.new( evt, arg )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Sequence < State
|
||||
def matches?(event)
|
||||
@events[@current].matches?( event )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Optional < State
|
||||
def next( event )
|
||||
if @current == 0
|
||||
rv = super
|
||||
return rv if rv
|
||||
@prior = @previous.pop
|
||||
return @prior.next( event )
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def matches?(event)
|
||||
@events[@current].matches?(event) ||
|
||||
(@current == 0 and @previous[-1].matches?(event))
|
||||
end
|
||||
|
||||
def expected
|
||||
return [ @prior.expected, @events[0] ].flatten if @current == 0
|
||||
return [@events[@current]]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class ZeroOrMore < Optional
|
||||
def next( event )
|
||||
expand_ref_in( @events, @current ) if @events[@current].class == Ref
|
||||
if ( @events[@current].matches?(event) )
|
||||
@current += 1
|
||||
if @events[@current].nil?
|
||||
@current = 0
|
||||
return self
|
||||
elsif @events[@current].kind_of? State
|
||||
@current += 1
|
||||
@events[@current-1].previous = self
|
||||
return @events[@current-1]
|
||||
else
|
||||
return self
|
||||
end
|
||||
else
|
||||
@prior = @previous.pop
|
||||
return @prior.next( event ) if @current == 0
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def expected
|
||||
return [ @prior.expected, @events[0] ].flatten if @current == 0
|
||||
return [@events[@current]]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class OneOrMore < State
|
||||
def initialize context
|
||||
super
|
||||
@ord = 0
|
||||
end
|
||||
|
||||
def reset
|
||||
super
|
||||
@ord = 0
|
||||
end
|
||||
|
||||
def next( event )
|
||||
expand_ref_in( @events, @current ) if @events[@current].class == Ref
|
||||
if ( @events[@current].matches?(event) )
|
||||
@current += 1
|
||||
@ord += 1
|
||||
if @events[@current].nil?
|
||||
@current = 0
|
||||
return self
|
||||
elsif @events[@current].kind_of? State
|
||||
@current += 1
|
||||
@events[@current-1].previous = self
|
||||
return @events[@current-1]
|
||||
else
|
||||
return self
|
||||
end
|
||||
else
|
||||
return @previous.pop.next( event ) if @current == 0 and @ord > 0
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def matches?( event )
|
||||
@events[@current].matches?(event) ||
|
||||
(@current == 0 and @ord > 0 and @previous[-1].matches?(event))
|
||||
end
|
||||
|
||||
def expected
|
||||
if @current == 0 and @ord > 0
|
||||
return [@previous[-1].expected, @events[0]].flatten
|
||||
else
|
||||
return [@events[@current]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Choice < State
|
||||
def initialize context
|
||||
super
|
||||
@choices = []
|
||||
end
|
||||
|
||||
def reset
|
||||
super
|
||||
@events = []
|
||||
@choices.each { |c| c.each { |s| s.reset if s.kind_of? State } }
|
||||
end
|
||||
|
||||
def <<( event )
|
||||
add_event_to_arry( @choices, event )
|
||||
end
|
||||
|
||||
def next( event )
|
||||
# Make the choice if we haven't
|
||||
if @events.size == 0
|
||||
c = 0 ; max = @choices.size
|
||||
while c < max
|
||||
if @choices[c][0].class == Ref
|
||||
expand_ref_in( @choices[c], 0 )
|
||||
@choices += @choices[c]
|
||||
@choices.delete( @choices[c] )
|
||||
max -= 1
|
||||
else
|
||||
c += 1
|
||||
end
|
||||
end
|
||||
@events = @choices.find { |evt| evt[0].matches? event }
|
||||
# Remove the references
|
||||
# Find the events
|
||||
end
|
||||
unless @events
|
||||
@events = []
|
||||
return nil
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def matches?( event )
|
||||
return @events[@current].matches?( event ) if @events.size > 0
|
||||
!@choices.find{|evt| evt[0].matches?(event)}.nil?
|
||||
end
|
||||
|
||||
def expected
|
||||
return [@events[@current]] if @events.size > 0
|
||||
return @choices.collect do |x|
|
||||
if x[0].kind_of? State
|
||||
x[0].expected
|
||||
else
|
||||
x[0]
|
||||
end
|
||||
end.flatten
|
||||
end
|
||||
|
||||
def inspect
|
||||
"< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' or ')} >"
|
||||
end
|
||||
|
||||
protected
|
||||
def add_event_to_arry( arry, evt )
|
||||
if evt.kind_of? State or evt.class == Ref
|
||||
arry << [evt]
|
||||
elsif evt[0] == :text
|
||||
if arry[-1] and
|
||||
arry[-1][-1].kind_of?( Event ) and
|
||||
arry[-1][-1].event_type == :text and @value
|
||||
|
||||
arry[-1][-1].event_arg = evt[1]
|
||||
@value = false
|
||||
end
|
||||
else
|
||||
arry << [] if evt[0] == :start_element
|
||||
arry[-1] << generate_event( evt )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Interleave < Choice
|
||||
def initialize context
|
||||
super
|
||||
@choice = 0
|
||||
end
|
||||
|
||||
def reset
|
||||
@choice = 0
|
||||
end
|
||||
|
||||
def next_current( event )
|
||||
# Expand references
|
||||
c = 0 ; max = @choices.size
|
||||
while c < max
|
||||
if @choices[c][0].class == Ref
|
||||
expand_ref_in( @choices[c], 0 )
|
||||
@choices += @choices[c]
|
||||
@choices.delete( @choices[c] )
|
||||
max -= 1
|
||||
else
|
||||
c += 1
|
||||
end
|
||||
end
|
||||
@events = @choices[@choice..-1].find { |evt| evt[0].matches? event }
|
||||
@current = 0
|
||||
if @events
|
||||
# reorder the choices
|
||||
old = @choices[@choice]
|
||||
idx = @choices.index( @events )
|
||||
@choices[@choice] = @events
|
||||
@choices[idx] = old
|
||||
@choice += 1
|
||||
end
|
||||
|
||||
@events = [] unless @events
|
||||
end
|
||||
|
||||
|
||||
def next( event )
|
||||
# Find the next series
|
||||
next_current(event) unless @events[@current]
|
||||
return nil unless @events[@current]
|
||||
|
||||
expand_ref_in( @events, @current ) if @events[@current].class == Ref
|
||||
if ( @events[@current].kind_of? State )
|
||||
@current += 1
|
||||
@events[@current-1].previous = self
|
||||
return @events[@current-1].next( event )
|
||||
end
|
||||
return @previous.pop.next( event ) if @events[@current].nil?
|
||||
if ( @events[@current].matches?(event) )
|
||||
@current += 1
|
||||
if @events[@current].nil?
|
||||
return self unless @choices[@choice].nil?
|
||||
return @previous.pop
|
||||
elsif @events[@current].kind_of? State
|
||||
@current += 1
|
||||
@events[@current-1].previous = self
|
||||
return @events[@current-1]
|
||||
else
|
||||
return self
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def matches?( event )
|
||||
return @events[@current].matches?( event ) if @events[@current]
|
||||
!@choices[@choice..-1].find{|evt| evt[0].matches?(event)}.nil?
|
||||
end
|
||||
|
||||
def expected
|
||||
return [@events[@current]] if @events[@current]
|
||||
return @choices[@choice..-1].collect do |x|
|
||||
if x[0].kind_of? State
|
||||
x[0].expected
|
||||
else
|
||||
x[0]
|
||||
end
|
||||
end.flatten
|
||||
end
|
||||
|
||||
def inspect
|
||||
"< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' and ')} >"
|
||||
end
|
||||
end
|
||||
|
||||
class Ref
|
||||
def initialize value
|
||||
@value = value
|
||||
end
|
||||
def to_s
|
||||
@value
|
||||
end
|
||||
def inspect
|
||||
"{#{to_s}}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,144 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'validationexception'
|
||||
|
||||
module REXML
|
||||
module Validation
|
||||
module Validator
|
||||
NILEVENT = [ nil ]
|
||||
def reset
|
||||
@current = @root
|
||||
@root.reset
|
||||
@root.previous = true
|
||||
@attr_stack = []
|
||||
self
|
||||
end
|
||||
def dump
|
||||
puts @root.inspect
|
||||
end
|
||||
def validate( event )
|
||||
@attr_stack = [] unless defined? @attr_stack
|
||||
match = @current.next(event)
|
||||
raise ValidationException.new( "Validation error. Expected: "+
|
||||
@current.expected.join( " or " )+" from #{@current.inspect} "+
|
||||
" but got #{Event.new( event[0], event[1] ).inspect}" ) unless match
|
||||
@current = match
|
||||
|
||||
# Check for attributes
|
||||
case event[0]
|
||||
when :start_element
|
||||
@attr_stack << event[2]
|
||||
begin
|
||||
sattr = [:start_attribute, nil]
|
||||
eattr = [:end_attribute]
|
||||
text = [:text, nil]
|
||||
k, = event[2].find { |key,value|
|
||||
sattr[1] = key
|
||||
m = @current.next( sattr )
|
||||
if m
|
||||
# If the state has text children...
|
||||
if m.matches?( eattr )
|
||||
@current = m
|
||||
else
|
||||
text[1] = value
|
||||
m = m.next( text )
|
||||
text[1] = nil
|
||||
return false unless m
|
||||
@current = m if m
|
||||
end
|
||||
m = @current.next( eattr )
|
||||
if m
|
||||
@current = m
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
}
|
||||
event[2].delete(k) if k
|
||||
end while k
|
||||
when :end_element
|
||||
attrs = @attr_stack.pop
|
||||
raise ValidationException.new( "Validation error. Illegal "+
|
||||
" attributes: #{attrs.inspect}") if attrs.length > 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Event
|
||||
def initialize(event_type, event_arg=nil )
|
||||
@event_type = event_type
|
||||
@event_arg = event_arg
|
||||
end
|
||||
|
||||
attr_reader :event_type
|
||||
attr_accessor :event_arg
|
||||
|
||||
def done?
|
||||
@done
|
||||
end
|
||||
|
||||
def single?
|
||||
return (@event_type != :start_element and @event_type != :start_attribute)
|
||||
end
|
||||
|
||||
def matches?( event )
|
||||
return false unless event[0] == @event_type
|
||||
case event[0]
|
||||
when nil
|
||||
return true
|
||||
when :start_element
|
||||
return true if event[1] == @event_arg
|
||||
when :end_element
|
||||
return true
|
||||
when :start_attribute
|
||||
return true if event[1] == @event_arg
|
||||
when :end_attribute
|
||||
return true
|
||||
when :end_document
|
||||
return true
|
||||
when :text
|
||||
return (@event_arg.nil? or @event_arg == event[1])
|
||||
=begin
|
||||
when :processing_instruction
|
||||
false
|
||||
when :xmldecl
|
||||
false
|
||||
when :start_doctype
|
||||
false
|
||||
when :end_doctype
|
||||
false
|
||||
when :externalentity
|
||||
false
|
||||
when :elementdecl
|
||||
false
|
||||
when :entity
|
||||
false
|
||||
when :attlistdecl
|
||||
false
|
||||
when :notationdecl
|
||||
false
|
||||
when :end_doctype
|
||||
false
|
||||
=end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def ==( other )
|
||||
return false unless other.kind_of? Event
|
||||
@event_type == other.event_type and @event_arg == other.event_arg
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{@event_type.inspect}( #@event_arg )"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
module REXML
|
||||
module Validation
|
||||
class ValidationException < RuntimeError
|
||||
def initialize msg
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,130 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
|
||||
require_relative 'encoding'
|
||||
require_relative 'source'
|
||||
|
||||
module REXML
|
||||
# NEEDS DOCUMENTATION
|
||||
class XMLDecl < Child
|
||||
include Encoding
|
||||
|
||||
DEFAULT_VERSION = "1.0"
|
||||
DEFAULT_ENCODING = "UTF-8"
|
||||
DEFAULT_STANDALONE = "no"
|
||||
START = "<?xml"
|
||||
STOP = "?>"
|
||||
|
||||
attr_accessor :version, :standalone
|
||||
attr_reader :writeencoding, :writethis
|
||||
|
||||
def initialize(version=DEFAULT_VERSION, encoding=nil, standalone=nil)
|
||||
@writethis = true
|
||||
@writeencoding = !encoding.nil?
|
||||
if version.kind_of? XMLDecl
|
||||
super()
|
||||
@version = version.version
|
||||
self.encoding = version.encoding
|
||||
@writeencoding = version.writeencoding
|
||||
@standalone = version.standalone
|
||||
@writethis = version.writethis
|
||||
else
|
||||
super()
|
||||
@version = version
|
||||
self.encoding = encoding
|
||||
@standalone = standalone
|
||||
end
|
||||
@version = DEFAULT_VERSION if @version.nil?
|
||||
end
|
||||
|
||||
def clone
|
||||
XMLDecl.new(self)
|
||||
end
|
||||
|
||||
# indent::
|
||||
# Ignored. There must be no whitespace before an XML declaration
|
||||
# transitive::
|
||||
# Ignored
|
||||
# ie_hack::
|
||||
# Ignored
|
||||
def write(writer, indent=-1, transitive=false, ie_hack=false)
|
||||
return nil unless @writethis or writer.kind_of? Output
|
||||
writer << START
|
||||
writer << " #{content encoding}"
|
||||
writer << STOP
|
||||
end
|
||||
|
||||
def ==( other )
|
||||
other.kind_of?(XMLDecl) and
|
||||
other.version == @version and
|
||||
other.encoding == self.encoding and
|
||||
other.standalone == @standalone
|
||||
end
|
||||
|
||||
def xmldecl version, encoding, standalone
|
||||
@version = version
|
||||
self.encoding = encoding
|
||||
@standalone = standalone
|
||||
end
|
||||
|
||||
def node_type
|
||||
:xmldecl
|
||||
end
|
||||
|
||||
alias :stand_alone? :standalone
|
||||
alias :old_enc= :encoding=
|
||||
|
||||
def encoding=( enc )
|
||||
if enc.nil?
|
||||
self.old_enc = "UTF-8"
|
||||
@writeencoding = false
|
||||
else
|
||||
self.old_enc = enc
|
||||
@writeencoding = true
|
||||
end
|
||||
self.dowrite
|
||||
end
|
||||
|
||||
# Only use this if you do not want the XML declaration to be written;
|
||||
# this object is ignored by the XML writer. Otherwise, instantiate your
|
||||
# own XMLDecl and add it to the document.
|
||||
#
|
||||
# Note that XML 1.1 documents *must* include an XML declaration
|
||||
def XMLDecl.default
|
||||
rv = XMLDecl.new( "1.0" )
|
||||
rv.nowrite
|
||||
rv
|
||||
end
|
||||
|
||||
def nowrite
|
||||
@writethis = false
|
||||
end
|
||||
|
||||
def dowrite
|
||||
@writethis = true
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{START} ... #{STOP}"
|
||||
end
|
||||
|
||||
private
|
||||
def content(enc)
|
||||
context = nil
|
||||
context = parent.context if parent
|
||||
if context and context[:prologue_quote] == :quote
|
||||
quote = "\""
|
||||
else
|
||||
quote = "'"
|
||||
end
|
||||
|
||||
rv = "version=#{quote}#{@version}#{quote}"
|
||||
if @writeencoding or enc !~ /\Autf-8\z/i
|
||||
rv << " encoding=#{quote}#{enc}#{quote}"
|
||||
end
|
||||
if @standalone
|
||||
rv << " standalone=#{quote}#{@standalone}#{quote}"
|
||||
end
|
||||
rv
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,85 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
module REXML
|
||||
# Defines a number of tokens used for parsing XML. Not for general
|
||||
# consumption.
|
||||
module XMLTokens
|
||||
# From http://www.w3.org/TR/REC-xml/#sec-common-syn
|
||||
#
|
||||
# [4] NameStartChar ::=
|
||||
# ":" |
|
||||
# [A-Z] |
|
||||
# "_" |
|
||||
# [a-z] |
|
||||
# [#xC0-#xD6] |
|
||||
# [#xD8-#xF6] |
|
||||
# [#xF8-#x2FF] |
|
||||
# [#x370-#x37D] |
|
||||
# [#x37F-#x1FFF] |
|
||||
# [#x200C-#x200D] |
|
||||
# [#x2070-#x218F] |
|
||||
# [#x2C00-#x2FEF] |
|
||||
# [#x3001-#xD7FF] |
|
||||
# [#xF900-#xFDCF] |
|
||||
# [#xFDF0-#xFFFD] |
|
||||
# [#x10000-#xEFFFF]
|
||||
name_start_chars = [
|
||||
":",
|
||||
"A-Z",
|
||||
"_",
|
||||
"a-z",
|
||||
"\\u00C0-\\u00D6",
|
||||
"\\u00D8-\\u00F6",
|
||||
"\\u00F8-\\u02FF",
|
||||
"\\u0370-\\u037D",
|
||||
"\\u037F-\\u1FFF",
|
||||
"\\u200C-\\u200D",
|
||||
"\\u2070-\\u218F",
|
||||
"\\u2C00-\\u2FEF",
|
||||
"\\u3001-\\uD7FF",
|
||||
"\\uF900-\\uFDCF",
|
||||
"\\uFDF0-\\uFFFD",
|
||||
"\\u{10000}-\\u{EFFFF}",
|
||||
]
|
||||
# From http://www.w3.org/TR/REC-xml/#sec-common-syn
|
||||
#
|
||||
# [4a] NameChar ::=
|
||||
# NameStartChar |
|
||||
# "-" |
|
||||
# "." |
|
||||
# [0-9] |
|
||||
# #xB7 |
|
||||
# [#x0300-#x036F] |
|
||||
# [#x203F-#x2040]
|
||||
name_chars = name_start_chars + [
|
||||
"\\-",
|
||||
"\\.",
|
||||
"0-9",
|
||||
"\\u00B7",
|
||||
"\\u0300-\\u036F",
|
||||
"\\u203F-\\u2040",
|
||||
]
|
||||
NAME_START_CHAR = "[#{name_start_chars.join('')}]"
|
||||
NAME_CHAR = "[#{name_chars.join('')}]"
|
||||
NAMECHAR = NAME_CHAR # deprecated. Use NAME_CHAR instead.
|
||||
|
||||
# From http://www.w3.org/TR/xml-names11/#NT-NCName
|
||||
#
|
||||
# [6] NCNameStartChar ::= NameStartChar - ':'
|
||||
ncname_start_chars = name_start_chars - [":"]
|
||||
# From http://www.w3.org/TR/xml-names11/#NT-NCName
|
||||
#
|
||||
# [5] NCNameChar ::= NameChar - ':'
|
||||
ncname_chars = name_chars - [":"]
|
||||
NCNAME_STR = "[#{ncname_start_chars.join('')}][#{ncname_chars.join('')}]*"
|
||||
NAME_STR = "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
|
||||
|
||||
NAME = "(#{NAME_START_CHAR}#{NAME_CHAR}*)"
|
||||
NMTOKEN = "(?:#{NAME_CHAR})+"
|
||||
NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*"
|
||||
REFERENCE = "(?:&#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)"
|
||||
|
||||
#REFERENCE = "(?:#{ENTITYREF}|#{CHARREF})"
|
||||
#ENTITYREF = "&#{NAME};"
|
||||
#CHARREF = "&#\\d+;|&#x[0-9a-fA-F]+;"
|
||||
end
|
||||
end
|
|
@ -1,81 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
require_relative 'functions'
|
||||
require_relative 'xpath_parser'
|
||||
|
||||
module REXML
|
||||
# Wrapper class. Use this class to access the XPath functions.
|
||||
class XPath
|
||||
include Functions
|
||||
# A base Hash object, supposing to be used when initializing a
|
||||
# default empty namespaces set, but is currently unused.
|
||||
# TODO: either set the namespaces=EMPTY_HASH, or deprecate this.
|
||||
EMPTY_HASH = {}
|
||||
|
||||
# Finds and returns the first node that matches the supplied xpath.
|
||||
# element::
|
||||
# The context element
|
||||
# path::
|
||||
# The xpath to search for. If not supplied or nil, returns the first
|
||||
# node matching '*'.
|
||||
# namespaces::
|
||||
# If supplied, a Hash which defines a namespace mapping.
|
||||
# variables::
|
||||
# If supplied, a Hash which maps $variables in the query
|
||||
# to values. This can be used to avoid XPath injection attacks
|
||||
# or to automatically handle escaping string values.
|
||||
#
|
||||
# XPath.first( node )
|
||||
# XPath.first( doc, "//b"} )
|
||||
# XPath.first( node, "a/x:b", { "x"=>"http://doofus" } )
|
||||
# XPath.first( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"})
|
||||
def XPath::first(element, path=nil, namespaces=nil, variables={}, options={})
|
||||
raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash)
|
||||
raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash)
|
||||
parser = XPathParser.new(**options)
|
||||
parser.namespaces = namespaces
|
||||
parser.variables = variables
|
||||
path = "*" unless path
|
||||
element = [element] unless element.kind_of? Array
|
||||
parser.parse(path, element).flatten[0]
|
||||
end
|
||||
|
||||
# Iterates over nodes that match the given path, calling the supplied
|
||||
# block with the match.
|
||||
# element::
|
||||
# The context element
|
||||
# path::
|
||||
# The xpath to search for. If not supplied or nil, defaults to '*'
|
||||
# namespaces::
|
||||
# If supplied, a Hash which defines a namespace mapping
|
||||
# variables::
|
||||
# If supplied, a Hash which maps $variables in the query
|
||||
# to values. This can be used to avoid XPath injection attacks
|
||||
# or to automatically handle escaping string values.
|
||||
#
|
||||
# XPath.each( node ) { |el| ... }
|
||||
# XPath.each( node, '/*[@attr='v']' ) { |el| ... }
|
||||
# XPath.each( node, 'ancestor::x' ) { |el| ... }
|
||||
# XPath.each( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"}) \
|
||||
# {|el| ... }
|
||||
def XPath::each(element, path=nil, namespaces=nil, variables={}, options={}, &block)
|
||||
raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash)
|
||||
raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash)
|
||||
parser = XPathParser.new(**options)
|
||||
parser.namespaces = namespaces
|
||||
parser.variables = variables
|
||||
path = "*" unless path
|
||||
element = [element] unless element.kind_of? Array
|
||||
parser.parse(path, element).each( &block )
|
||||
end
|
||||
|
||||
# Returns an array of nodes matching a given XPath.
|
||||
def XPath::match(element, path=nil, namespaces=nil, variables={}, options={})
|
||||
parser = XPathParser.new(**options)
|
||||
parser.namespaces = namespaces
|
||||
parser.variables = variables
|
||||
path = "*" unless path
|
||||
element = [element] unless element.kind_of? Array
|
||||
parser.parse(path,element)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,968 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
|
||||
require "pp"
|
||||
|
||||
require_relative 'namespace'
|
||||
require_relative 'xmltokens'
|
||||
require_relative 'attribute'
|
||||
require_relative 'parsers/xpathparser'
|
||||
|
||||
class Object
|
||||
# provides a unified +clone+ operation, for REXML::XPathParser
|
||||
# to use across multiple Object types
|
||||
def dclone
|
||||
clone
|
||||
end
|
||||
end
|
||||
class Symbol
|
||||
# provides a unified +clone+ operation, for REXML::XPathParser
|
||||
# to use across multiple Object types
|
||||
def dclone ; self ; end
|
||||
end
|
||||
class Integer
|
||||
# provides a unified +clone+ operation, for REXML::XPathParser
|
||||
# to use across multiple Object types
|
||||
def dclone ; self ; end
|
||||
end
|
||||
class Float
|
||||
# provides a unified +clone+ operation, for REXML::XPathParser
|
||||
# to use across multiple Object types
|
||||
def dclone ; self ; end
|
||||
end
|
||||
class Array
|
||||
# provides a unified +clone+ operation, for REXML::XPathParser
|
||||
# to use across multiple Object+ types
|
||||
def dclone
|
||||
klone = self.clone
|
||||
klone.clear
|
||||
self.each{|v| klone << v.dclone}
|
||||
klone
|
||||
end
|
||||
end
|
||||
|
||||
module REXML
|
||||
# You don't want to use this class. Really. Use XPath, which is a wrapper
|
||||
# for this class. Believe me. You don't want to poke around in here.
|
||||
# There is strange, dark magic at work in this code. Beware. Go back! Go
|
||||
# back while you still can!
|
||||
class XPathParser
|
||||
include XMLTokens
|
||||
LITERAL = /^'([^']*)'|^"([^"]*)"/u
|
||||
|
||||
DEBUG = (ENV["REXML_XPATH_PARSER_DEBUG"] == "true")
|
||||
|
||||
def initialize(strict: false)
|
||||
@debug = DEBUG
|
||||
@parser = REXML::Parsers::XPathParser.new
|
||||
@namespaces = nil
|
||||
@variables = {}
|
||||
@nest = 0
|
||||
@strict = strict
|
||||
end
|
||||
|
||||
def namespaces=( namespaces={} )
|
||||
Functions::namespace_context = namespaces
|
||||
@namespaces = namespaces
|
||||
end
|
||||
|
||||
def variables=( vars={} )
|
||||
Functions::variables = vars
|
||||
@variables = vars
|
||||
end
|
||||
|
||||
def parse path, nodeset
|
||||
path_stack = @parser.parse( path )
|
||||
match( path_stack, nodeset )
|
||||
end
|
||||
|
||||
def get_first path, nodeset
|
||||
path_stack = @parser.parse( path )
|
||||
first( path_stack, nodeset )
|
||||
end
|
||||
|
||||
def predicate path, nodeset
|
||||
path_stack = @parser.parse( path )
|
||||
match( path_stack, nodeset )
|
||||
end
|
||||
|
||||
def []=( variable_name, value )
|
||||
@variables[ variable_name ] = value
|
||||
end
|
||||
|
||||
|
||||
# Performs a depth-first (document order) XPath search, and returns the
|
||||
# first match. This is the fastest, lightest way to return a single result.
|
||||
#
|
||||
# FIXME: This method is incomplete!
|
||||
def first( path_stack, node )
|
||||
return nil if path.size == 0
|
||||
|
||||
case path[0]
|
||||
when :document
|
||||
# do nothing
|
||||
return first( path[1..-1], node )
|
||||
when :child
|
||||
for c in node.children
|
||||
r = first( path[1..-1], c )
|
||||
return r if r
|
||||
end
|
||||
when :qname
|
||||
name = path[2]
|
||||
if node.name == name
|
||||
return node if path.size == 3
|
||||
return first( path[3..-1], node )
|
||||
else
|
||||
return nil
|
||||
end
|
||||
when :descendant_or_self
|
||||
r = first( path[1..-1], node )
|
||||
return r if r
|
||||
for c in node.children
|
||||
r = first( path, c )
|
||||
return r if r
|
||||
end
|
||||
when :node
|
||||
return first( path[1..-1], node )
|
||||
when :any
|
||||
return first( path[1..-1], node )
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
def match(path_stack, nodeset)
|
||||
nodeset = nodeset.collect.with_index do |node, i|
|
||||
position = i + 1
|
||||
XPathNode.new(node, position: position)
|
||||
end
|
||||
result = expr(path_stack, nodeset)
|
||||
case result
|
||||
when Array # nodeset
|
||||
unnode(result)
|
||||
else
|
||||
[result]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def strict?
|
||||
@strict
|
||||
end
|
||||
|
||||
# Returns a String namespace for a node, given a prefix
|
||||
# The rules are:
|
||||
#
|
||||
# 1. Use the supplied namespace mapping first.
|
||||
# 2. If no mapping was supplied, use the context node to look up the namespace
|
||||
def get_namespace( node, prefix )
|
||||
if @namespaces
|
||||
return @namespaces[prefix] || ''
|
||||
else
|
||||
return node.namespace( prefix ) if node.node_type == :element
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Expr takes a stack of path elements and a set of nodes (either a Parent
|
||||
# or an Array and returns an Array of matching nodes
|
||||
def expr( path_stack, nodeset, context=nil )
|
||||
enter(:expr, path_stack, nodeset) if @debug
|
||||
return nodeset if path_stack.length == 0 || nodeset.length == 0
|
||||
while path_stack.length > 0
|
||||
trace(:while, path_stack, nodeset) if @debug
|
||||
if nodeset.length == 0
|
||||
path_stack.clear
|
||||
return []
|
||||
end
|
||||
op = path_stack.shift
|
||||
case op
|
||||
when :document
|
||||
first_raw_node = nodeset.first.raw_node
|
||||
nodeset = [XPathNode.new(first_raw_node.root_node, position: 1)]
|
||||
when :self
|
||||
nodeset = step(path_stack) do
|
||||
[nodeset]
|
||||
end
|
||||
when :child
|
||||
nodeset = step(path_stack) do
|
||||
child(nodeset)
|
||||
end
|
||||
when :literal
|
||||
trace(:literal, path_stack, nodeset) if @debug
|
||||
return path_stack.shift
|
||||
when :attribute
|
||||
nodeset = step(path_stack, any_type: :attribute) do
|
||||
nodesets = []
|
||||
nodeset.each do |node|
|
||||
raw_node = node.raw_node
|
||||
next unless raw_node.node_type == :element
|
||||
attributes = raw_node.attributes
|
||||
next if attributes.empty?
|
||||
nodesets << attributes.each_attribute.collect.with_index do |attribute, i|
|
||||
XPathNode.new(attribute, position: i + 1)
|
||||
end
|
||||
end
|
||||
nodesets
|
||||
end
|
||||
when :namespace
|
||||
pre_defined_namespaces = {
|
||||
"xml" => "http://www.w3.org/XML/1998/namespace",
|
||||
}
|
||||
nodeset = step(path_stack, any_type: :namespace) do
|
||||
nodesets = []
|
||||
nodeset.each do |node|
|
||||
raw_node = node.raw_node
|
||||
case raw_node.node_type
|
||||
when :element
|
||||
if @namespaces
|
||||
nodesets << pre_defined_namespaces.merge(@namespaces)
|
||||
else
|
||||
nodesets << pre_defined_namespaces.merge(raw_node.namespaces)
|
||||
end
|
||||
when :attribute
|
||||
if @namespaces
|
||||
nodesets << pre_defined_namespaces.merge(@namespaces)
|
||||
else
|
||||
nodesets << pre_defined_namespaces.merge(raw_node.element.namespaces)
|
||||
end
|
||||
end
|
||||
end
|
||||
nodesets
|
||||
end
|
||||
when :parent
|
||||
nodeset = step(path_stack) do
|
||||
nodesets = []
|
||||
nodeset.each do |node|
|
||||
raw_node = node.raw_node
|
||||
if raw_node.node_type == :attribute
|
||||
parent = raw_node.element
|
||||
else
|
||||
parent = raw_node.parent
|
||||
end
|
||||
nodesets << [XPathNode.new(parent, position: 1)] if parent
|
||||
end
|
||||
nodesets
|
||||
end
|
||||
when :ancestor
|
||||
nodeset = step(path_stack) do
|
||||
nodesets = []
|
||||
# new_nodes = {}
|
||||
nodeset.each do |node|
|
||||
raw_node = node.raw_node
|
||||
new_nodeset = []
|
||||
while raw_node.parent
|
||||
raw_node = raw_node.parent
|
||||
# next if new_nodes.key?(node)
|
||||
new_nodeset << XPathNode.new(raw_node,
|
||||
position: new_nodeset.size + 1)
|
||||
# new_nodes[node] = true
|
||||
end
|
||||
nodesets << new_nodeset unless new_nodeset.empty?
|
||||
end
|
||||
nodesets
|
||||
end
|
||||
when :ancestor_or_self
|
||||
nodeset = step(path_stack) do
|
||||
nodesets = []
|
||||
# new_nodes = {}
|
||||
nodeset.each do |node|
|
||||
raw_node = node.raw_node
|
||||
next unless raw_node.node_type == :element
|
||||
new_nodeset = [XPathNode.new(raw_node, position: 1)]
|
||||
# new_nodes[node] = true
|
||||
while raw_node.parent
|
||||
raw_node = raw_node.parent
|
||||
# next if new_nodes.key?(node)
|
||||
new_nodeset << XPathNode.new(raw_node,
|
||||
position: new_nodeset.size + 1)
|
||||
# new_nodes[node] = true
|
||||
end
|
||||
nodesets << new_nodeset unless new_nodeset.empty?
|
||||
end
|
||||
nodesets
|
||||
end
|
||||
when :descendant_or_self
|
||||
nodeset = step(path_stack) do
|
||||
descendant(nodeset, true)
|
||||
end
|
||||
when :descendant
|
||||
nodeset = step(path_stack) do
|
||||
descendant(nodeset, false)
|
||||
end
|
||||
when :following_sibling
|
||||
nodeset = step(path_stack) do
|
||||
nodesets = []
|
||||
nodeset.each do |node|
|
||||
raw_node = node.raw_node
|
||||
next unless raw_node.respond_to?(:parent)
|
||||
next if raw_node.parent.nil?
|
||||
all_siblings = raw_node.parent.children
|
||||
current_index = all_siblings.index(raw_node)
|
||||
following_siblings = all_siblings[(current_index + 1)..-1]
|
||||
next if following_siblings.empty?
|
||||
nodesets << following_siblings.collect.with_index do |sibling, i|
|
||||
XPathNode.new(sibling, position: i + 1)
|
||||
end
|
||||
end
|
||||
nodesets
|
||||
end
|
||||
when :preceding_sibling
|
||||
nodeset = step(path_stack, order: :reverse) do
|
||||
nodesets = []
|
||||
nodeset.each do |node|
|
||||
raw_node = node.raw_node
|
||||
next unless raw_node.respond_to?(:parent)
|
||||
next if raw_node.parent.nil?
|
||||
all_siblings = raw_node.parent.children
|
||||
current_index = all_siblings.index(raw_node)
|
||||
preceding_siblings = all_siblings[0, current_index].reverse
|
||||
next if preceding_siblings.empty?
|
||||
nodesets << preceding_siblings.collect.with_index do |sibling, i|
|
||||
XPathNode.new(sibling, position: i + 1)
|
||||
end
|
||||
end
|
||||
nodesets
|
||||
end
|
||||
when :preceding
|
||||
nodeset = step(path_stack, order: :reverse) do
|
||||
unnode(nodeset) do |node|
|
||||
preceding(node)
|
||||
end
|
||||
end
|
||||
when :following
|
||||
nodeset = step(path_stack) do
|
||||
unnode(nodeset) do |node|
|
||||
following(node)
|
||||
end
|
||||
end
|
||||
when :variable
|
||||
var_name = path_stack.shift
|
||||
return [@variables[var_name]]
|
||||
|
||||
when :eq, :neq, :lt, :lteq, :gt, :gteq
|
||||
left = expr( path_stack.shift, nodeset.dup, context )
|
||||
right = expr( path_stack.shift, nodeset.dup, context )
|
||||
res = equality_relational_compare( left, op, right )
|
||||
trace(op, left, right, res) if @debug
|
||||
return res
|
||||
|
||||
when :or
|
||||
left = expr(path_stack.shift, nodeset.dup, context)
|
||||
return true if Functions.boolean(left)
|
||||
right = expr(path_stack.shift, nodeset.dup, context)
|
||||
return Functions.boolean(right)
|
||||
|
||||
when :and
|
||||
left = expr(path_stack.shift, nodeset.dup, context)
|
||||
return false unless Functions.boolean(left)
|
||||
right = expr(path_stack.shift, nodeset.dup, context)
|
||||
return Functions.boolean(right)
|
||||
|
||||
when :div, :mod, :mult, :plus, :minus
|
||||
left = expr(path_stack.shift, nodeset, context)
|
||||
right = expr(path_stack.shift, nodeset, context)
|
||||
left = unnode(left) if left.is_a?(Array)
|
||||
right = unnode(right) if right.is_a?(Array)
|
||||
left = Functions::number(left)
|
||||
right = Functions::number(right)
|
||||
case op
|
||||
when :div
|
||||
return left / right
|
||||
when :mod
|
||||
return left % right
|
||||
when :mult
|
||||
return left * right
|
||||
when :plus
|
||||
return left + right
|
||||
when :minus
|
||||
return left - right
|
||||
else
|
||||
raise "[BUG] Unexpected operator: <#{op.inspect}>"
|
||||
end
|
||||
when :union
|
||||
left = expr( path_stack.shift, nodeset, context )
|
||||
right = expr( path_stack.shift, nodeset, context )
|
||||
left = unnode(left) if left.is_a?(Array)
|
||||
right = unnode(right) if right.is_a?(Array)
|
||||
return (left | right)
|
||||
when :neg
|
||||
res = expr( path_stack, nodeset, context )
|
||||
res = unnode(res) if res.is_a?(Array)
|
||||
return -Functions.number(res)
|
||||
when :not
|
||||
when :function
|
||||
func_name = path_stack.shift.tr('-','_')
|
||||
arguments = path_stack.shift
|
||||
|
||||
if nodeset.size != 1
|
||||
message = "[BUG] Node set size must be 1 for function call: "
|
||||
message += "<#{func_name}>: <#{nodeset.inspect}>: "
|
||||
message += "<#{arguments.inspect}>"
|
||||
raise message
|
||||
end
|
||||
|
||||
node = nodeset.first
|
||||
if context
|
||||
target_context = context
|
||||
else
|
||||
target_context = {:size => nodeset.size}
|
||||
if node.is_a?(XPathNode)
|
||||
target_context[:node] = node.raw_node
|
||||
target_context[:index] = node.position
|
||||
else
|
||||
target_context[:node] = node
|
||||
target_context[:index] = 1
|
||||
end
|
||||
end
|
||||
args = arguments.dclone.collect do |arg|
|
||||
result = expr(arg, nodeset, target_context)
|
||||
result = unnode(result) if result.is_a?(Array)
|
||||
result
|
||||
end
|
||||
Functions.context = target_context
|
||||
return Functions.send(func_name, *args)
|
||||
|
||||
else
|
||||
raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>"
|
||||
end
|
||||
end # while
|
||||
return nodeset
|
||||
ensure
|
||||
leave(:expr, path_stack, nodeset) if @debug
|
||||
end
|
||||
|
||||
def step(path_stack, any_type: :element, order: :forward)
|
||||
nodesets = yield
|
||||
begin
|
||||
enter(:step, path_stack, nodesets) if @debug
|
||||
nodesets = node_test(path_stack, nodesets, any_type: any_type)
|
||||
while path_stack[0] == :predicate
|
||||
path_stack.shift # :predicate
|
||||
predicate_expression = path_stack.shift.dclone
|
||||
nodesets = evaluate_predicate(predicate_expression, nodesets)
|
||||
end
|
||||
if nodesets.size == 1
|
||||
ordered_nodeset = nodesets[0]
|
||||
else
|
||||
raw_nodes = []
|
||||
nodesets.each do |nodeset|
|
||||
nodeset.each do |node|
|
||||
if node.respond_to?(:raw_node)
|
||||
raw_nodes << node.raw_node
|
||||
else
|
||||
raw_nodes << node
|
||||
end
|
||||
end
|
||||
end
|
||||
ordered_nodeset = sort(raw_nodes, order)
|
||||
end
|
||||
new_nodeset = []
|
||||
ordered_nodeset.each do |node|
|
||||
# TODO: Remove duplicated
|
||||
new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
|
||||
end
|
||||
new_nodeset
|
||||
ensure
|
||||
leave(:step, path_stack, new_nodeset) if @debug
|
||||
end
|
||||
end
|
||||
|
||||
def node_test(path_stack, nodesets, any_type: :element)
|
||||
enter(:node_test, path_stack, nodesets) if @debug
|
||||
operator = path_stack.shift
|
||||
case operator
|
||||
when :qname
|
||||
prefix = path_stack.shift
|
||||
name = path_stack.shift
|
||||
new_nodesets = nodesets.collect do |nodeset|
|
||||
filter_nodeset(nodeset) do |node|
|
||||
raw_node = node.raw_node
|
||||
case raw_node.node_type
|
||||
when :element
|
||||
if prefix.nil?
|
||||
raw_node.name == name
|
||||
elsif prefix.empty?
|
||||
if strict?
|
||||
raw_node.name == name and raw_node.namespace == ""
|
||||
else
|
||||
# FIXME: This DOUBLES the time XPath searches take
|
||||
ns = get_namespace(raw_node, prefix)
|
||||
raw_node.name == name and raw_node.namespace == ns
|
||||
end
|
||||
else
|
||||
# FIXME: This DOUBLES the time XPath searches take
|
||||
ns = get_namespace(raw_node, prefix)
|
||||
raw_node.name == name and raw_node.namespace == ns
|
||||
end
|
||||
when :attribute
|
||||
if prefix.nil?
|
||||
raw_node.name == name
|
||||
elsif prefix.empty?
|
||||
raw_node.name == name and raw_node.namespace == ""
|
||||
else
|
||||
# FIXME: This DOUBLES the time XPath searches take
|
||||
ns = get_namespace(raw_node.element, prefix)
|
||||
raw_node.name == name and raw_node.namespace == ns
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
when :namespace
|
||||
prefix = path_stack.shift
|
||||
new_nodesets = nodesets.collect do |nodeset|
|
||||
filter_nodeset(nodeset) do |node|
|
||||
raw_node = node.raw_node
|
||||
case raw_node.node_type
|
||||
when :element
|
||||
namespaces = @namespaces || raw_node.namespaces
|
||||
raw_node.namespace == namespaces[prefix]
|
||||
when :attribute
|
||||
namespaces = @namespaces || raw_node.element.namespaces
|
||||
raw_node.namespace == namespaces[prefix]
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
when :any
|
||||
new_nodesets = nodesets.collect do |nodeset|
|
||||
filter_nodeset(nodeset) do |node|
|
||||
raw_node = node.raw_node
|
||||
raw_node.node_type == any_type
|
||||
end
|
||||
end
|
||||
when :comment
|
||||
new_nodesets = nodesets.collect do |nodeset|
|
||||
filter_nodeset(nodeset) do |node|
|
||||
raw_node = node.raw_node
|
||||
raw_node.node_type == :comment
|
||||
end
|
||||
end
|
||||
when :text
|
||||
new_nodesets = nodesets.collect do |nodeset|
|
||||
filter_nodeset(nodeset) do |node|
|
||||
raw_node = node.raw_node
|
||||
raw_node.node_type == :text
|
||||
end
|
||||
end
|
||||
when :processing_instruction
|
||||
target = path_stack.shift
|
||||
new_nodesets = nodesets.collect do |nodeset|
|
||||
filter_nodeset(nodeset) do |node|
|
||||
raw_node = node.raw_node
|
||||
(raw_node.node_type == :processing_instruction) and
|
||||
(target.empty? or (raw_node.target == target))
|
||||
end
|
||||
end
|
||||
when :node
|
||||
new_nodesets = nodesets.collect do |nodeset|
|
||||
filter_nodeset(nodeset) do |node|
|
||||
true
|
||||
end
|
||||
end
|
||||
else
|
||||
message = "[BUG] Unexpected node test: " +
|
||||
"<#{operator.inspect}>: <#{path_stack.inspect}>"
|
||||
raise message
|
||||
end
|
||||
new_nodesets
|
||||
ensure
|
||||
leave(:node_test, path_stack, new_nodesets) if @debug
|
||||
end
|
||||
|
||||
def filter_nodeset(nodeset)
|
||||
new_nodeset = []
|
||||
nodeset.each do |node|
|
||||
next unless yield(node)
|
||||
new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
|
||||
end
|
||||
new_nodeset
|
||||
end
|
||||
|
||||
def evaluate_predicate(expression, nodesets)
|
||||
enter(:predicate, expression, nodesets) if @debug
|
||||
new_nodesets = nodesets.collect do |nodeset|
|
||||
new_nodeset = []
|
||||
subcontext = { :size => nodeset.size }
|
||||
nodeset.each_with_index do |node, index|
|
||||
if node.is_a?(XPathNode)
|
||||
subcontext[:node] = node.raw_node
|
||||
subcontext[:index] = node.position
|
||||
else
|
||||
subcontext[:node] = node
|
||||
subcontext[:index] = index + 1
|
||||
end
|
||||
result = expr(expression.dclone, [node], subcontext)
|
||||
trace(:predicate_evaluate, expression, node, subcontext, result) if @debug
|
||||
result = result[0] if result.kind_of? Array and result.length == 1
|
||||
if result.kind_of? Numeric
|
||||
if result == node.position
|
||||
new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
|
||||
end
|
||||
elsif result.instance_of? Array
|
||||
if result.size > 0 and result.inject(false) {|k,s| s or k}
|
||||
if result.size > 0
|
||||
new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
|
||||
end
|
||||
end
|
||||
else
|
||||
if result
|
||||
new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
new_nodeset
|
||||
end
|
||||
new_nodesets
|
||||
ensure
|
||||
leave(:predicate, new_nodesets) if @debug
|
||||
end
|
||||
|
||||
def trace(*args)
|
||||
indent = " " * @nest
|
||||
PP.pp(args, "").each_line do |line|
|
||||
puts("#{indent}#{line}")
|
||||
end
|
||||
end
|
||||
|
||||
def enter(tag, *args)
|
||||
trace(:enter, tag, *args)
|
||||
@nest += 1
|
||||
end
|
||||
|
||||
def leave(tag, *args)
|
||||
@nest -= 1
|
||||
trace(:leave, tag, *args)
|
||||
end
|
||||
|
||||
# Reorders an array of nodes so that they are in document order
|
||||
# It tries to do this efficiently.
|
||||
#
|
||||
# FIXME: I need to get rid of this, but the issue is that most of the XPath
|
||||
# interpreter functions as a filter, which means that we lose context going
|
||||
# in and out of function calls. If I knew what the index of the nodes was,
|
||||
# I wouldn't have to do this. Maybe add a document IDX for each node?
|
||||
# Problems with mutable documents. Or, rewrite everything.
|
||||
def sort(array_of_nodes, order)
|
||||
new_arry = []
|
||||
array_of_nodes.each { |node|
|
||||
node_idx = []
|
||||
np = node.node_type == :attribute ? node.element : node
|
||||
while np.parent and np.parent.node_type == :element
|
||||
node_idx << np.parent.index( np )
|
||||
np = np.parent
|
||||
end
|
||||
new_arry << [ node_idx.reverse, node ]
|
||||
}
|
||||
ordered = new_arry.sort_by do |index, node|
|
||||
if order == :forward
|
||||
index
|
||||
else
|
||||
-index
|
||||
end
|
||||
end
|
||||
ordered.collect do |_index, node|
|
||||
node
|
||||
end
|
||||
end
|
||||
|
||||
def descendant(nodeset, include_self)
|
||||
nodesets = []
|
||||
nodeset.each do |node|
|
||||
new_nodeset = []
|
||||
new_nodes = {}
|
||||
descendant_recursive(node.raw_node, new_nodeset, new_nodes, include_self)
|
||||
nodesets << new_nodeset unless new_nodeset.empty?
|
||||
end
|
||||
nodesets
|
||||
end
|
||||
|
||||
def descendant_recursive(raw_node, new_nodeset, new_nodes, include_self)
|
||||
if include_self
|
||||
return if new_nodes.key?(raw_node)
|
||||
new_nodeset << XPathNode.new(raw_node, position: new_nodeset.size + 1)
|
||||
new_nodes[raw_node] = true
|
||||
end
|
||||
|
||||
node_type = raw_node.node_type
|
||||
if node_type == :element or node_type == :document
|
||||
raw_node.children.each do |child|
|
||||
descendant_recursive(child, new_nodeset, new_nodes, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Builds a nodeset of all of the preceding nodes of the supplied node,
|
||||
# in reverse document order
|
||||
# preceding:: includes every element in the document that precedes this node,
|
||||
# except for ancestors
|
||||
def preceding(node)
|
||||
ancestors = []
|
||||
parent = node.parent
|
||||
while parent
|
||||
ancestors << parent
|
||||
parent = parent.parent
|
||||
end
|
||||
|
||||
precedings = []
|
||||
preceding_node = preceding_node_of(node)
|
||||
while preceding_node
|
||||
if ancestors.include?(preceding_node)
|
||||
ancestors.delete(preceding_node)
|
||||
else
|
||||
precedings << XPathNode.new(preceding_node,
|
||||
position: precedings.size + 1)
|
||||
end
|
||||
preceding_node = preceding_node_of(preceding_node)
|
||||
end
|
||||
precedings
|
||||
end
|
||||
|
||||
def preceding_node_of( node )
|
||||
psn = node.previous_sibling_node
|
||||
if psn.nil?
|
||||
if node.parent.nil? or node.parent.class == Document
|
||||
return nil
|
||||
end
|
||||
return node.parent
|
||||
#psn = preceding_node_of( node.parent )
|
||||
end
|
||||
while psn and psn.kind_of? Element and psn.children.size > 0
|
||||
psn = psn.children[-1]
|
||||
end
|
||||
psn
|
||||
end
|
||||
|
||||
def following(node)
|
||||
followings = []
|
||||
following_node = next_sibling_node(node)
|
||||
while following_node
|
||||
followings << XPathNode.new(following_node,
|
||||
position: followings.size + 1)
|
||||
following_node = following_node_of(following_node)
|
||||
end
|
||||
followings
|
||||
end
|
||||
|
||||
def following_node_of( node )
|
||||
if node.kind_of? Element and node.children.size > 0
|
||||
return node.children[0]
|
||||
end
|
||||
return next_sibling_node(node)
|
||||
end
|
||||
|
||||
def next_sibling_node(node)
|
||||
psn = node.next_sibling_node
|
||||
while psn.nil?
|
||||
if node.parent.nil? or node.parent.class == Document
|
||||
return nil
|
||||
end
|
||||
node = node.parent
|
||||
psn = node.next_sibling_node
|
||||
end
|
||||
return psn
|
||||
end
|
||||
|
||||
def child(nodeset)
|
||||
nodesets = []
|
||||
nodeset.each do |node|
|
||||
raw_node = node.raw_node
|
||||
node_type = raw_node.node_type
|
||||
# trace(:child, node_type, node)
|
||||
case node_type
|
||||
when :element
|
||||
nodesets << raw_node.children.collect.with_index do |child_node, i|
|
||||
XPathNode.new(child_node, position: i + 1)
|
||||
end
|
||||
when :document
|
||||
new_nodeset = []
|
||||
raw_node.children.each do |child|
|
||||
case child
|
||||
when XMLDecl, Text
|
||||
# Ignore
|
||||
else
|
||||
new_nodeset << XPathNode.new(child, position: new_nodeset.size + 1)
|
||||
end
|
||||
end
|
||||
nodesets << new_nodeset unless new_nodeset.empty?
|
||||
end
|
||||
end
|
||||
nodesets
|
||||
end
|
||||
|
||||
def norm b
|
||||
case b
|
||||
when true, false
|
||||
return b
|
||||
when 'true', 'false'
|
||||
return Functions::boolean( b )
|
||||
when /^\d+(\.\d+)?$/, Numeric
|
||||
return Functions::number( b )
|
||||
else
|
||||
return Functions::string( b )
|
||||
end
|
||||
end
|
||||
|
||||
def equality_relational_compare(set1, op, set2)
|
||||
set1 = unnode(set1) if set1.is_a?(Array)
|
||||
set2 = unnode(set2) if set2.is_a?(Array)
|
||||
|
||||
if set1.kind_of? Array and set2.kind_of? Array
|
||||
# If both objects to be compared are node-sets, then the
|
||||
# comparison will be true if and only if there is a node in the
|
||||
# first node-set and a node in the second node-set such that the
|
||||
# result of performing the comparison on the string-values of
|
||||
# the two nodes is true.
|
||||
set1.product(set2).any? do |node1, node2|
|
||||
node_string1 = Functions.string(node1)
|
||||
node_string2 = Functions.string(node2)
|
||||
compare(node_string1, op, node_string2)
|
||||
end
|
||||
elsif set1.kind_of? Array or set2.kind_of? Array
|
||||
# If one is nodeset and other is number, compare number to each item
|
||||
# in nodeset s.t. number op number(string(item))
|
||||
# If one is nodeset and other is string, compare string to each item
|
||||
# in nodeset s.t. string op string(item)
|
||||
# If one is nodeset and other is boolean, compare boolean to each item
|
||||
# in nodeset s.t. boolean op boolean(item)
|
||||
if set1.kind_of? Array
|
||||
a = set1
|
||||
b = set2
|
||||
else
|
||||
a = set2
|
||||
b = set1
|
||||
end
|
||||
|
||||
case b
|
||||
when true, false
|
||||
each_unnode(a).any? do |unnoded|
|
||||
compare(Functions.boolean(unnoded), op, b)
|
||||
end
|
||||
when Numeric
|
||||
each_unnode(a).any? do |unnoded|
|
||||
compare(Functions.number(unnoded), op, b)
|
||||
end
|
||||
when /\A\d+(\.\d+)?\z/
|
||||
b = Functions.number(b)
|
||||
each_unnode(a).any? do |unnoded|
|
||||
compare(Functions.number(unnoded), op, b)
|
||||
end
|
||||
else
|
||||
b = Functions::string(b)
|
||||
each_unnode(a).any? do |unnoded|
|
||||
compare(Functions::string(unnoded), op, b)
|
||||
end
|
||||
end
|
||||
else
|
||||
# If neither is nodeset,
|
||||
# If op is = or !=
|
||||
# If either boolean, convert to boolean
|
||||
# If either number, convert to number
|
||||
# Else, convert to string
|
||||
# Else
|
||||
# Convert both to numbers and compare
|
||||
compare(set1, op, set2)
|
||||
end
|
||||
end
|
||||
|
||||
def value_type(value)
|
||||
case value
|
||||
when true, false
|
||||
:boolean
|
||||
when Numeric
|
||||
:number
|
||||
when String
|
||||
:string
|
||||
else
|
||||
raise "[BUG] Unexpected value type: <#{value.inspect}>"
|
||||
end
|
||||
end
|
||||
|
||||
def normalize_compare_values(a, operator, b)
|
||||
a_type = value_type(a)
|
||||
b_type = value_type(b)
|
||||
case operator
|
||||
when :eq, :neq
|
||||
if a_type == :boolean or b_type == :boolean
|
||||
a = Functions.boolean(a) unless a_type == :boolean
|
||||
b = Functions.boolean(b) unless b_type == :boolean
|
||||
elsif a_type == :number or b_type == :number
|
||||
a = Functions.number(a) unless a_type == :number
|
||||
b = Functions.number(b) unless b_type == :number
|
||||
else
|
||||
a = Functions.string(a) unless a_type == :string
|
||||
b = Functions.string(b) unless b_type == :string
|
||||
end
|
||||
when :lt, :lteq, :gt, :gteq
|
||||
a = Functions.number(a) unless a_type == :number
|
||||
b = Functions.number(b) unless b_type == :number
|
||||
else
|
||||
message = "[BUG] Unexpected compare operator: " +
|
||||
"<#{operator.inspect}>: <#{a.inspect}>: <#{b.inspect}>"
|
||||
raise message
|
||||
end
|
||||
[a, b]
|
||||
end
|
||||
|
||||
def compare(a, operator, b)
|
||||
a, b = normalize_compare_values(a, operator, b)
|
||||
case operator
|
||||
when :eq
|
||||
a == b
|
||||
when :neq
|
||||
a != b
|
||||
when :lt
|
||||
a < b
|
||||
when :lteq
|
||||
a <= b
|
||||
when :gt
|
||||
a > b
|
||||
when :gteq
|
||||
a >= b
|
||||
else
|
||||
message = "[BUG] Unexpected compare operator: " +
|
||||
"<#{operator.inspect}>: <#{a.inspect}>: <#{b.inspect}>"
|
||||
raise message
|
||||
end
|
||||
end
|
||||
|
||||
def each_unnode(nodeset)
|
||||
return to_enum(__method__, nodeset) unless block_given?
|
||||
nodeset.each do |node|
|
||||
if node.is_a?(XPathNode)
|
||||
unnoded = node.raw_node
|
||||
else
|
||||
unnoded = node
|
||||
end
|
||||
yield(unnoded)
|
||||
end
|
||||
end
|
||||
|
||||
def unnode(nodeset)
|
||||
each_unnode(nodeset).collect do |unnoded|
|
||||
unnoded = yield(unnoded) if block_given?
|
||||
unnoded
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# @private
|
||||
class XPathNode
|
||||
attr_reader :raw_node, :context
|
||||
def initialize(node, context=nil)
|
||||
if node.is_a?(XPathNode)
|
||||
@raw_node = node.raw_node
|
||||
else
|
||||
@raw_node = node
|
||||
end
|
||||
@context = context || {}
|
||||
end
|
||||
|
||||
def position
|
||||
@context[:position]
|
||||
end
|
||||
end
|
||||
end
|
Двоичные данные
test/rexml/data/LostineRiver.kml.gz
Двоичные данные
test/rexml/data/LostineRiver.kml.gz
Двоичный файл не отображается.
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
<ProductionSupport version="1.1" >
|
||||
<Errors>
|
||||
<CommonErrors>
|
||||
<CommonError>
|
||||
<Key><![CDATA[RoyUpdatePolicyBusReq(Object)>>#error:]]></Key>
|
||||
<Patterns>
|
||||
<Pattern><![CDATA[The error code is '9997']]></Pattern>
|
||||
</Patterns>
|
||||
<Message>
|
||||
<String>Update Policy request 9997: Please check CICS log</String>
|
||||
</Message>
|
||||
<BackendSupport/>
|
||||
</CommonError>
|
||||
<CommonError>
|
||||
<Key>MotorInsuranceContract(Object)>>#error:</Key>
|
||||
<Patterns>
|
||||
<Pattern>Have not got a complete</Pattern>
|
||||
</Patterns>
|
||||
<Message>
|
||||
<String>Have not got a complete and consistent set of price matrices for policy period - ask back-end prod supp to sort out</String>
|
||||
</Message>
|
||||
<BackendSupport/>
|
||||
</CommonError>
|
||||
</CommonErrors>
|
||||
</Errors>
|
||||
</ProductionSupport>
|
|
@ -1,25 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<root>
|
||||
|
||||
<a>
|
||||
<a.1/>
|
||||
<a.2/>
|
||||
<a.3/>
|
||||
<a.4/>
|
||||
<a.5/>
|
||||
</a>
|
||||
|
||||
<b>
|
||||
<b.1/>
|
||||
<b.2/>
|
||||
<b.3/>
|
||||
<b.4/>
|
||||
<b.5/>
|
||||
<b.6/>
|
||||
<b.7/>
|
||||
<b.8/>
|
||||
<b.9/>
|
||||
</b>
|
||||
|
||||
</root>
|
|
@ -1,5 +0,0 @@
|
|||
<a>
|
||||
Here is an XML document.
|
||||
<b>
|
||||
It has some elements, but it also has a hidden < error! (or two)
|
||||
</a>
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
|
||||
<foo>
|
||||
<bar>
|
||||
<baz/>
|
||||
<cheese/>
|
||||
<baz/>
|
||||
<cheese/>
|
||||
<baz/>
|
||||
</bar>
|
||||
</foo>
|
|
@ -1,47 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
|
||||
<xu:modifications xmlns:xu="http://www.xmldb.org/xupdate">
|
||||
|
||||
<xu:append select="/foo/bar/cheese[1]">
|
||||
Goudse kaas
|
||||
<edam type="jong belegen">Rond</edam>
|
||||
</xu:append>
|
||||
|
||||
<xu:remove select="/foo/bar/baz[2]"/>
|
||||
|
||||
<xu:if test="/foo">
|
||||
<xu:insert-before select="/foo/bar/baz[2]">
|
||||
<cheese>More cheese!</cheese>
|
||||
</xu:insert-before>
|
||||
</xu:if>
|
||||
|
||||
<xu:insert-before select="/foo/bar/baz[2]">
|
||||
<cheese>Even more cheese!</cheese>
|
||||
</xu:insert-before>
|
||||
|
||||
<xu:if test="/bar">
|
||||
<xu:insert-before select="/foo/bar/baz[2]">
|
||||
<sausages>No sausages today</sausages>
|
||||
</xu:insert-before>
|
||||
</xu:if>
|
||||
|
||||
<xu:variable
|
||||
xmlns:private="http://www.jaxen.org/private"
|
||||
name="private:twice">
|
||||
<cracker/>
|
||||
<!-- champagne -->
|
||||
<?oisters with a bit of lemon?>
|
||||
</xu:variable>
|
||||
|
||||
<xu:variable name="twice" select="'Twice'"/>
|
||||
|
||||
<xu:insert-after
|
||||
select="/foo/bar"
|
||||
xmlns:private="http://www.jaxen.org/private"
|
||||
>
|
||||
<xu:value-of select="$private:twice"/>
|
||||
<xu:value-of select="$private:twice"/>
|
||||
<xu:value-of select="$twice"/>
|
||||
</xu:insert-after>
|
||||
|
||||
</xu:modifications>
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE rdf:RDF [
|
||||
<!ENTITY % HTMLlat1 PUBLIC
|
||||
"-//W3C//ENTITIES Latin 1 for XHTML//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
|
||||
%HTMLlat1;
|
||||
]>
|
||||
|
||||
<rdf:RDF
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:on="http://www.oreillynet.com/csrss/"
|
||||
xmlns="http://purl.org/rss/1.0/"
|
||||
>
|
||||
|
||||
<channel rdf:about="http://www.oreillynet.com/">
|
||||
<title>O'Reilly Network Articles</title>
|
||||
<link>http://www.oreillynet.com/</link>
|
||||
</channel>
|
||||
</rdf:RDF>
|
|
@ -1,70 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<?xml-stylesheet href="XSL\JavaXML.html.xsl" type="text/xsl"?>
|
||||
<?xml-stylesheet href="XSL\JavaXML.wml.xsl" type="text/xsl"
|
||||
media="wap"?>
|
||||
<?cocoon-process type="xslt"?>
|
||||
|
||||
<!-- Java and XML -->
|
||||
<JavaXML:Book xmlns:JavaXML="http://www.oreilly.com/catalog/javaxml/"
|
||||
xmlns:ora="http://www.oreilly.com"
|
||||
xmlns:unused="http://www.unused.com"
|
||||
ora:category="Java"
|
||||
>
|
||||
<!-- comment one -->
|
||||
<!-- comment two -->
|
||||
|
||||
<JavaXML:Title>Java and XML</JavaXML:Title>
|
||||
<JavaXML:Contents xmlns:topic="http://www.oreilly.com/topics">
|
||||
<JavaXML:Chapter topic:focus="XML">
|
||||
<JavaXML:Heading>Introduction</JavaXML:Heading>
|
||||
<JavaXML:Topic subSections="7">
|
||||
What Is It?
|
||||
</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="3">
|
||||
How Do I Use It?
|
||||
</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="4">
|
||||
Why Should I Use It?
|
||||
</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="0">
|
||||
What's Next?
|
||||
</JavaXML:Topic>
|
||||
</JavaXML:Chapter>
|
||||
|
||||
<JavaXML:Chapter topic:focus="XML">
|
||||
<JavaXML:Heading>Creating XML</JavaXML:Heading>
|
||||
<JavaXML:Topic subSections="0">An XML Document</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="2">The Header</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="6">The Content</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="1">What's Next?</JavaXML:Topic>
|
||||
</JavaXML:Chapter>
|
||||
|
||||
<JavaXML:Chapter topic:focus="Java">
|
||||
<JavaXML:Heading>Parsing XML</JavaXML:Heading>
|
||||
<JavaXML:Topic subSections="3">Getting Prepared</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="3">SAX Readers</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="9">Content Handlers</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="4">Error Handlers</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="0">
|
||||
A Better Way to Load a Parser
|
||||
</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="4">"Gotcha!"</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="0">What's Next?</JavaXML:Topic>
|
||||
</JavaXML:Chapter>
|
||||
|
||||
<JavaXML:SectionBreak/>
|
||||
|
||||
<JavaXML:Chapter topic:focus="Java">
|
||||
<JavaXML:Heading>Web Publishing Frameworks</JavaXML:Heading>
|
||||
<JavaXML:Topic subSections="4">Selecting a Framework</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="4">Installation</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="3">
|
||||
Using a Publishing Framework
|
||||
</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="2">XSP</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="3">Cocoon 2.0 and Beyond</JavaXML:Topic>
|
||||
<JavaXML:Topic subSections="0">What's Next?</JavaXML:Topic>
|
||||
</JavaXML:Chapter>
|
||||
</JavaXML:Contents>
|
||||
</JavaXML:Book>
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<content-repository>
|
||||
<content-repository-child-1>
|
||||
<content-1>content-1-text</content-1>
|
||||
</content-repository-child-1>
|
||||
<content-repository-child-2>
|
||||
<content-2>content-2-text</content-2>
|
||||
</content-repository-child-2>
|
||||
<content-repository-child-3>
|
||||
<content-3>content-3-text</content-3>
|
||||
</content-repository-child-3>
|
||||
</content-repository>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<a xmlns="http://dummyNamespace/">
|
||||
<b>
|
||||
<c>Hello</c>
|
||||
</b>
|
||||
</a>
|
|
@ -1,34 +0,0 @@
|
|||
<!DOCTYPE internationalization SYSTEM "l10n.dtd" [
|
||||
<!ENTITY af SYSTEM "af.xml">
|
||||
<!ENTITY ca SYSTEM "ca.xml">
|
||||
<!ENTITY cs SYSTEM "cs.xml">
|
||||
<!ENTITY da SYSTEM "da.xml">
|
||||
<!ENTITY de SYSTEM "de.xml">
|
||||
<!ENTITY el SYSTEM "el.xml">
|
||||
<!ENTITY en SYSTEM "en.xml">
|
||||
<!ENTITY es SYSTEM "es.xml">
|
||||
<!ENTITY et SYSTEM "et.xml">
|
||||
<!ENTITY fi SYSTEM "fi.xml">
|
||||
<!ENTITY fr SYSTEM "fr.xml">
|
||||
<!ENTITY hu SYSTEM "hu.xml">
|
||||
<!ENTITY id SYSTEM "id.xml">
|
||||
<!ENTITY it SYSTEM "it.xml">
|
||||
<!ENTITY ja SYSTEM "ja.xml">
|
||||
<!ENTITY ko SYSTEM "ko.xml">
|
||||
<!ENTITY nl SYSTEM "nl.xml">
|
||||
<!ENTITY no SYSTEM "no.xml">
|
||||
<!ENTITY no_ny SYSTEM "no_ny.xml">
|
||||
<!ENTITY pl SYSTEM "pl.xml">
|
||||
<!ENTITY pt SYSTEM "pt.xml">
|
||||
<!ENTITY pt_br SYSTEM "pt_br.xml">
|
||||
<!ENTITY ro SYSTEM "ro.xml">
|
||||
<!ENTITY ru SYSTEM "ru.xml">
|
||||
<!ENTITY sk SYSTEM "sk.xml">
|
||||
<!ENTITY sl SYSTEM "sl.xml">
|
||||
<!ENTITY sr SYSTEM "sr.xml">
|
||||
<!ENTITY sv SYSTEM "sv.xml">
|
||||
<!ENTITY tr SYSTEM "tr.xml">
|
||||
<!ENTITY zh_cn SYSTEM "zh_cn.xml">
|
||||
<!ENTITY zh_tw SYSTEM "zh_tw.xml">
|
||||
]>
|
||||
<x/>
|
|
@ -1,542 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/css" href="http://www.germane-software.com/repositories/public/documentation/documentation.css"?>
|
||||
<?xml-stylesheet alternative="yes" type="text/css" href="file:/home/ser/Work/documentation/documentation.css"?>
|
||||
<?xml-stylesheet alternative="yes" type="text/xsl" href="http://www.germane-software.com/repositories/public/documentation/paged.xsl"?>
|
||||
<!DOCTYPE documentation SYSTEM "http://www.germane-software.com/repositories/public/documentation/documentation.dtd">
|
||||
<documentation>
|
||||
<head>
|
||||
<title>REXML</title>
|
||||
|
||||
<banner href="img/rexml.png" />
|
||||
|
||||
<version>@ANT_VERSION@</version>
|
||||
|
||||
<date>@ANT_DATE@</date>
|
||||
|
||||
<home>http://www.germane-software.com/software/rexml</home>
|
||||
|
||||
<base>rexml</base>
|
||||
|
||||
<language>ruby</language>
|
||||
|
||||
<author email="ser@germane-software.com"
|
||||
href="http://www.ser1.net/" jabber="seanerussell@gmail.com">Sean
|
||||
Russell</author>
|
||||
</head>
|
||||
|
||||
<overview>
|
||||
<purpose lang="en">
|
||||
<p>REXML is a conformant XML processor for the Ruby programming
|
||||
language. REXML passes 100% of the Oasis non-validating tests and
|
||||
includes full XPath support. It is reasonably fast, and is implemented
|
||||
in pure Ruby. Best of all, it has a clean, intuitive API. REXML is
|
||||
included in the standard library of Ruby</p>
|
||||
|
||||
<p>This software is distribute under the <link href="LICENSE.txt">Ruby
|
||||
license</link>.</p>
|
||||
</purpose>
|
||||
|
||||
<general>
|
||||
<p>REXML arose out of a desire for a straightforward XML API, and is an
|
||||
attempt at an API that doesn't require constant referencing of
|
||||
documentation to do common tasks. "Keep the common case simple, and the
|
||||
uncommon, possible."</p>
|
||||
|
||||
<p>REXML avoids The DOM API, which violates the maxim of simplicity. It
|
||||
does provide <em>a</em> DOM model, but one that is Ruby-ized. It is an
|
||||
XML API oriented for Ruby programmers, not for XML programmers coming
|
||||
from Java.</p>
|
||||
|
||||
<p>Some of the common differences are that the Ruby API relies on block
|
||||
enumerations, rather than iterators. For example, the Java code:</p>
|
||||
|
||||
<example>for (Enumeration e=parent.getChildren(); e.hasMoreElements(); ) {
|
||||
Element child = (Element)e.nextElement(); // Do something with child
|
||||
}</example>
|
||||
|
||||
<p>in Ruby becomes:</p>
|
||||
|
||||
<example>parent.each_child{ |child| # Do something with child }</example>
|
||||
|
||||
<p>Can't you feel the peace and contentment in this block of code? Ruby
|
||||
is the language Buddha would have programmed in.</p>
|
||||
|
||||
<p>One last thing. If you use and like this software, and you're in a
|
||||
position of power in a company in Western Europe and are looking for a
|
||||
software architect or developer, drop me a line. I took a lot of French
|
||||
classes in college (all of which I've forgotten), and I lived in Munich
|
||||
long enough that I was pretty fluent by the time I left, and I'd love to
|
||||
get back over there.</p>
|
||||
</general>
|
||||
|
||||
<features lang="en">
|
||||
<item>Four intuitive parsing APIs.</item>
|
||||
|
||||
<item>Intuitive, powerful, and reasonably fast tree parsing API (a-la
|
||||
DOM</item>
|
||||
|
||||
<item>Fast stream parsing API (a-la SAX)<footnote>This is not a SAX
|
||||
API.</footnote></item>
|
||||
|
||||
<item>SAX2-based API<footnote>In addition to the native REXML streaming
|
||||
API. This is slower than the native REXML API, but does a lot more work
|
||||
for you.</footnote></item>
|
||||
|
||||
<item>Pull parsing API.</item>
|
||||
|
||||
<item>Small</item>
|
||||
|
||||
<item>Reasonably fast (for interpreted code)</item>
|
||||
|
||||
<item>Native Ruby</item>
|
||||
|
||||
<item>Full XPath support<footnote>Currently only available for the tree
|
||||
API</footnote></item>
|
||||
|
||||
<item>XML 1.0 conformant<footnote>REXML passes all of the non-validating
|
||||
OASIS tests. There are probably places where REXML isn't conformant, but
|
||||
I try to fix them as they're reported.</footnote></item>
|
||||
|
||||
<item>ISO-8859-1, UNILE, UTF-16 and UTF-8 input and output; also,
|
||||
support for any encoding the iconv supports.</item>
|
||||
|
||||
<item>Documentation</item>
|
||||
</features>
|
||||
</overview>
|
||||
|
||||
<operation lang="en">
|
||||
<subsection title="Installation">
|
||||
<p>You don't <em>have</em> to install anything; if you're running a
|
||||
version of Ruby greater than 1.8, REXML is included. However, if you
|
||||
choose to upgrade from the REXML distribution, run the command:
|
||||
<code>ruby bin/install.rb</code>. By the way, you really should look at
|
||||
these sorts of files before you run them as root. They could contain
|
||||
anything, and since (in Ruby, at least) they tend to be mercifully
|
||||
short, it doesn't hurt to glance over them. If you want to uninstall
|
||||
REXML, run <code>ruby bin/install.rb -u</code>.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Unit tests">
|
||||
<p>If you have Test::Unit installed, you can run the unit test cases.
|
||||
Run the command: <code>ruby bin/suite.rb</code>; it runs against the
|
||||
distribution, not against the installed version.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Benchmarks">
|
||||
<p>There is a benchmark suite in <code>benchmarks/</code>. To run the
|
||||
benchmarks, change into that directory and run <code>ruby
|
||||
comparison.rb</code>. If you have nothing else installed, only the
|
||||
benchmarks for REXML will be run. However, if you have any of the
|
||||
following installed, benchmarks for those tools will also be run:</p>
|
||||
|
||||
<list>
|
||||
<item>NQXML</item>
|
||||
|
||||
<item>XMLParser</item>
|
||||
|
||||
<item>Electric XML (you must copy <code>EXML.jar</code> into the
|
||||
<code>benchmarks</code> directory and compile
|
||||
<code>flatbench.java</code> before running the test)</item>
|
||||
</list>
|
||||
|
||||
<p>The results will be written to <code>index.html</code>.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="General Usage">
|
||||
<p>Please see <link href="docs/tutorial.html">the Tutorial</link>.</p>
|
||||
|
||||
<p>The API documentation is available <link
|
||||
href="http://www.germane-software.com/software/XML/rexml/doc">on-line</link>,
|
||||
or it can be downloaded as an archive <link
|
||||
href="http://www.germane-software.com/software/archives/rexml_api_@ANT_VERSION@.tgz">in
|
||||
tgz format (~70Kb)</link> or (if you're a masochist) <link
|
||||
href="http://www.germane-software.com/software/archives/rexml_api_@ANT_VERSION@.zip">in
|
||||
zip format (~280Kb)</link>. The best solution is to download and install
|
||||
Dave Thomas' most excellent <link
|
||||
href="http://rdoc.sourceforge.net">rdoc</link> and generate the API docs
|
||||
yourself; then you'll be sure to have the latest API docs and won't have
|
||||
to keep downloading the doc archive.</p>
|
||||
|
||||
<p>The unit tests in <code>test/</code> and the benchmarking code in
|
||||
<code>benchmark/</code> provide additional examples of using REXML. The
|
||||
Tutorial provides examples with commentary. The documentation unpacks
|
||||
into <link href="doc/index.html"><code>rexml/doc</code></link>.</p>
|
||||
|
||||
<p>Kouhei Sutou maintains a <link
|
||||
href="http://www.germane-software.com/software/rexml_doc_ja/current/index.html">Japanese
|
||||
version</link> of the REXML API docs. <link
|
||||
href="http://www.germane-software.com/software/rexml_doc_ja/current/japanese_documentation.html">Kou's
|
||||
documentation page</link> contains links to binary archives for various
|
||||
versions of the documentation.</p>
|
||||
</subsection>
|
||||
</operation>
|
||||
|
||||
<status>
|
||||
<subsection title="Speed and Completeness">
|
||||
<p>Unfortunately, NQXML is the only package REXML can be compared
|
||||
against; XMLParser uses expat, which is a native library, and really is
|
||||
a different beast altogether. So in comparing NQXML and REXML you can
|
||||
look at four things: speed, size, completeness, and API.</p>
|
||||
|
||||
<p><link href="benchmarks/index.html">Benchmarks</link></p>
|
||||
|
||||
<p>REXML is faster than NQXML in some things, and slower than NQXML in a
|
||||
couple of things. You can see this for yourself by running the supplied
|
||||
benchmarks. Most of the places where REXML are slower are because of the
|
||||
convenience methods<footnote>For example,
|
||||
<code>element.elements[index]</code> isn't really an array operation;
|
||||
index can be an Integer or an XPath, and this feature is relatively time
|
||||
expensive.</footnote>. On the positive side, most of the convenience
|
||||
methods can be bypassed if you know what you are doing. Check the <link
|
||||
href="benchmarks/index.html"> benchmark comparison page</link> for a
|
||||
<em>general</em> comparison. You can look at the benchmark code yourself
|
||||
to decide how much salt to take with them.</p>
|
||||
|
||||
<p>The sizes of the XML parsers are close<footnote>As measured with
|
||||
<code>ruby -nle 'print unless /^\s*(#.*|)$/' *.rb | wc -l</code>
|
||||
</footnote>. NQXML 1.1.3 has 1580 non-blank, non-comment lines of code;
|
||||
REXML 2.0 has 2340<footnote>REXML started out with about 1200, but that
|
||||
number has been steadily increasing as features are added. XPath
|
||||
accounts for 541 lines of that code, so the core REXML has about 1800
|
||||
LOC.</footnote>.</p>
|
||||
|
||||
<p>REXML is a conformant XML 1.0 parser. It supports multiple language
|
||||
encodings, and internal processing uses the required UTF-8 and UTF-16
|
||||
encodings. It passes 100% of the Oasis non-validating tests.
|
||||
Furthermore, it provides a full implementation of XPath, a SAX2 and a
|
||||
PullParser API.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="XPath">
|
||||
<p>As of release 2.0, XPath 1.0 is fully implemented.</p>
|
||||
|
||||
<p>I fully expect bugs to crop up from time to time, so if you see any
|
||||
bogus XPath results, please let me know. That said, since I'm now
|
||||
following the XPath grammar and spec fairly closely, I suspect that you
|
||||
won't be surprised by REXML's XPath very often, and it should become
|
||||
rock solid fairly quickly.</p>
|
||||
|
||||
<p>Check the "bugs" section for known problems; there are little bits of
|
||||
XPath here and there that are not yet implemented, but I'll get to them
|
||||
soon.</p>
|
||||
|
||||
<p>Namespace support is rather odd, but it isn't my fault. I can only do
|
||||
so much and still conform to the specs. In particular, XPath attempts to
|
||||
help as much as possible. Therefore, in the trivial cases, you can pass
|
||||
namespace prefixes to Element.elements[...] and so on -- in these cases,
|
||||
XPath will use the namespace environment of the base element you're
|
||||
starting your XPath search from. However, if you want to do something
|
||||
more complex, like pass in your own namespace environment, you have to
|
||||
use the XPath first(), each(), and match() methods. Also, default
|
||||
namespaces <em>force</em> you to use the XPath methods, rather than the
|
||||
convenience methods, because there is no way for XPath to know what the
|
||||
mappings for the default namespaces should be. This is exactly why I
|
||||
loath namespaces -- a pox on the person(s) who thought them up!</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Namespaces">
|
||||
<p>Namespace support is now fairly stable. One thing to be aware of is
|
||||
that REXML is not (yet) a validating parser. This means that some
|
||||
invalid namespace declarations are not caught.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Mailing list">
|
||||
<p>There is a low-volume mailing list dedicated to REXML. To subscribe,
|
||||
send an empty email to <link
|
||||
href="mailto:ser-rexml-subscribe@germane-software.com">ser-rexml-subscribe@germane-software.com</link>.
|
||||
This list is more or less spam proof. To unsubscribe, similarly send a
|
||||
message to <link
|
||||
href="mailto:ser-rexml-unsubscribe@germane-software.com">ser-rexml-unsubscribe@germane-software.com</link>.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="RSS">
|
||||
<p>An <link
|
||||
href="http://www.germane-software.com/projects/rexml/timeline?ticket=on&max=50&daysback=90&format=rss">RSS
|
||||
file</link> for REXML is now being generated from the change log. This
|
||||
allows you to be alerted of bug fixes and feature additions via "pull".
|
||||
<link href="http://www.germane-software.com/software/rexml/rss.xml">Another
|
||||
RSS</link> is available which contains a single item: the release notice
|
||||
for the most recent release. This is an abuse of the RSS
|
||||
mechanism, which was intended to be a distribution system for headlines
|
||||
linked back to full articles, but it works. The headline for REXML is
|
||||
the version number, and the description is the change log. The links all
|
||||
link back to the REXML home page. The URL for the RSS itself is
|
||||
http://www.germane-software.com/software/rexml/rss.xml.</p>
|
||||
|
||||
<p>The <link href="release.html">changelog itself is here</link>.</p>
|
||||
|
||||
<p>For those who are interested, there's a <link
|
||||
href="docs/sloccount.txt">SLOCCount</link> (by David A. Wheeler) file
|
||||
with stats on the REXML sourcecode. Note that the SLOCCount output
|
||||
includes the files in the test/, benchmarks/, and bin/ directories, as
|
||||
well as the main sourcecode for REXML itself.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Applications that use REXML">
|
||||
<list>
|
||||
<item><link
|
||||
href="http://www.pablotron.org/software/raggle/">Raggle</link> is a
|
||||
console-based RSS aggregator.</item>
|
||||
|
||||
<item><link
|
||||
href="http://www.zweknu.org/technical/index.rhtml?s=p|10/">getrss</link>
|
||||
is an RSS aggregator</item>
|
||||
|
||||
<item>Ned Konz's <link
|
||||
href="http://www.bikenomad.microship.com/ruby/">ruby-htmltools</link>
|
||||
uses REXML</item>
|
||||
|
||||
<item>Hiroshi NAKAMURA's <link
|
||||
href="http://www.ruby-lang.org/en/raa-list.rhtml?name=SOAP4R">SOAP4R</link>
|
||||
package can use REXML as the XML processor.</item>
|
||||
|
||||
<item>Chris Morris' <link href="http://clabs.org/clxmlserial.htm">XML
|
||||
Serializer</link>. XML Serializer provides a serialization mechanism
|
||||
for Ruby that provides a bidirectional mapping between Ruby classes
|
||||
and XML documents.</item>
|
||||
|
||||
<item>Much of the <link href="http://www.rubyxml.com">RubyXML</link>
|
||||
site is generated with scripts that use REXML. RubyXML is a great
|
||||
place to find information about th intersection between Ruby and
|
||||
XML.</item>
|
||||
</list>
|
||||
</subsection>
|
||||
|
||||
<bugs lang="en">
|
||||
<p>You can submit bug reports and feature requests, and view the list of
|
||||
known bugs, at the <link
|
||||
href="http://www.germane-software.com/projects/rexml">REXML bug report
|
||||
page.</link> Please do submit bug reports. If you really want your bug
|
||||
fixed fast, include an runit or Test::Unit method (or methods) that
|
||||
illustrates the problem. At the very least, send me some XML that REXML
|
||||
doesn't process properly.</p>
|
||||
|
||||
<p>You don't have to send an entire test suite -- just the unit test
|
||||
methods. If you don't send me a unit test, I'll have to write one
|
||||
myself, which will mean that your bug will take longer to fix.</p>
|
||||
|
||||
<p>When submitting bug reports, please include the version of Ruby and
|
||||
of REXML that you're using, and the operating system you're running on.
|
||||
Just run: <code>ruby -vrrexml/rexml -e 'p
|
||||
REXML::VERSION,PLATFORM'</code> and paste the results in your bug
|
||||
report. Include your email if you want a response about the bug.</p>
|
||||
|
||||
<item>Attributes are not handled internally as nodes, so you can't
|
||||
perform node functions on them. This will have to change. It'll also
|
||||
probably mean that, rather than returning attribute values, XPath will
|
||||
return the Attribute nodes.</item>
|
||||
|
||||
<item>Some of the XPath <em>functions</em> are untested<footnote>Mike
|
||||
Stok has been testing, debugging, and implementing some of these
|
||||
Functions (and he's been doing a good job) so there's steady improvement
|
||||
in this area.</footnote>. Any XPath functions that don't work are also
|
||||
bugs... please report them. If you send a unit test that illustrates the
|
||||
problem, I'll try to fix the problem within a couple of days (if I can)
|
||||
and send you a patch, personally.</item>
|
||||
|
||||
<item>Accessing prefixes for which there is no defined namespace in an
|
||||
XPath should throw an exception. It currently doesn't -- it just fails
|
||||
to match.</item>
|
||||
</bugs>
|
||||
|
||||
<todo lang="en">
|
||||
<item>Reparsing a tree with a pull/SAX parser</item>
|
||||
|
||||
<item>Better namespace support in SAX</item>
|
||||
|
||||
<item>Lazy tree parsing</item>
|
||||
|
||||
<item>Segregate parsers, for optimized minimal distributions</item>
|
||||
|
||||
<item>XML <-> Ruby</item>
|
||||
|
||||
<item>Validation support</item>
|
||||
|
||||
<item>True XML character support</item>
|
||||
|
||||
<item>Add XPath support for streaming APIs</item>
|
||||
|
||||
<item status="request">XQuery support</item>
|
||||
|
||||
<item status="request">XUpdate support</item>
|
||||
|
||||
<item>Make sure namespaces are supported in pull parser</item>
|
||||
|
||||
<item status="request">Add document start and entity replacement events
|
||||
in pull parser</item>
|
||||
|
||||
<item>Better stream parsing exception handling</item>
|
||||
|
||||
<item>I'd like to hack XMLRPC4R to use REXML, for my own
|
||||
purposes.</item>
|
||||
</todo>
|
||||
</status>
|
||||
|
||||
<faq>
|
||||
<q>REXML is hanging while parsing one of my XML files.</q>
|
||||
|
||||
<a>Your XML is probably malformed. Some malformed XML, especially XML that
|
||||
contains literal '<' embedded in the document, causes REXML to hang.
|
||||
REXML should be throwing an exception, but it doesn't; this is a bug. I'm
|
||||
aware that it is an extremely annoying bug, and it is one I'm trying to
|
||||
solve in a way that doesn't significantly reduce REXML's parsing
|
||||
speed.</a>
|
||||
|
||||
<q>I'm using the XPath '//foo' on an XML branch node X, and keep getting
|
||||
all of the 'foo' elements in the entire document. Why? Shouldn't it return
|
||||
only the 'foo' element descendants of X?</q>
|
||||
|
||||
<a>No. XPath specifies that '/' returns the document root, regardless of
|
||||
the context node. '//' also starts at the document root. If you want to
|
||||
limit your search to a branch, you need to use the self:: axe. EG,
|
||||
'self::node()//foo', or the shorthand './/foo'.</a>
|
||||
|
||||
<q>I want to parse a document both as a tree, and as a stream. Can I do
|
||||
this?</q>
|
||||
|
||||
<a>Yes, and no. There is no mechanism that directly supports this in
|
||||
REXML. However, aside from writing your own traversal layer, there is a
|
||||
way of doing this. To turn a tree into a stream, just turn the branch you
|
||||
want to process as a stream back into a string, and re-parse it with your
|
||||
preferred API. EG: pp = PullParser.new( some_element.to_s ). The other
|
||||
direction is more difficult; you basically have to build a tree from the
|
||||
events. REXML will have one of these builders, eventually, but it doesn't
|
||||
currently exist.</a>
|
||||
|
||||
<q>Why is Element.elements indexed off of '1' instead of '0'?</q>
|
||||
|
||||
<a>Because of XPath. The XPath specification states that the index of the
|
||||
first child node is '1'. Although it may be counter-intuitive to base
|
||||
elements on 1, it is more undesireable to have element.elements[0] ==
|
||||
element.elements[ 'node()[1]' ]. Since I can't change the XPath
|
||||
specification, the result is that Element.elements[1] is the first child
|
||||
element.</a>
|
||||
|
||||
<q>Why isn't REXML a validating parser?</q>
|
||||
|
||||
<a>Because validating parsers must include code that parses and interprets
|
||||
DTDs. I hate DTDs. REXML supports the barest minimum of DTD parsing, and
|
||||
even that isn't complete. There is DTD parsing code in the works, but I
|
||||
only work on it when I'm really, really bored. Rumor has it that a
|
||||
contributor is working on a DTD parser for REXML; rest assured that any
|
||||
such contribution will be included with REXML as soon as it is
|
||||
available.</a>
|
||||
|
||||
<q>I'm trying to create an ISO-8859-1 document, but when I add text to the
|
||||
document it isn't being properly encoded.</q>
|
||||
|
||||
<a>Regardless of what the encoding of your document is, when you add text
|
||||
programmatically to a REXML document you <em>must</em> ensure that you are
|
||||
only adding UTF-8 to the tree. In particular, you can't add ISO-8859-1
|
||||
encoded text that contains characters above 0x80 to REXML trees -- you
|
||||
must convert it to UTF-8 before doing so. Luckily, this is easy:
|
||||
<code>text.unpack('C*').pack('U*')</code> will do the trick. 7-bit ASCII
|
||||
is identical to UTF-8, so you probably won't need to worry about this.</a>
|
||||
|
||||
<q>How do I get the tag name of an Element?</q>
|
||||
|
||||
<a>You take a look at the APIs, and notice that <code>Element</code>
|
||||
includes <code>Namespace</code>. Then you click on the
|
||||
<code>Namespace</code> link and look at the methods that
|
||||
<code>Element</code> includes from <code>Namespace</code>. One of these is
|
||||
<code>name()</code>. Another is <code>expanded_name()</code>. Yet another
|
||||
is <code>prefix()</code>. Then, you email the author of rdoc and ask him
|
||||
to extend rdoc so that it lists methods in the API that are included from
|
||||
other files, so that you don't have to do all of that looking around for
|
||||
your method.</a>
|
||||
</faq>
|
||||
|
||||
<credits>
|
||||
<p>I've had help from a number of resources; if I haven't listed you here,
|
||||
it means that I just haven't gotten around to adding you, or that I'm a
|
||||
dork and have forgotten. In either case, feel free to write me and
|
||||
complain.</p>
|
||||
|
||||
<list>
|
||||
<item>Mike Stok has been very active, sending not only fixes for bugs
|
||||
(especially in Functions), but also by providing unit tests and making
|
||||
sure REXML runs under Ruby 1.7. He also sent the most awesome hand
|
||||
knitted tea cozy, with "REXML" and the Ruby knitted into it.</item>
|
||||
|
||||
<item>Kouhei Sutou translated the REXML API documentation to Japanese!
|
||||
Links are in the API docs section of the main documentation. He has also
|
||||
contributed a large number of bug reports and patches to fix bugs in
|
||||
REXML.</item>
|
||||
|
||||
<item>Erik Terpstra heard my pleas and submitted several logos for
|
||||
REXML. After sagely procrastinating for several weeks, I finally forced
|
||||
my poor slave of a wife to pick one (this is what we call "delegation").
|
||||
She did, with caveats; Erik quickly made the changes, and the result is
|
||||
what you now see at the top of this page. He also supplied a <link
|
||||
href="img/rexml_50p.png">smaller version</link> that you can include
|
||||
with your projects that use REXML, if you'd like.</item>
|
||||
|
||||
<item>Ernest Ellingson contributed the sourcecode for turning UTF16 and
|
||||
UNILE encodings into UTF8, which allowed REXML to get the 100% OASIS
|
||||
valid tests rating.</item>
|
||||
|
||||
<item>Ian Macdonald provided me with a comprehensive, well written RPM
|
||||
spec file.</item>
|
||||
|
||||
<item>Oliver M . Bolzer is maintaining a Debian package distribution of
|
||||
REXML. He also has provided good feedback and bug reports about
|
||||
namespace support.</item>
|
||||
|
||||
<item>Michael Granger supplied a patch for REXML that make the unit
|
||||
tests pass under Ruby 1.7.</item>
|
||||
|
||||
<item>James Britt contributed code that makes using
|
||||
Document.parse_stream easier to use by allowing it to be passed either a
|
||||
Source, File, or String.</item>
|
||||
|
||||
<item>Tobias Reif: Numerous bug reports, and suggestions for
|
||||
improvement.</item>
|
||||
|
||||
<item>Stefan Scholl, who provided a lot of feedback and bug reports
|
||||
while I was trying to get ISO-8859-1 support working.</item>
|
||||
|
||||
<item>Steven E Lumos for volunteering information about XPath
|
||||
particulars.</item>
|
||||
|
||||
<item>Fumitoshi UKAI provided some bug fixes for CData metacharacter
|
||||
quoting.</item>
|
||||
|
||||
<item>TAKAHASHI Masayoshi, for information on UTF</item>
|
||||
|
||||
<item>Robert Feldt: Bug reports and suggestions/recommendations about
|
||||
improving REXML. Testing is one of the most important aspects of
|
||||
software development.</item>
|
||||
|
||||
<item><link
|
||||
href="http://www.themindelectric.com/exml/index.html">Electric
|
||||
XML</link>: This was, after all, the inspiration for REXML. Originally,
|
||||
I was just going to do a straight port, and although REXML doesn't in
|
||||
any way, shape or form resemble Electric XML, still the basic framework
|
||||
and philosophy was inspired by E-XML. And I still use E-XML in my Java
|
||||
projects.</item>
|
||||
|
||||
<item><link
|
||||
href="http://www.io.com/~jimm/downloads/nqxml/index.html">NQXML</link>:
|
||||
While I may complain about the NQXML API, I wrote a few applications
|
||||
using it that wouldn't have been written otherwise, and it was very
|
||||
useful to me. It also encouraged me to write REXML. Never complain about
|
||||
free software *slap*.</item>
|
||||
|
||||
<item>See my <link
|
||||
href="http://www.germane-software.com/~ser/technology.html">technologies
|
||||
page</link> for a more comprehensive list of computer technologies that
|
||||
I depend on for my day-to-day work.</item>
|
||||
|
||||
<item>rdoc, an excellent JavaDoc analog<footnote>When I was first
|
||||
working on REXML, rdoc wasn't, IMO, very good, so I wrote API2XML.
|
||||
API2XML was good enough for a while, and then there was a flurry of work
|
||||
on rdoc, and it quickly surpassed API2XML in features. Since I was never
|
||||
really interested in maintaining a JavaDoc analog, I stopped support of
|
||||
API2XML, and am now recommending that people use
|
||||
rdoc.</footnote>.</item>
|
||||
|
||||
<item>Many, many other people who've submitted bug reports, suggestions,
|
||||
and positive feedback. You're all co-developers!</item>
|
||||
</list>
|
||||
</credits>
|
||||
</documentation>
|
|
@ -1,296 +0,0 @@
|
|||
<?xml version="1.0" encoding="euc-jp"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN">
|
||||
<html xml:lang="ja">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
<meta name="author" content="U.Nakamura" />
|
||||
<link rev="made" href="mailto:usa@ruby-lang.org" />
|
||||
<link rel="StyleSheet" href="./ruby.css" type="text/css" />
|
||||
<title>Ruby-mswin32</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1><a id="top">Ruby-mswin32</a></h1>
|
||||
<p>あるいは、Windowsとの終わりなき戦い ;-(</p>
|
||||
<p>[日本語 / <a href="./index_en.html">English</a>]</p>
|
||||
|
||||
|
||||
|
||||
<h2><a id="menu">▼ 目次</a></h2>
|
||||
<ul>
|
||||
<li><a href="#remark">注意事項</a></li>
|
||||
<li><a href="#ruby">Rubyとは?</a></li>
|
||||
<li><a href="#mswin32">mswin32版rubyとは?</a></li>
|
||||
<li><a href="#download">バイナリ ダウンロード</a></li>
|
||||
<li><a href="#install">インストール</a></li>
|
||||
<li><a href="#recent">最近の出来事</a></li>
|
||||
<li><a href="#link">リンク</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<h2><a id="remark">▼ 注意事項</a></h2>
|
||||
<p>このページでは、mswin32版rubyの配布と変更状況のお知らせを行っています。</p>
|
||||
<p>ここは別に公式ページでもなんでもなくて、私が勝手に書いてるページです。ここで入手できるプログラム・情報(無いに等しいけど)については、各自の判断でご利用ください。<br />
|
||||
問い合わせは<a href="mailto:usa@ruby-lang.org">私</a>へ。間違っても他の人に迷惑をかけるようなことはしないでね。</p>
|
||||
|
||||
|
||||
|
||||
<h2><a id="ruby">▼ Rubyとは?</a></h2>
|
||||
<p><a href="http://www.ruby-lang.org/ja/">Rubyのサイト</a>をご覧下さい。</p>
|
||||
|
||||
|
||||
|
||||
<h2><a id="mswin32">▼ mswin32版rubyとは?</a></h2>
|
||||
<p>mswin32版rubyとは、32bit版Windows(Windows95・Windows98・WindowsMe・Windows NT・Windows 2000・WindowsXP・Windows 2003 Server、以下Windowsと表記)上で動作するRubyのバイナリの一つです。<br />
|
||||
Windows上で動作するRubyとしては、現在、5種類のバイナリが存在します。これらはそれぞれmswin32版・cygwin版・mingw32版・bccwin32版・djgpp版と呼ばれています。<br />
|
||||
それぞれの違いをまとめると以下のようになります(事実誤認があればお知らせください)。</p>
|
||||
<dl>
|
||||
<dt><a id="label:1">mswin32版</a></dt><dd>
|
||||
<p>VC++でコンパイルされる。Windowsから見ればもっとも「普通」のバイナリと言えるが、反面、Rubyが持つUNIXで特徴的な機能の一部が使用できない。1.7.3以降はmingw32版と拡張ライブラリについてはバイナリ互換性がある。<br />
|
||||
RUBY_PLATFORMは*-mswin32。</p>
|
||||
</dd>
|
||||
<dt><a id="label:2">cygwin版</a></dt><dd>
|
||||
<p>gccでコンパイルされ、<a href="http://sources.redhat.com/cygwin/">cygwin</a>環境で動作する。cygwin環境がUNIXライクな環境をWindows上で構築するものであるので、当然、cygwin版rubyは一般のUNIX用のものとだいたい同じように動作する(ことが期待できる)。<br />
|
||||
RUBY_PLATFORMは*-cygwin。</p>
|
||||
</dd>
|
||||
<dt><a id="label:3">mingw32版</a></dt><dd>
|
||||
<p>gccでコンパイルされる。ソースはほとんどmswin32版と共通であり、ランタイムライブラリも共通(MSVCRT.dll)なので、動作も(おそらく)mswin32版とほぼ同じ。1.7.3以降はmswin32版と拡張ライブラリについてはバイナリ互換性がある。<br />
|
||||
RUBY_PLATFORMは*-mingw32。</p>
|
||||
</dd>
|
||||
<dt><a id="label:4">bccwin32版</a></dt><dd>
|
||||
<p>BC++でコンパイルされる。ソースはかなりの部分がmswin32版と共通ではあるが、ランタイムライブラリが異なるので、細かいところで挙動がmswin32版とは異なる(はず)。1.7以降でサポートされる。<br />
|
||||
RUBY_PLATFORMは*-bccwin32。</p>
|
||||
</dd>
|
||||
<dt><a id="label:5">djgpp版</a></dt><dd>
|
||||
<p>DJGPPでコンパイルされる。DOS用のバイナリなので、もちろんDOSでも動作する。反面、WindowsにあってDOSにない機能の多くが使えない(ネットワーク関連など)。<br />
|
||||
RUBY_PLATFORMは*-msdosdjgpp。</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>このページでは、上記のうちmswin32版のみを扱っています。<br />
|
||||
なお、cygwin版・mingw32版・djgpp版についてはわたなべさんの<a href="http://www.os.rim.or.jp/~eban/">Ruby binaries</a>から入手可能です。また、bccwin32版については小西さんの<a href="http://www001.upp.so-net.ne.jp/konishi/ruby/index.htm">Ruby</a>から入手可能です。</p>
|
||||
|
||||
|
||||
|
||||
<h2><a id="download">▼ バイナリ ダウンロード</a></h2>
|
||||
<p>現在配布中の全てのバイナリはVC++ 5.0(Version 11.00.7022 for 80x86)でmakeしたものです。ruby自体に関しては、<a href="http://www.ruby-lang.org/ja/download.html">標準配布のソース</a>(または<a href="http://www.ruby-lang.org/~knu/cvsrepo-guide.html">CVS</a>のソース)からそのまま作成しています。拡張ライブラリについては各々のリンク先を参照してください。<br />
|
||||
いずれのバイナリもzip形式でアーカイブされています。</p>
|
||||
<p>md5sumのチェック方法ですが、例えばrubyがインストールされているなら下記のような方法があります。<br />
|
||||
<code>ruby -r md5 -e "puts MD5.new(File.open('filename', 'rb').read).hexdigest"</code></p>
|
||||
|
||||
|
||||
|
||||
<h3><a id="release">Release</a></h3>
|
||||
<ul>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.8.1-i386-mswin32.zip">ruby-1.8.1-i386-mswin32.zip</a> (3,764KB) <em>最新Release版</em><br />
|
||||
ruby 1.8.1 (2003-12-25) [i386-mswin32]<br />
|
||||
md5sum : 6bbdabeb29f1a15fa69901e87d1108ac</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.8.0-i386-mswin32.zip">ruby-1.8.0-i386-mswin32.zip</a> (2,507KB)<br />
|
||||
ruby 1.8.0 (2003-08-04) [i386-mswin32]<br />
|
||||
md5sum : eaf9263062429fd4f722d9a70a38a9dc</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.8-i586-mswin32.zip">ruby-1.6.8-i586-mswin32.zip</a> (1,964KB)<br />
|
||||
ruby 1.6.8 (2002-12-24) [i586-mswin32]<br />
|
||||
md5sum : f704f1248ec25b96e3e1f3070afa915e</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.7-i586-mswin32.zip">ruby-1.6.7-i586-mswin32.zip</a> (1,972KB)<br />
|
||||
ruby 1.6.7 (2002-03-01) [i586-mswin32]<br />
|
||||
md5sum : ddedc40d0fc3b0ea1d6ac74f4976bfc6</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.6-i586-mswin32.zip">ruby-1.6.6-i586-mswin32.zip</a> (1,944KB)<br />
|
||||
ruby 1.6.6 (2001-12-26) [i586-mswin32]<br />
|
||||
md5sum : 96e0d1d19a37e5e7e50ae7ce99e34636</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.5-i586-mswin32.zip">ruby-1.6.5-i586-mswin32.zip</a> (1,896KB)<br />
|
||||
ruby 1.6.5 (2001-09-19) [i586-mswin32]<br />
|
||||
md5sum : c708ae98a05df2ff8dea5a70e3791bfa</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.4-i586-mswin32.zip">ruby-1.6.4-i586-mswin32.zip</a> (1,821KB)<br />
|
||||
ruby 1.6.4 (2001-06-04) [i586-mswin32]<br />
|
||||
md5sum : cf813ca19e40be164057b3562575e4da</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3><a id="develop">Developing versions snapshots</a></h3>
|
||||
<ul>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.9.0-20040126-i386-mswin32.zip">ruby-1.9.0-20040126-i386-mswin32.zip</a> (3,849KB)<br />
|
||||
ruby 1.9.0 (2004-01-26) [i386-mswin32]<br />
|
||||
md5sum : fffafbf881cb6a85982220eae5a5b4f5</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<h3><a id="stable">Stable versions snapshots</a></h3>
|
||||
<h4><a id="stable">1.8.0</a></h4>
|
||||
<ul>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.8.1-20040127-i386-mswin32.zip">ruby-1.8.1-20040127-i386-mswin32.zip</a> (3,822KB)<br />
|
||||
ruby 1.8.1 (2004-01-27) [i386-mswin32]<br />
|
||||
md5sum : 1fc0d5f53f0a75d0c6d1ca5d21082089</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.8.1-20031027-i386-mswin32.zip">ruby-1.8.1-20031027-i386-mswin32.zip</a> (3,075KB)<br />
|
||||
ruby 1.8.1 (2003-10-27) [i386-mswin32]<br />
|
||||
md5sum : 9dbdc644c529d207d0bda5d64478a5c4</li>
|
||||
</ul>
|
||||
<h4><a id="stable">1.6.8</a></h4>
|
||||
<ul>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.8-20030727-i586-mswin32.zip">ruby-1.6.8-20030727-i586-mswin32.zip</a> (2,093KB)<br />
|
||||
ruby 1.6.8 (2003-07-27) [i586-mswin32]<br />
|
||||
md5sum : 28c3b92b162319b3d6bc99c9996cad15</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ruby-1.6.8-20030515-i586-mswin32.zip">ruby-1.6.8-20030515-i586-mswin32.zip</a> (2,091KB)<br />
|
||||
ruby 1.6.8 (2003-05-15) [i586-mswin32]<br />
|
||||
md5sum : e5f6558de261d111add4f657ad5e345f</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<h3><a id="ext">Extension libraries</a></h3>
|
||||
<h4><a id="ext">1.8.0</a></h4>
|
||||
<ul>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/racc-1.4.4-all-i386-mswin32-1.8.zip">racc-1.4.4-all-i386-mswin32-1.8.zip</a> (70KB)<br />
|
||||
<a href="http://www.loveruby.net/en/racc.html">racc</a> 1.4.4-all (for ruby 1.8.1)<br />
|
||||
md5sum : 46c4d48b714fb1ded880e7e7af456b28</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/vrswin-030906-i386-mswin32-1.8.zip">vrswin-030906-i386-mswin32-1.8.zip</a> (54KB)<br />
|
||||
<a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">VisualuRuby (swin)</a> 030906 (for ruby 1.8.0)<br />
|
||||
md5sum : 11c2d30e2a05e9ea7e097ec7b066cedf</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/vruby-030906-i386-mswin32-1.8.zip">vruby-030906-i386-mswin32-1.8.zip</a> (96KB)<br />
|
||||
<a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">VisualuRuby (vruby)</a> 030906 (for ruby 1.8.0)<br />
|
||||
md5sum : 77a42995e42e869932f5fb282cc297ea</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/eruby-1.0.4-i386-mswin32-1.8.zip">eruby-1.0.4-i386-mswin32-1.8.zip</a> (75KB)<br />
|
||||
<a href="http://www.modruby.net/">eruby</a> 1.0.4 (for ruby 1.8.0)<br />
|
||||
md5sum : de7282647f015b1d20a28dcf7c2b8715</li>
|
||||
</ul>
|
||||
<h4><a id="ext">1.6.8</a></h4>
|
||||
<ul>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/vrswin-030521-i586-mswin32-1.6.zip">vrswin-030521-i586-mswin32-1.6.zip</a> (55KB)<br />
|
||||
<a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">VisualuRuby (swin)</a> 030521 (for ruby 1.6.8)<br />
|
||||
md5sum : eae3284c6f79be7a119858ff9e940985</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/vruby-030517-i586-mswin32-1.6.zip">vruby-030517-i586-mswin32-1.6.zip</a> (95KB)<br />
|
||||
<a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">VisualuRuby (vruby)</a> 030517 (for ruby 1.6.8)<br />
|
||||
md5sum : a32af752428cf3aa03000d66d8deca33</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/tmail-0.10.7-i586-mswin32-1.6.zip">tmail-0.10.7-i586-mswin32-1.6.zip</a> (160KB)<br />
|
||||
<a href="http://www.loveruby.net/en/tmail.html">TMail</a> 0.10.7 (for ruby 1.6.8)<br />
|
||||
md5sum : 74351ed81550dfbf3bfaf8252c316326</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/eruby-1.0.3-i586-mswin32-1.6.zip">eruby-1.0.3-i586-mswin32-1.6.zip</a> (74KB)<br />
|
||||
<a href="http://www.modruby.net/">eruby</a> 1.0.3 (for ruby 1.6.8)<br />
|
||||
md5sum : e05d654128422846f86ca84f55bf7bcb</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/rubywin-0.0.4.3-i586-mswin32-1.6.zip">rubywin-0.0.4.3-i586-mswin32-1.6.zip</a> (286KB)<br />
|
||||
<a href="http://homepage1.nifty.com/markey/ruby/rubywin/index_e.html">RubyWin</a> 0.0.4.3 (for ruby 1.6.8)<br />
|
||||
md5sum : 3f2226ef0c6e41b31c2f337f778e3e18</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/shim-20021224-i586-mswin32-1.6.zip">shim-20021224-i586-mswin32-1.6.zip</a> (138KB)<br />
|
||||
<a href="http://www.ruby-lang.org/~knu/cgi-bin/cvsweb.cgi/shim/">Ruby Shim</a> 20021224 (for ruby 1.6.8)<br />
|
||||
md5sum : 7ee4363195973a1df0584cb467e5ce82</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/racc-1.4.3-all-i586-mswin32-1.6.zip">racc-1.4.3-all-i586-mswin32-1.6.zip</a> (124KB)<br />
|
||||
<a href="http://www.loveruby.net/en/racc.html">racc</a> 1.4.3-all (for ruby 1.6.8)<br />
|
||||
md5sum : 1f093aabb464bef3074112949228a8c6</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/win32ole-0.5.2-i586-mswin32-1.6.zip">win32ole-0.5.2-i586-mswin32-1.6.zip</a> (59KB)<br />
|
||||
<a href="http://homepage1.nifty.com/markey/ruby/win32ole/index_e.html">Win32OLE</a> 0.5.2 (for ruby 1.6.8)<<br />
|
||||
md5sum : 960f7205923a9243cff567d291b254ad</li>
|
||||
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/binaries/mswin32/ext/uconv-0.4.11-i586-mswin32-1.6.zip">uconv-0.4.11-i586-mswin32-1.6.zip</a> (110KB)<br />
|
||||
<a href="http://www.yoshidam.net/Ruby.html#uconv">uconv</a> 0.4.11 (for ruby 1.6.8)<br />
|
||||
md5sum : c08f3662abee8e7186283741f89b88d7</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<h2><a id="install">▼ インストール</a></h2>
|
||||
<p>上記のバイナリをインストールする場合は、お好みのディレクトリ(以下<code>$TOPDIR</code>と記述)に展開してください。ディレクトリ付きで圧縮されてますので、展開時にはディレクトリ付きで展開するのを忘れずに(意味がわからない人は気にしなくていいです)。<br />
|
||||
展開後は、<code>$TOPDIR\bin</code>に<code>PATH</code>を通しておいてください。</p>
|
||||
<p>なお、以下の拡張ライブラリは、この配布物に含まれない外部のライブラリに依存しています。</p>
|
||||
<ul>
|
||||
<li>curses.so : PDCursesに依存しています。</li>
|
||||
<li>dbm.so : GDBMに依存しています。</li>
|
||||
<li>gdbm.so : GDBMに依存しています。</li>
|
||||
<li>iconv.so : Iconvに依存しています。</li>
|
||||
<li>openssl.so : OpenSSLに依存しています。</li>
|
||||
<li>readline.so : readlineに依存しています。</li>
|
||||
<li>tcltklib.so : Tcl/Tkに依存しています。</li>
|
||||
<li>zlib.so : Zlibに依存しています。</li>
|
||||
</ul>
|
||||
<p>上記のうち、PDCurses・GDBM・OpenSSL・readline・Zlibについては、<a href="http://jarp.jin.gr.jp/win32/">Porting Libraries to Win32</a>にバイナリが存在します。<br />
|
||||
Iconvについては、Meadowy.orgで配布されている<a href="http://www.meadowy.org/meadow/dists/snapshot/iconv-1.8.win32.zip">iconv-1.8.win32.zip</a>を利用しています。<br />
|
||||
Tcl/Tkについては、<a href="http://www.activestate.com/">ActiveState</a>で配布されている<a href="http://www.activestate.com/Products/ActiveTcl/">ActiveTcl</a>を利用しています。</p>
|
||||
|
||||
|
||||
|
||||
<h2><a id="recent">▼ 最近の出来事</a></h2>
|
||||
|
||||
|
||||
|
||||
<h3><a id="d20040127">2004-01-27</a></h3>
|
||||
<p>あけましておめでとうございます。って遅いよ!</p>
|
||||
<p>ruby-1.8.1-20040127とruby-1.9.0-20040126を置きました。<br />
|
||||
前者は主に1.8.1リリース版で発見された不具合の修正です。後者は開発版。</p>
|
||||
|
||||
<h3><a id="d20031225">2003-12-25</a></h3>
|
||||
<p>メリークリスマス! ruby-1.8.1がリリースされました!<br />
|
||||
(previewを一つ飛ばしちゃいました、ごめんなさい...)</p>
|
||||
|
||||
<h3><a id="d20031206">2003-12-06</a></h3>
|
||||
<p>ruby-1.8.1-preview3を置きました。思いのほか間が空いちゃいましたね。</p>
|
||||
|
||||
<h3><a id="d20031101">2003-11-01</a></h3>
|
||||
<p>ruby-1.8.1-preview2を置きました。</p>
|
||||
|
||||
<h3><a id="d20031028">2003-10-28</a></h3>
|
||||
<p>ruby-1.8.1-20031027を置きました。<br />
|
||||
racc-1.4.4-allを置きました。</p>
|
||||
|
||||
<h3><a id="d20030907">2003-09-07</a></h3>
|
||||
<p>eruby-1.0.4・vrswin-030906・vruby-030906を置きました。</p>
|
||||
|
||||
<h3><a id="d20030812">2003-08-12</a></h3>
|
||||
<p>ruby-1.8.0-20030812を置きました。</p>
|
||||
<p>vrswin-030811・vruby-030811を置きました。今回から置く拡張ライブラリは全て1.8用になります。</p>
|
||||
|
||||
<h3><a id="d20030804">2003-08-04</a></h3>
|
||||
<p>ruby-1.8.0を置きました。Ruby 1.8系最初のリリースとなります。<br />
|
||||
1.6系からの変更点については、<a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/changes.1.8.0">changes.1.8.0</a>などをご覧ください。</p>
|
||||
|
||||
<h3><a id="d20030731">2003-07-31</a></h3>
|
||||
<p>ruby-1.8.0-preview6を置きました。えーと、これを含めてあと2回previewが出る模様です :)</p>
|
||||
|
||||
<h3><a id="d20030728">2003-07-28</a></h3>
|
||||
<p>ruby-1.8.0-preview5を置きました。おそらくこれが1.8.0の最後のpreviewになるでしょう。<br />
|
||||
ruby-1.6.8-20030727を置きました。</p>
|
||||
|
||||
|
||||
|
||||
<h3><a id="old">かつての出来事</a></h3>
|
||||
<p><a href="./old.html">こちら</a>をどうぞ。</p>
|
||||
|
||||
|
||||
|
||||
<h2><a id="link">▼ リンク</a></h2>
|
||||
<p>mswin32版に関する(と思われる)リンクです。勝手に張ってますので、不都合があれば<a href="mailto:usa@osb.att.ne.jp">私</a>までご連絡ください。</p>
|
||||
<ul>
|
||||
<li><a href="http://www.ruby-lang.org/">Ruby Home Page</a> (<a href="http://www.ruby-lang.org/ja/">日本語</a> / <a href="http://www.ruby-lang.org/en/">English</a>)<br />
|
||||
言わずとしれた、本家サイト。</li>
|
||||
<li>雑記帳 (<a href="http://homepage1.nifty.com/markey/">日本語</a> / <a href="http://homepage1.nifty.com/markey/index_e.html">English</a>)<br />
|
||||
助田さんのページ。<a href="http://homepage1.nifty.com/markey/ruby/rubywin/index.html">RubWin</a>・<a href="http://homepage1.nifty.com/markey/ruby/win32ole/index.html">Win32OLE</a>などがあります。</li>
|
||||
<li><a href="http://www.moonwolf.com/ruby/">Script/Ruby</a> (日本語)<br />
|
||||
MoonWolfさんのページ。Win32Moduleなどがあります。</li>
|
||||
<li>ActiveScriptRuby (<a href="http://www.geocities.co.jp/SiliconValley-PaloAlto/9251/ruby/index.html">日本語</a> / <a href="http://www.geocities.co.jp/SiliconValley-PaloAlto/9251/ruby/main.html">English</a>)<br />
|
||||
artonさんのページ。Windows版Rubyの未来はここにあるのかも。</li>
|
||||
<li>VisualuRuby計画(仮称) (<a href="http://www.osk.3web.ne.jp/~nyasu/software/vrproject.html">日本語</a> / <a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">English</a>)<br />
|
||||
nyasuさんのページ。VisualuRubyなどがあります(ってこれもそのまんま)。WindowsでRubyを使うための<a href="http://www.osk.3web.ne.jp/~nyasu/software/rubyonwin.html">リンク集</a>が充実してます。ちなみに、当サイトのデザインはこちらのパクリです。多謝!</li>
|
||||
<li>Ruby (<a href="http://www.yoshidam.net/Ruby_ja.html">日本語</a> / <a href="http://www.yoshidam.net/Ruby.html">English</a>)<br />
|
||||
よしだむさんのページ。<a href="http://www.yoshidam.net/Ruby_ja.html#susie">Susieプラグインライブラリ</a>や<a href="http://www.yoshidam.net/Ruby_ja.html#rddraw">DirectDraw for Ruby</a>などがあります。</li>
|
||||
<li><a href="http://homepage2.nifty.com/sakazuki/rde.html">RDE(Ruby Development Environment)</a> (日本語)<br />
|
||||
sakazukiさんのページ。mswin32版ユーザなら必見です。推奨環境はartonさんとこのActiveScriptRubyですが (^^; ここの配布物を使っても問題なく動作します。</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<p class="footer">
|
||||
[<a href="../">戻る</a>]
|
||||
</p>
|
||||
<address>written by <a href="mailto:usa@ruby-lang.org">U.Nakamura</a></address>
|
||||
<p class="versions">
|
||||
ruby 1.9.0 (2004-01-13)<br />
|
||||
ERb 2.0.4<br />
|
||||
RDtool 0.6.11<br />
|
||||
rublog 0.0.2
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
|
||||
<evaluate>
|
||||
<data>
|
||||
<jumps>
|
||||
<subject>
|
||||
<the/>
|
||||
<fox color="brown"/>
|
||||
<speed category="quick"/>
|
||||
</subject>
|
||||
<over/>
|
||||
<object>
|
||||
<the/>
|
||||
<dog color="unspecified"/>
|
||||
<speed category="lazy"/>
|
||||
</object>
|
||||
</jumps>
|
||||
</data>
|
||||
|
||||
<!-- there is one element with attribute color="brown" should this
|
||||
meta-test should succeed -->
|
||||
|
||||
<metatest select="//@color">brown</metatest>
|
||||
|
||||
<!-- there is no element with attribute category="moderate" -->
|
||||
<metatest select="//speed/@category">moderate</metatest>
|
||||
|
||||
</evaluate>
|
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Fibonacci_Numbers>
|
||||
<fibonacci index="0">0</fibonacci>
|
||||
<fibonacci index="1">1</fibonacci>
|
||||
<fibonacci index="2">1</fibonacci>
|
||||
<fibonacci index="3">2</fibonacci>
|
||||
<fibonacci index="4">3</fibonacci>
|
||||
<fibonacci index="5">5</fibonacci>
|
||||
<fibonacci index="6">8</fibonacci>
|
||||
<fibonacci index="7">13</fibonacci>
|
||||
<fibonacci index="8">21</fibonacci>
|
||||
<fibonacci index="9">34</fibonacci>
|
||||
<fibonacci index="10">55</fibonacci>
|
||||
<fibonacci index="11">89</fibonacci>
|
||||
<fibonacci index="12">144</fibonacci>
|
||||
<fibonacci index="13">233</fibonacci>
|
||||
<fibonacci index="14">377</fibonacci>
|
||||
<fibonacci index="15">610</fibonacci>
|
||||
<fibonacci index="16">987</fibonacci>
|
||||
<fibonacci index="17">1597</fibonacci>
|
||||
<fibonacci index="18">2584</fibonacci>
|
||||
<fibonacci index="19">4181</fibonacci>
|
||||
<fibonacci index="20">6765</fibonacci>
|
||||
<fibonacci index="21">10946</fibonacci>
|
||||
<fibonacci index="22">17711</fibonacci>
|
||||
<fibonacci index="23">28657</fibonacci>
|
||||
<fibonacci index="24">46368</fibonacci>
|
||||
<fibonacci index="25">75025</fibonacci>
|
||||
</Fibonacci_Numbers>
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE schema SYSTEM "foo.dtd" [
|
||||
<!ATTLIST root-el
|
||||
xmlns:human CDATA #FIXED "http://www.foo.com/human">
|
||||
]>
|
||||
<root-el xmlns="http://www.bar.com/doc"
|
||||
xmlns:table="http://www.foo.com/table">
|
||||
<human:leg>human leg</human:leg>
|
||||
<table:leg>table leg</table:leg>
|
||||
</root-el>
|
|
@ -1,156 +0,0 @@
|
|||
<form xmlns='http://www.w3.org/1999/xhtml'
|
||||
enctype='application/x-www-form-urlencoded' class='rollover'
|
||||
action='ModifyCampaign' method='POST'
|
||||
onsubmit='return beforeRolloverSubmit(this, 'No campaigns selected.');'>
|
||||
<a name='campaigns' shape='rect'/>
|
||||
<tr bgcolor='#dbe6de'>
|
||||
<th class='boxcolumn' rowspan='1' align='left' colspan='1'
|
||||
width='1%'>
|
||||
<script type='text/javascript'>document.write(" <input
|
||||
type\u003d\"checkbox\" name\u003d\"toggleAll\"
|
||||
onclick\u003d\"rowToggleAll(this);\" title\u003d\"Select or
|
||||
de-select all campaigns on this page\"> ");</script>
|
||||
</th>
|
||||
<th bgcolor='#dbe6de' title='Sort by campaign name' nowrap='nowrap'
|
||||
rowspan='1' align='left' colspan='1'>
|
||||
<b>
|
||||
<a href='CampaignSummary?campaignsummaryt=0%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>Campaign Name</a>
|
||||
</b>
|
||||
</th>
|
||||
<th bgcolor='#dbe6de' title='Sort by campaign status'
|
||||
nowrap='nowrap' rowspan='1' align='left' colspan='1'>
|
||||
<a href='CampaignSummary?campaignsummaryt=1%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>Current Status</a>
|
||||
</th>
|
||||
<th bgcolor='#dbe6de'
|
||||
title='Sort by daily budget (maximum spending per day)'
|
||||
nowrap='nowrap' rowspan='1' align='right' colspan='1'>
|
||||
<a href='CampaignSummary?campaignsummaryt=2%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>Current Budget</a>
|
||||
<span style='white-space: nowrap'>
|
||||
<a href='/support/bin/answer.py?answer=6312&hl=en_US'
|
||||
shape='rect' id='' onclick='return helpPopUp(this);' style=''
|
||||
target='google_popup'>[?]</a>
|
||||
</span>
|
||||
</th>
|
||||
<th bgcolor='#c6d7cf' title='Sort by clicks on your ads'
|
||||
nowrap='nowrap' rowspan='1' align='right' colspan='1'>
|
||||
<a href='CampaignSummary?campaignsummaryt=-3%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>Clicks</a>
|
||||
<a href='CampaignSummary?campaignsummaryt=-3%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
shape='rect' style='text-decoration:none;'>
|
||||
<img src='/select/images/sortdown.gif' border='0' alt=''/>
|
||||
</a>
|
||||
</th>
|
||||
<th bgcolor='#dbe6de' title='Sort by ad impressions served'
|
||||
nowrap='nowrap' rowspan='1' align='right' colspan='1'>
|
||||
<a href='CampaignSummary?campaignsummaryt=4%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>Impr.</a>
|
||||
</th>
|
||||
<th bgcolor='#dbe6de' title='Sort by CTR (clickthrough rate)'
|
||||
nowrap='nowrap' rowspan='1' align='right' colspan='1'>
|
||||
<a href='CampaignSummary?campaignsummaryt=5%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>CTR</a>
|
||||
</th>
|
||||
<th bgcolor='#dbe6de' title='Sort by average cost per click (USD)'
|
||||
nowrap='nowrap' rowspan='1' align='right' colspan='1'>
|
||||
<a href='CampaignSummary?campaignsummaryt=6%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>Avg. CPC</a>
|
||||
</th>
|
||||
<th bgcolor='#dbe6de' class='' title='Sort by total cost (USD)'
|
||||
nowrap='nowrap' rowspan='1' align='right' colspan='1'>
|
||||
<a href='CampaignSummary?campaignsummaryt=8%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>Cost</a>
|
||||
</th>
|
||||
<th bgcolor='#dbe6de' title='Conversion Rate' nowrap='nowrap'
|
||||
rowspan='1' align='right' colspan='1'>
|
||||
<a href='CampaignSummary?campaignsummaryt=11%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>Conv. Rate</a>
|
||||
</th>
|
||||
<th bgcolor='#dbe6de' class='rightcolumn'
|
||||
title='Cost per Conversion' nowrap='nowrap' rowspan='1'
|
||||
align='right' colspan='1'>
|
||||
<a href='CampaignSummary?campaignsummaryt=12%3Aa&gsessionid=ezmXK9aaXnI#campaigns'
|
||||
class='bluelink' shape='rect'>Cost/Conv.</a>
|
||||
</th>
|
||||
</tr>
|
||||
<tr onmouseover='ron(3527627);' id='tr_3527627'
|
||||
onmouseout='roff(3527627);'>
|
||||
<td class='boxcolumn' rowspan='1' onclick='rowToggle(3527627);'
|
||||
colspan='1'>
|
||||
<input name='campaignid' type='checkbox' id='box_3527627'
|
||||
value='3527627' onclick='toggleRow(this);'/>
|
||||
</td>
|
||||
<td rowspan='1' colspan='1'>
|
||||
<a href='CampaignManagement?campaignid=3527627#a' shape='rect'>Test</a>
|
||||
</td>
|
||||
<td rowspan='1' colspan='1'>
|
||||
<b>
|
||||
<font size='-1' color='#b98b00'>Paused</font>
|
||||
</b>
|
||||
</td>
|
||||
<td class='r' rowspan='1' colspan='1'>
|
||||
<font color='#666666'>Test</font>
|
||||
</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='' rowspan='1' align='right' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='rightcolumn' rowspan='1' align='right' colspan='1'>1</td>
|
||||
</tr>
|
||||
<tr onmouseover='ron(7680287);' id='tr_7680287'
|
||||
onmouseout='roff(7680287);'>
|
||||
<td class='boxcolumn' rowspan='1' onclick='rowToggle(7680287);'
|
||||
colspan='1'>
|
||||
<input name='campaignid' type='checkbox' id='box_7680287'
|
||||
value='7680287' onclick='toggleRow(this);'/>
|
||||
</td>
|
||||
<td rowspan='1' colspan='1'>
|
||||
<a href='CampaignManagement?campaignid=7680287#a' shape='rect'>Test</a>
|
||||
</td>
|
||||
<td rowspan='1' colspan='1'>
|
||||
<b>
|
||||
<font size='-1' color='#b98b00'>Paused</font>
|
||||
</b>
|
||||
</td>
|
||||
<td class='r' rowspan='1' colspan='1'>
|
||||
<font color='#666666'>Test</font>
|
||||
</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='' rowspan='1' align='right' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='rightcolumn' rowspan='1' align='right' colspan='1'>1</td>
|
||||
</tr>
|
||||
<tr onmouseover='ron(6747347);' id='tr_6747347'
|
||||
onmouseout='roff(6747347);'>
|
||||
<td class='boxcolumn' rowspan='1' onclick='rowToggle(6747347);'
|
||||
colspan='1'>
|
||||
<input name='campaignid' type='checkbox' id='box_6747347'
|
||||
value='6747347' onclick='toggleRow(this);'/>
|
||||
</td>
|
||||
<td rowspan='1' colspan='1'>
|
||||
<a href='CampaignManagement?campaignid=6747347#a' shape='rect'>Test</a>
|
||||
</td>
|
||||
<td rowspan='1' colspan='1'>
|
||||
<b>
|
||||
<font size='-1' color='#b98b00'>Test</font>
|
||||
</b>
|
||||
</td>
|
||||
<td class='r' rowspan='1' colspan='1'>
|
||||
<font color='#666666'>Test</font>
|
||||
</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='' rowspan='1' align='right' colspan='1'>1</td>
|
||||
<td class='r' rowspan='1' colspan='1'>1</td>
|
||||
<td class='rightcolumn' rowspan='1' align='right' colspan='1'>1</td>
|
||||
</tr>
|
||||
</form>
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!DOCTYPE foo [
|
||||
|
||||
<!ELEMENT foo (bar)>
|
||||
<!ATTLIST foo id CDATA #IMPLIED>
|
||||
<!ELEMENT bar (#PCDATA|cheese)*>
|
||||
<!ATTLIST bar id ID #REQUIRED>
|
||||
<!ELEMENT cheese (#PCDATA)>
|
||||
<!ATTLIST cheese kind ID #IMPLIED>
|
||||
]>
|
||||
|
||||
<foo id="foobar">
|
||||
<bar id="fb1">
|
||||
baz
|
||||
<cheese kind="edam">gouda</cheese>
|
||||
baz
|
||||
<cheese kind="gouda">cheddar</cheese>
|
||||
baz
|
||||
</bar>
|
||||
</foo>
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version='1.0' encoding='ISO-8859-1'?>
|
||||
<booh>
|
||||
<image caption='andrè is nice'/>
|
||||
</booh>
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<body><p><span></span></p><div></div></body>
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration>
|
||||
<hostname>
|
||||
<val>2</val>
|
||||
<attrlist>
|
||||
<hostname>CE-A</hostname>
|
||||
</attrlist>
|
||||
</hostname>
|
||||
<hostname>
|
||||
<val>1</val>
|
||||
<attrlist>
|
||||
<hostname>CE-B</hostname>
|
||||
</attrlist>
|
||||
</hostname>
|
||||
</Configuration>
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<e1 xml:lang="hr">
|
||||
<e2 xml:lang="en-US">
|
||||
<e3/>
|
||||
</e2>
|
||||
<e2 xml:lang="hu">
|
||||
<e3/>
|
||||
<e3/>
|
||||
<e3 xml:lang="es"/>
|
||||
</e2>
|
||||
</e1>
|
|
@ -1,18 +0,0 @@
|
|||
<programming_languages>
|
||||
<language oop='yes'>
|
||||
<name>
|
||||
Ruby
|
||||
</name>
|
||||
<creator>
|
||||
Yukihiro Matsumoto
|
||||
</creator>
|
||||
</language>
|
||||
<language oop='yes'>
|
||||
<name>
|
||||
Python
|
||||
</name>
|
||||
<creator>
|
||||
Guido van Rossum
|
||||
</creator>
|
||||
</language>
|
||||
</programming_languages>
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<message>
|
||||
<header>
|
||||
<service>lookupformservice</service>
|
||||
<connectionid>9</connectionid>
|
||||
<appid>stammdaten</appid>
|
||||
<action>new</action>
|
||||
</header>
|
||||
<body>
|
||||
<data>
|
||||
<items>
|
||||
<item>
|
||||
<name>iteminfo</name>
|
||||
<value>ELE</value>
|
||||
</item>
|
||||
<item>
|
||||
<name>parentinfo</name>
|
||||
<value>Pruefgebiete</value>
|
||||
</item>
|
||||
<item>
|
||||
<name>id</name>
|
||||
<value>1</value>
|
||||
</item>
|
||||
</items>
|
||||
</data>
|
||||
</body>
|
||||
</message>
|
|
@ -1,244 +0,0 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<moreovernews>
|
||||
<article code="13563275">
|
||||
<url>http://c.moreover.com/click/here.pl?x13563273</url>
|
||||
<headline_text>e-Commerce Operators Present Version 1.0 of the XML Standard</headline_text>
|
||||
<source>StockAccess</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.stockaccess.com/index.html</document_url>
|
||||
<harvest_time>Dec 24 2000 6:28AM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13560996">
|
||||
<url>http://c.moreover.com/click/here.pl?x13560995</url>
|
||||
<headline_text>W3C Publishes XML Protocol Requirements Document</headline_text>
|
||||
<source>Xml</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.xml.com/</document_url>
|
||||
<harvest_time>Dec 24 2000 12:22AM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13553522">
|
||||
<url>http://c.moreover.com/click/here.pl?x13553521</url>
|
||||
<headline_text>Prowler: Open Source XML-Based Content Management Framework</headline_text>
|
||||
<source>Xml</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.xml.com/</document_url>
|
||||
<harvest_time>Dec 23 2000 2:05PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13549014">
|
||||
<url>http://c.moreover.com/click/here.pl?x13549013</url>
|
||||
<headline_text>The Middleware Company Debuts Public Training Courses in Ejb, J2ee And Xml</headline_text>
|
||||
<source>Java Industry Connection</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
|
||||
<harvest_time>Dec 23 2000 12:15PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13544468">
|
||||
<url>http://c.moreover.com/click/here.pl?x13544467</url>
|
||||
<headline_text>Revised Working Draft for the W3C XML Information Set</headline_text>
|
||||
<source>Xml</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.xml.com/</document_url>
|
||||
<harvest_time>Dec 23 2000 5:50AM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13534837">
|
||||
<url>http://c.moreover.com/click/here.pl?x13534836</url>
|
||||
<headline_text>XML: Its The Great Peacemaker</headline_text>
|
||||
<source>ZDNet</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.zdnet.com/intweek/</document_url>
|
||||
<harvest_time>Dec 22 2000 9:05PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13533486">
|
||||
<url>http://c.moreover.com/click/here.pl?x13533485</url>
|
||||
<headline_text>Project eL - The XML Leningrad Codex Markup Project</headline_text>
|
||||
<source>Xml</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.xml.com/</document_url>
|
||||
<harvest_time>Dec 22 2000 8:34PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13533489">
|
||||
<url>http://c.moreover.com/click/here.pl?x13533488</url>
|
||||
<headline_text>XML Linking Language (XLink) and XML Base Specifications Issued as W3C Proposed Recommenda</headline_text>
|
||||
<source>Xml</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.xml.com/</document_url>
|
||||
<harvest_time>Dec 22 2000 8:34PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13533493">
|
||||
<url>http://c.moreover.com/click/here.pl?x13533492</url>
|
||||
<headline_text>W3C Releases XHTML Basic Specification as a W3C Recommendation</headline_text>
|
||||
<source>Xml</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.xml.com/</document_url>
|
||||
<harvest_time>Dec 22 2000 8:34PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13521835">
|
||||
<url>http://c.moreover.com/click/here.pl?x13521827</url>
|
||||
<headline_text>Java, Xml And Oracle9i(TM) Make A Great Team</headline_text>
|
||||
<source>Java Industry Connection</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
|
||||
<harvest_time>Dec 22 2000 3:21PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13512020">
|
||||
<url>http://c.moreover.com/click/here.pl?x13511233</url>
|
||||
<headline_text>Competing initiatives to vie for security standard</headline_text>
|
||||
<source>ZDNet</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.zdnet.com/eweek/filters/news/</document_url>
|
||||
<harvest_time>Dec 22 2000 10:54AM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13492401">
|
||||
<url>http://c.moreover.com/click/here.pl?x13492397</url>
|
||||
<headline_text>Oracle Provides Developers with Great Xml Reading This Holiday Season</headline_text>
|
||||
<source>Java Industry Connection</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
|
||||
<harvest_time>Dec 21 2000 8:08PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13491296">
|
||||
<url>http://c.moreover.com/click/here.pl?x13491292</url>
|
||||
<headline_text>XML as the great peacemaker - Extensible Markup Language Accomplished The Seemingly Impossible This Year: It B</headline_text>
|
||||
<source>Hospitality Net</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.hospitalitynet.org/news/list.htm?c=2000</document_url>
|
||||
<harvest_time>Dec 21 2000 7:45PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13484761">
|
||||
<url>http://c.moreover.com/click/here.pl?x13484758</url>
|
||||
<headline_text>XML as the great peacemaker</headline_text>
|
||||
<source>CNET</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://news.cnet.com/news/0-1003.html?tag=st.ne.1002.dir.1003</document_url>
|
||||
<harvest_time>Dec 21 2000 4:41PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13480897">
|
||||
<url>http://c.moreover.com/click/here.pl?x13480896</url>
|
||||
<headline_text>COOP Switzerland Selects Mercator as Integration Platform</headline_text>
|
||||
<source>Stockhouse Canada</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.stockhouse.ca/news/</document_url>
|
||||
<harvest_time>Dec 21 2000 1:55PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13471024">
|
||||
<url>http://c.moreover.com/click/here.pl?x13471023</url>
|
||||
<headline_text>Competing XML Specs Move Toward a Union</headline_text>
|
||||
<source>Internet World</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://www.internetworld.com/</document_url>
|
||||
<harvest_time>Dec 21 2000 11:14AM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13452281">
|
||||
<url>http://c.moreover.com/click/here.pl?x13452280</url>
|
||||
<headline_text>Next-generation XHTML stripped down for handhelds</headline_text>
|
||||
<source>CNET</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://news.cnet.com/news/0-1005.html?tag=st.ne.1002.dir.1005</document_url>
|
||||
<harvest_time>Dec 20 2000 9:11PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13451791">
|
||||
<url>http://c.moreover.com/click/here.pl?x13451789</url>
|
||||
<headline_text>Xml Powers Oracle9i(TM) Dynamic Services</headline_text>
|
||||
<source>Java Industry Connection</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
|
||||
<harvest_time>Dec 20 2000 9:05PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13442098">
|
||||
<url>http://c.moreover.com/click/here.pl?x13442097</url>
|
||||
<headline_text>XML DOM reference guide</headline_text>
|
||||
<source>ASPWire</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://aspwire.com/</document_url>
|
||||
<harvest_time>Dec 20 2000 6:26PM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
<article code="13424118">
|
||||
<url>http://c.moreover.com/click/here.pl?x13424117</url>
|
||||
<headline_text>Repeat/Xqsite And Bowstreet Team to Deliver Integrated Xml Solutions</headline_text>
|
||||
<source>Java Industry Connection</source>
|
||||
<media_type>text</media_type>
|
||||
<cluster>moreover...</cluster>
|
||||
<tagline> </tagline>
|
||||
<document_url>http://industry.java.sun.com/javanews/more/hotnews/</document_url>
|
||||
<harvest_time>Dec 20 2000 9:04AM</harvest_time>
|
||||
<access_registration> </access_registration>
|
||||
<access_status> </access_status>
|
||||
</article>
|
||||
</moreovernews>
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<foo:a xmlns:foo="http://fooNamespace/">
|
||||
<b>
|
||||
<c>Hello</c>
|
||||
</b>
|
||||
|
||||
<foo:d>
|
||||
<foo:e>Hey</foo:e>
|
||||
</foo:d>
|
||||
|
||||
<bar:f xmlns:bar="http://barNamespace/">
|
||||
<bar:g>Hey2</bar:g>
|
||||
</bar:f>
|
||||
|
||||
<alias:x xmlns:alias="http://fooNamespace/">
|
||||
<alias:y>Hey3</alias:y>
|
||||
</alias:x>
|
||||
</foo:a>
|
|
@ -1,67 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<nitf>
|
||||
|
||||
<!-- Example of markup of URLs (at the bottom of the story) -->
|
||||
|
||||
<head>
|
||||
<meta name="ap-cycle" content="AP"/>
|
||||
<meta name="ap-online-code" content="1700"/>
|
||||
<meta name="ap-company" content="CO:Media Metrix Inc;TS:MMXI;IG:SVC;"/>
|
||||
<meta name="ap-routing" content="ENTITLEMENTS,pfONLINE,pf1700"/>
|
||||
<meta name="ap-format" content="bx"/>
|
||||
<meta name="ap-category" content="f"/>
|
||||
<meta name="ap-selector" content="-----"/>
|
||||
<meta name="ap-transref" content="V0347"/>
|
||||
<docdata>
|
||||
<doc-id regsrc="AP" id-string="D76UIMO80"/>
|
||||
<urgency ed-urg="7"/>
|
||||
<date.issue norm="20000911T185842Z"/>
|
||||
<du-key key="Napster Traffic"/>
|
||||
<doc.copyright holder="(AP)"/>
|
||||
</docdata>
|
||||
</head>
|
||||
<body>
|
||||
<body.head>
|
||||
<hedline>
|
||||
<hl1>Use of Napster Quadruples</hl1>
|
||||
</hedline>
|
||||
<byline>By PETER SVENSSON
|
||||
<byttl>AP Business Writer</byttl>
|
||||
</byline>
|
||||
<distributor>The Associated Press</distributor>
|
||||
<dateline>
|
||||
<location>NEW YORK</location>
|
||||
</dateline>
|
||||
</body.head>
|
||||
<body.content>
|
||||
<block>
|
||||
<p>Despite the uncertain legality of the Napster online music-sharing service, the number of people
|
||||
using it more than quadrupled in just five months, Media Metrix said Monday.</p>
|
||||
<p>That made Napster the fastest-growing software application ever recorded by the Internet research
|
||||
company.</p>
|
||||
<p>From 1.1 million home users in the United States in February, the first month Media Metrix
|
||||
tracked the application, Napster use rocketed to 4.9 million users in July.</p>
|
||||
<p>That represents 6 percent of U.S. home PC users who have modems, said Media Metrix, which pays
|
||||
people to install monitoring software on their computers.</p>
|
||||
<p>It estimates total usage from a panel of about 50,000 people in the United States.</p>
|
||||
<p>Napster was also used at work by 887,000 people in July, Media Metrix said.</p>
|
||||
<p>Napster Inc. has been sued by the recording industry for allegedly enabling copyright
|
||||
infringement. The federal government weighed in on the case Friday, saying the service is not protected
|
||||
under a key copyright law, as the San Mateo, Calif., company claims.</p>
|
||||
<p>Bruce Ryon, head of Media Metrix's New Media Group, said Napster was used by "the full spectrum of PC users, not just the youth with time on their hands and a passion for music."</p>
|
||||
<p>The Napster program allows users to copy digital music files from the hard drives of other
|
||||
users over the Internet.</p>
|
||||
<p>Napster Inc. said last week that 28 million people had downloaded its program. It does not reveal
|
||||
its own figures for how many people actually use the software.</p>
|
||||
<p>Because the program connects to the company's computers over the Internet every time
|
||||
it is run, Napster Inc. can track usage exactly.</p>
|
||||
<p>__</p>
|
||||
<p>On the Net:</p>
|
||||
<p><a href="http://www.napster.com">
|
||||
http://www.napster.com</a></p>
|
||||
<p><a href="http://www.mediametrix.com">
|
||||
http://www.mediametrix.com</a></p>
|
||||
</block>
|
||||
</body.content>
|
||||
</body>
|
||||
</nitf>
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<numbers>
|
||||
<set>
|
||||
<nr>3</nr>
|
||||
<nr>24</nr>
|
||||
<nr>55</nr>
|
||||
<nr>11</nr>
|
||||
<nr>2</nr>
|
||||
<nr>-3</nr>
|
||||
</set>
|
||||
<set>
|
||||
<nr value="66"/>
|
||||
<nr value="123"/>
|
||||
<nr value="55"/>
|
||||
<nr value="9999"/>
|
||||
</set>
|
||||
</numbers>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<foo>
|
||||
<?cheese is tasty?>
|
||||
<bar>
|
||||
<baz/>
|
||||
<cheese/>
|
||||
<baz/>
|
||||
<?toast is tasty?>
|
||||
<cheese/>
|
||||
<baz/>
|
||||
</bar>
|
||||
<?cheese is gooey?>
|
||||
</foo>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<a>
|
||||
<b>foo</b>
|
||||
<?toc order-by="x"?>
|
||||
<c>bar</c>
|
||||
</a>
|
|
@ -1 +0,0 @@
|
|||
<Project id="17" Name="dave test" Deprecated="false"><Creator User="etools" Date="10/24/00 10:54 AM"></Creator><LastModifier User="Default" Date="Fri Jun 22 14:01:54 PDT 2001"></LastModifier><Description>tool testing</Description><Purpose> </Purpose><Datasets><link name="Test data 1" idref="18"></link><link name="veg plot data" idref="21"></link></Datasets></Project>
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<root><a>a</a><b>b</b><c><d>d</d></c></root>
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<g>
|
||||
<f a="é" />
|
||||
</g>
|
Двоичные данные
test/rexml/data/t63-1.xml
Двоичные данные
test/rexml/data/t63-1.xml
Двоичный файл не отображается.
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,31 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<!-- generated by hnb 1.9.17 (http://hnb.sourceforge.net) -->
|
||||
|
||||
<!DOCTYPE tree[
|
||||
<!ELEMENT tree (node*)>
|
||||
<!ELEMENT data (#PCDATA)> <!-- (max 4096 bytes long) -->
|
||||
<!ELEMENT node (data?,node*)>
|
||||
<!ATTLIST node done (yes|no) #IMPLIED
|
||||
type CDATA #IMPLIED
|
||||
>]>
|
||||
|
||||
<tree>
|
||||
<node done="no" type="todo"><data>Next_Actions</data>
|
||||
<node done="no" type="todo"><data>@class</data></node>
|
||||
<node done="no" type="todo"><data>@agenda</data>
|
||||
<node done="yes" type="todo"><data>this is something I'd like to do</data></node>
|
||||
</node>
|
||||
<node done="no" type="todo"><data>@dorm</data>
|
||||
<node done="no" type="todo"><data>clean room</data></node>
|
||||
</node>
|
||||
<node done="no" type="todo"><data>@computer</data>
|
||||
<node done="no" type="todo"><data>Write general makefile for cs projects</data></node>
|
||||
<node done="no" type="todo"><data>Set up bash podder</data></node>
|
||||
</node>
|
||||
<node done="no" type="todo"><data>@errands</data>
|
||||
<node done="no" type="todo"><data>Purchase geo lab book</data></node>
|
||||
</node>
|
||||
<node done="no" type="todo"><data>@dublin</data></node>
|
||||
</node>
|
||||
<node><data>projects</data></node>
|
||||
</tree>
|
|
@ -1,683 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tests xmlns:var="http://jaxen.org/test-harness/var">
|
||||
<!-- test for jaxen-24 -->
|
||||
<document url="xml/jaxen24.xml">
|
||||
<context select="/body/div">
|
||||
<test select="preceding::*[1]" count="1"/>
|
||||
<valueOf select="local-name(preceding::*[1])">span</valueOf>
|
||||
</context>
|
||||
<!-- jaxen-58 -->
|
||||
<context select="/">
|
||||
<test select="//preceding::x" count="0"/>
|
||||
<test select="//following::x" count="0"/>
|
||||
<test select="/descendant::*/preceding::x" count="0"/>
|
||||
<test select="/descendant::node()/preceding::x" count="0"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<!-- test for jaxen-3 -->
|
||||
<document url="xml/simple.xml">
|
||||
<context select="/">
|
||||
<valueOf select="string()">abd</valueOf>
|
||||
</context>
|
||||
<context select="/root">
|
||||
<valueOf select="string()">abd</valueOf>
|
||||
</context>
|
||||
<context select="/root/a">
|
||||
<valueOf select="string()">a</valueOf>
|
||||
</context>
|
||||
<context select="/root/c">
|
||||
<valueOf select="string()">d</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
|
||||
<!-- test for jaxen-3 -->
|
||||
<document url="xml/jaxen3.xml">
|
||||
<context select="/">
|
||||
<test select="/Configuration/hostname/attrlist/hostname[. = 'CE-A'] " count="1"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<!-- parser test cases all of which should fail-->
|
||||
<document url="xml/numbers.xml">
|
||||
<context select="/">
|
||||
<!-- repeated xpaths, jaxen-35 -->
|
||||
<test exception="true" select="/numbers numbers" count="0"/>
|
||||
<!-- invalid xpath, jaxen-34 -->
|
||||
<test exception="true" select="/a/b[c > d]efg" count="0"/>
|
||||
<!-- invalid xpath, jaxen-27 -->
|
||||
<test exception="true" select="/inv/child::" count="0"/>
|
||||
<!-- invalid xpath, jaxen-26 -->
|
||||
<!--
|
||||
|
||||
<test exception="true" select="/invoice/@test[abcd" count="0"/>
|
||||
<test exception="true" select="/invoice/@test[abcd > x" count="0"/>
|
||||
|
||||
<test exception="true" select="string-length('a" count="0"/>
|
||||
<test exception="true" select="/descendant::()" count="0"/>
|
||||
<test exception="true" select="(1 + 1" count="0"/>
|
||||
|
||||
-->
|
||||
</context>
|
||||
</document>
|
||||
|
||||
|
||||
<!-- test cases for the use of underscores in names -->
|
||||
<document url="xml/underscore.xml">
|
||||
<context select="/">
|
||||
<test select="/root/@a" count="1"/>
|
||||
<test select="/root/@_a" count="1"/>
|
||||
<test select="/root/b" count="1"/>
|
||||
<test select="/root/_b" count="1"/>
|
||||
<valueOf select="/root/@a">1</valueOf>
|
||||
<valueOf select="/root/@_a">2</valueOf>
|
||||
<valueOf select="/root/b">1</valueOf>
|
||||
<valueOf select="/root/_b">2</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<!-- test cases for the use of = with nodesets -->
|
||||
<document url="xml/web.xml">
|
||||
<context select="/">
|
||||
<valueOf select="/web-app/servlet/servlet-name = 'file'">true</valueOf>
|
||||
<valueOf select="/web-app/servlet/servlet-name = 'snoop'">true</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<document url="xml/numbers.xml">
|
||||
<context select="/">
|
||||
<valueOf select="/numbers/set/nr = '-3'">true</valueOf>
|
||||
<valueOf select="/numbers/set/nr = -3">true</valueOf>
|
||||
<valueOf select="/numbers/set/nr = 24">true</valueOf>
|
||||
<valueOf select="/numbers/set/nr/@value = '9999'">true</valueOf>
|
||||
<valueOf select="/numbers/set/nr/@value = 9999.0">true</valueOf>
|
||||
<valueOf select="/numbers/set/nr/@value = 66">true</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<!-- test basic math... -->
|
||||
<document url="xml/numbers.xml">
|
||||
<context select="/">
|
||||
<valueOf select="(8 * 2 + 1) = 17">true</valueOf>
|
||||
<valueOf select="(1 + 8 * 2) = 17">true</valueOf>
|
||||
<valueOf select="(7 - 3 + 1) = 5">true</valueOf>
|
||||
<valueOf select="(8 - 4 + 5 - 6) = 3">true</valueOf>
|
||||
<!-- left-assoc tests, comments show WRONG evaluation -->
|
||||
<!-- 3 - 2 - 1 != 2 -->
|
||||
<valueOf select="3 - 2 - 1">0</valueOf>
|
||||
<!-- 8 div 4 div 2 != 4 -->
|
||||
<valueOf select="8 div 4 div 2">1</valueOf>
|
||||
<!-- 3 mod 5 mod 7 != 1 -->
|
||||
<valueOf select="3 mod 7 mod 5">3</valueOf>
|
||||
<!-- 1=(2=2) is true-->
|
||||
<valueOf select="1 = 2 = 2">false</valueOf>
|
||||
<!-- 2!=(3!=1) => 2!=1 => true, (2!=3)!=1 => 1!=1 => false -->
|
||||
<valueOf select="2 != 3 != 1">false</valueOf>
|
||||
<!-- 3 > (2 > 1) is true -->
|
||||
<valueOf select="3 > 2 > 1">false</valueOf>
|
||||
<!-- 3 >= (2 >= 2) is true -->
|
||||
<valueOf select="3 >= 2 >= 2">false</valueOf>
|
||||
<!-- 1 < (2 < 3) is false -->
|
||||
<valueOf select="1 < 2 < 3">true</valueOf>
|
||||
<!-- 0 <= (2 <= 3) is true -->
|
||||
<valueOf select="2 <= 2 <= 3">true</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<!-- test cases for preceding axis with different node types -->
|
||||
<document url="xml/pi2.xml">
|
||||
<context select="/a/c">
|
||||
<test select="//processing-instruction()" count="1"/>
|
||||
<test select="preceding-sibling::*" count="1"/>
|
||||
<test select="preceding-sibling::node()" count="5"/>
|
||||
<test select="preceding-sibling::*[1]" count="1"/>
|
||||
<test select="preceding-sibling::processing-instruction()" count="1"/>
|
||||
<valueOf select="preceding-sibling::processing-instruction()">order-by="x"</valueOf>
|
||||
<valueOf select="preceding-sibling::*[1]">foo</valueOf>
|
||||
<valueOf select="preceding-sibling::node()[2]">order-by="x"</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<document url="xml/id.xml">
|
||||
<context select="/"
|
||||
var:foobar="foobar"
|
||||
var:foo="foo">
|
||||
<valueOf select="$foobar">foobar</valueOf>
|
||||
<test select="/foo[@id=$foobar]" count="1"/>
|
||||
<test select="/foo[@id='$foobar']" count="0"/>
|
||||
<test select="/foo[concat($foo, 'bar')=@id]" count="1"/>
|
||||
<test select="CD_Library/artist[@name=$artist]" count="0"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<document url="xml/id.xml">
|
||||
<context select="/">
|
||||
<!-- attributes have a parent: their element -->
|
||||
<test select="/foo/@id/parent::foo" count="1"/>
|
||||
</context>
|
||||
<!-- attributes can also be used as context nodes -->
|
||||
<context select="/foo/@id">
|
||||
<test select="parent::foo" count="1"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<document url="xml/pi.xml">
|
||||
<context select="/">
|
||||
<test select="//processing-instruction()" count="3"/>
|
||||
<test select="//processing-instruction('cheese')" count="2"/>
|
||||
<test select="//processing-instruction('toast')" count="1">
|
||||
<valueOf select="string()">is tasty</valueOf>
|
||||
</test>
|
||||
</context>
|
||||
|
||||
</document>
|
||||
|
||||
<!-- test evaluate() extension function -->
|
||||
<document url="xml/evaluate.xml">
|
||||
<context select="/">
|
||||
<test select="evaluate('//jumps/*')" count="3"/>
|
||||
<test select="evaluate('//jumps/object/dog')" count="1"/>
|
||||
<test select="evaluate('//jumps/object')/evaluate" count="0"/>
|
||||
<test select="evaluate('//jumps/object')/dog" count="1"/>
|
||||
<test select="evaluate('//jumps/*')/dog" count="1"/>
|
||||
<test select="//metatest[ evaluate(@select) = . ]" count="1"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<document url="xml/numbers.xml">
|
||||
<context select="/numbers/set[1]">
|
||||
<test select="*[-3 = .]" count="1"/>
|
||||
<valueOf select="54 < *">true</valueOf>
|
||||
<valueOf select="55 <= *">true</valueOf>
|
||||
<valueOf select="69 < *">false</valueOf>
|
||||
<valueOf select="-2 > *">true</valueOf>
|
||||
<valueOf select="-3 >= *">true</valueOf>
|
||||
<valueOf select="-4 >= *">false</valueOf>
|
||||
</context>
|
||||
<!-- TODO
|
||||
This context should work, but needs a fixed version of saxpath to parse the right-hand side
|
||||
of the greater-than expression.
|
||||
<context select="/numbers/set[2]">
|
||||
<valueOf select="1 > nr/@value">false</valueOf>
|
||||
<valueOf select="55 > nr/@value">false</valueOf>
|
||||
<valueOf select="55 >= nr/@value">true</valueOf>
|
||||
<valueOf select="1000000 > nr/@value">true</valueOf>
|
||||
</context>
|
||||
-->
|
||||
</document>
|
||||
|
||||
|
||||
<!-- test sibling axes -->
|
||||
<document url="xml/axis.xml">
|
||||
|
||||
<context select="/root">
|
||||
<test select="preceding-sibling::*" count="0"/>
|
||||
</context>
|
||||
|
||||
<context select="/root/a/a.3">
|
||||
<test select="preceding::*" count="2"/>
|
||||
</context>
|
||||
|
||||
<context select="/root/a/a.3">
|
||||
<test select="preceding-sibling::*" count="2"/>
|
||||
</context>
|
||||
|
||||
<context select="/">
|
||||
<valueOf select="name(/root/a/a.3/preceding-sibling::*[1])">a.2</valueOf>
|
||||
<valueOf select="name(/root/a/a.3/preceding-sibling::*[2])">a.1</valueOf>
|
||||
</context>
|
||||
|
||||
<context select="/">
|
||||
<valueOf select="name(/root/a/a.3/following-sibling::*[1])">a.4</valueOf>
|
||||
<valueOf select="name(/root/a/a.3/following-sibling::*[2])">a.5</valueOf>
|
||||
</context>
|
||||
|
||||
</document>
|
||||
|
||||
|
||||
<document url="xml/web.xml">
|
||||
|
||||
<context select="/">
|
||||
<valueOf select="/web-app/servlet[1]/servlet-name">snoop</valueOf>
|
||||
<valueOf select="/web-app/servlet[1]/servlet-name/text()">snoop</valueOf>
|
||||
<valueOf select="/web-app/servlet[2]/servlet-name">file</valueOf>
|
||||
<valueOf select="/web-app/servlet[2]/servlet-name/text()">file</valueOf>
|
||||
</context>
|
||||
|
||||
<context select="/web-app/servlet[1]">
|
||||
<valueOf select="servlet-name">snoop</valueOf>
|
||||
<valueOf select="servlet-name/text()">snoop</valueOf>
|
||||
</context>
|
||||
|
||||
<context select="/web-app/servlet[2]/servlet-name">
|
||||
<test select="preceding::*" count="3"/>
|
||||
</context>
|
||||
|
||||
<context select="/web-app/servlet[2]/servlet-name">
|
||||
<test select="following::*" count="13"/>
|
||||
</context>
|
||||
|
||||
</document>
|
||||
|
||||
|
||||
<!-- test name -->
|
||||
|
||||
<document url="xml/web.xml">
|
||||
<context select="/">
|
||||
|
||||
<test select="*" count="1">
|
||||
<valueOf select="name()">web-app</valueOf>
|
||||
</test>
|
||||
|
||||
<!-- NOTE that the child::node() tests only work if the
|
||||
XML document does not comments or PIs
|
||||
-->
|
||||
|
||||
<test select="./*" count="1">
|
||||
<valueOf select="name()">web-app</valueOf>
|
||||
</test>
|
||||
<test select="child::*" count="1">
|
||||
<valueOf select="name()">web-app</valueOf>
|
||||
</test>
|
||||
<test select="/*" count="1">
|
||||
<valueOf select="name()">web-app</valueOf>
|
||||
</test>
|
||||
<test select="/child::node()" count="1">
|
||||
<valueOf select="name(.)">web-app</valueOf>
|
||||
</test>
|
||||
<test select="child::node()" count="1">
|
||||
<valueOf select="name(.)">web-app</valueOf>
|
||||
</test>
|
||||
|
||||
<!-- empty names -->
|
||||
|
||||
<valueOf select="name()"></valueOf>
|
||||
<valueOf select="name(.)"></valueOf>
|
||||
<valueOf select="name(parent::*)"></valueOf>
|
||||
<valueOf select="name(/)"></valueOf>
|
||||
<valueOf select="name(/.)"></valueOf>
|
||||
<valueOf select="name(/self::node())"></valueOf>
|
||||
|
||||
<!-- name of root elemet -->
|
||||
<valueOf select="name(node())">web-app</valueOf>
|
||||
<valueOf select="name(/node())">web-app</valueOf>
|
||||
<valueOf select="name(/*)">web-app</valueOf>
|
||||
<valueOf select="name(/child::*)">web-app</valueOf>
|
||||
<valueOf select="name(/child::node())">web-app</valueOf>
|
||||
<valueOf select="name(/child::node())">web-app</valueOf>
|
||||
<valueOf select="name(child::node())">web-app</valueOf>
|
||||
<valueOf select="name(./*)">web-app</valueOf>
|
||||
<valueOf select="name(*)">web-app</valueOf>
|
||||
|
||||
</context>
|
||||
|
||||
<context select="/*">
|
||||
<!-- empty names -->
|
||||
<valueOf select="name(..)"></valueOf>
|
||||
<valueOf select="name(parent::node())"></valueOf>
|
||||
<valueOf select="name(parent::*)"></valueOf>
|
||||
|
||||
<!-- name of root elemet -->
|
||||
<valueOf select="name()">web-app</valueOf>
|
||||
<valueOf select="name(.)">web-app</valueOf>
|
||||
<valueOf select="name(../*)">web-app</valueOf>
|
||||
<valueOf select="name(../child::node())">web-app</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
|
||||
|
||||
<!-- test predicates -->
|
||||
|
||||
<document url="xml/nitf.xml">
|
||||
<context select="/nitf/head/docdata">
|
||||
<test select="doc-id[@regsrc='AP' and @id-string='D76UIMO80']" count="1"/>
|
||||
</context>
|
||||
<context select="/nitf/head">
|
||||
<test select="meta[@name='ap-cycle']" count="1"/>
|
||||
<test select="meta[@content='AP']" count="1"/>
|
||||
<test select="meta[@name and @content]" count="8"/>
|
||||
<test select="meta[@name='ap-cycle' and @content='AP']" count="1"/>
|
||||
<test select="meta[@name != 'ap-cycle']" count="7"/>
|
||||
</context>
|
||||
<context select="/">
|
||||
<test select="/nitf/head/meta[@name='ap-cycle']" count="1"/>
|
||||
<test select="/nitf/head/meta[@content='AP']" count="1"/>
|
||||
<test select="/nitf/head/meta[@name and @content]" count="8"/>
|
||||
<test select="/nitf/head/meta[@name='ap-cycle' and @content='AP']" count="1"/>
|
||||
<test select="/nitf/head/meta[@name != 'ap-cycle']" count="7"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
|
||||
<document url="xml/moreover.xml">
|
||||
<context select="/">
|
||||
<test select="/child::node()" count="1"/>
|
||||
<test select="/*" count="1"/>
|
||||
|
||||
<test select="/*/article" count="20"/>
|
||||
<test select="//*" count="221"/>
|
||||
<test select="//*[local-name()='article']" count="20"/>
|
||||
<test select="//article" count="20"/>
|
||||
<test select="/*/*[@code]" count="20"/>
|
||||
|
||||
<test select="/moreovernews/article[@code='13563275']" count="1"/>
|
||||
|
||||
<test select="/moreovernews/article[@code='13563275']">
|
||||
<valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
|
||||
</test>
|
||||
<test select="/*/article[@code='13563275']">
|
||||
<valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
|
||||
</test>
|
||||
<test select="//article[@code='13563275']">
|
||||
<valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
|
||||
</test>
|
||||
<test select="//*[@code='13563275']">
|
||||
<valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
|
||||
</test>
|
||||
<test select="/child::node()/child::node()[@code='13563275']">
|
||||
<valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
|
||||
</test>
|
||||
<test select="/*/*[@code='13563275']">
|
||||
<valueOf select="url">http://c.moreover.com/click/here.pl?x13563273</valueOf>
|
||||
</test>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
|
||||
|
||||
<!-- test other node types-->
|
||||
|
||||
<document url="xml/contents.xml">
|
||||
<context select="/">
|
||||
<test select="processing-instruction()" count="3"/>
|
||||
<test select="/processing-instruction()" count="3"/>
|
||||
<test select="/comment()" count="1"/>
|
||||
<test select="comment()" count="1"/>
|
||||
<test select="/child::node()/comment()" count="2"/>
|
||||
<test select="/*/comment()" count="2"/>
|
||||
<test select="//comment()" count="3"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
|
||||
|
||||
<!-- test positioning -->
|
||||
|
||||
<document url="xml/fibo.xml">
|
||||
<context select="/">
|
||||
<test select="/*/fibonacci[position() < 10]" count="9"/>
|
||||
<valueOf select="sum(//fibonacci)">196417</valueOf>
|
||||
<valueOf select="sum(//fibonacci/@index)">325</valueOf>
|
||||
<valueOf select="/*/fibonacci[2]">1</valueOf>
|
||||
<valueOf select="/*/fibonacci[ count(/*/fibonacci) ]">75025</valueOf>
|
||||
<valueOf select="/*/fibonacci[ count(/*/fibonacci) - 1 ]">46368</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
|
||||
<!-- test number functions -->
|
||||
|
||||
<!-- test Axes -->
|
||||
|
||||
<document url="xml/web.xml">
|
||||
<context select="/">
|
||||
<test select="descendant-or-self::*" count="19"/>
|
||||
<test select="descendant::*" count="19"/>
|
||||
<test select="/descendant::*" count="19"/>
|
||||
<test select="/descendant-or-self::*" count="19"/>
|
||||
<test select="/descendant::servlet" count="2"/>
|
||||
<test select="/descendant-or-self::servlet" count="2"/>
|
||||
<test select="descendant-or-self::servlet" count="2"/>
|
||||
<test select="descendant::servlet" count="2"/>
|
||||
<test select="/*/servlet" count="2"/>
|
||||
<valueOf select="count(/*/servlet)">2</valueOf>
|
||||
<test select="//servlet" count="2"/>
|
||||
<valueOf select="count(//servlet)">2</valueOf>
|
||||
</context>
|
||||
<context select="/web-app">
|
||||
<test select="/descendant::servlet" count="2"/>
|
||||
<test select="/descendant-or-self::servlet" count="2"/>
|
||||
<test select="descendant-or-self::servlet" count="2"/>
|
||||
<test select="descendant::servlet" count="2"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<document url="xml/much_ado.xml">
|
||||
<context select="/">
|
||||
<test select="/descendant::ACT" count="5"/>
|
||||
<test select="descendant::ACT" count="5"/>
|
||||
<valueOf select="/PLAY/TITLE">Much Ado about Nothing</valueOf>
|
||||
<valueOf select="2+2">4</valueOf>
|
||||
<valueOf select="5 * 4 + 1">21</valueOf>
|
||||
<valueOf select="count(descendant::ACT)">5</valueOf>
|
||||
<valueOf select="10 + count(descendant::ACT) * 5">35</valueOf>
|
||||
<valueOf select="(10 + count(descendant::ACT)) * 5">75</valueOf>
|
||||
</context>
|
||||
<context select="/PLAY/ACT[2]/SCENE[1]">
|
||||
<test select="/descendant::ACT" count="5"/>
|
||||
<test select="../../descendant::ACT" count="5"/>
|
||||
<test select="/PLAY/ACT[2]/SCENE[1]/descendant::SPEAKER" count="141"/>
|
||||
<test select="descendant::SPEAKER" count="141"/>
|
||||
<valueOf select="count(descendant::*)+1">646</valueOf>
|
||||
<valueOf select="count(descendant::SPEAKER)+1">142</valueOf>
|
||||
<valueOf select="count(ancestor::*)">2</valueOf>
|
||||
<valueOf select="count(ancestor::PLAY)">1</valueOf>
|
||||
<valueOf select="count(ancestor-or-self::*)">3</valueOf>
|
||||
<valueOf select="count(ancestor-or-self::PLAY)">1</valueOf>
|
||||
<valueOf select="5+count(ancestor::*)-1">6</valueOf>
|
||||
</context>
|
||||
<context select="/">
|
||||
<!-- Test correct predicate application -->
|
||||
<valueOf select="count(/PLAY/ACT/SCENE[1])">5</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<!-- test axis node ordering -->
|
||||
<document url="xml/web.xml">
|
||||
<context select="/">
|
||||
<!-- Reported as Jira issue JAXEN-24 -->
|
||||
<test select="//servlet-mapping/preceding::*[1][name()='description']" count="1"/>
|
||||
<test select="/web-app/servlet//description/following::*[1][name()='servlet-mapping']" count="1"/>
|
||||
<test select="/web-app/servlet//description/following::*[2][name()='servlet-name']" count="1"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<!-- test document function -->
|
||||
<document url="xml/text.xml">
|
||||
<context select="/">
|
||||
<test select="document('xml/web.xml')" count="1">
|
||||
<valueOf select="/web-app/servlet[1]/servlet-name">snoop</valueOf>
|
||||
<valueOf select="/web-app/servlet[1]/servlet-name/text()">snoop</valueOf>
|
||||
</test>
|
||||
<valueOf select="document('xml/web.xml')/web-app/servlet[1]/servlet-name">snoop</valueOf>
|
||||
</context>
|
||||
<!-- Test to check if the context changes when an extension function is used.
|
||||
First test is an example, second is the actual test.
|
||||
-->
|
||||
<context select="/foo/bar/cheese[1]">
|
||||
<valueOf select="concat(./@id,'foo',@id)">3foo3</valueOf>
|
||||
<valueOf select="concat(./@id,document('xml/web.xml')/web-app/servlet[1]/servlet-name,./@id)">3snoop3</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<document url="xml/message.xml">
|
||||
<context select="/">
|
||||
<valueOf select="/message/body/data/items/item[name/text()='parentinfo']/value">Pruefgebiete</valueOf>
|
||||
<valueOf select="document('xml/message.xml')/message/body/data/items/item[name/text()='parentinfo']/value">Pruefgebiete</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<document url="xml/simple.xml">
|
||||
|
||||
<!-- test behaviour of AbsoluteLocationPath -->
|
||||
<context select="/root/a">
|
||||
<valueOf select="concat( ., /root/b )">ab</valueOf>
|
||||
<valueOf select="concat( ../b, . )">ba</valueOf>
|
||||
<valueOf select="concat( /root/b, . )">ba</valueOf>
|
||||
<valueOf select="concat( /root/c/d, ../b )">db</valueOf>
|
||||
</context>
|
||||
|
||||
<!-- test the translate() function -->
|
||||
<context select="/">
|
||||
<valueOf select="translate( '', '', '' )"></valueOf>
|
||||
<valueOf select="translate( 'abcd', '', '' )">abcd</valueOf>
|
||||
<valueOf select="translate( 'abcd', 'abcd', 'abcd' )">abcd</valueOf>
|
||||
<valueOf select="translate( 'abcd', 'dcba', 'dcba' )">abcd</valueOf>
|
||||
<valueOf select="translate( 'abcd', 'abcd', 'dcba' )">dcba</valueOf>
|
||||
<valueOf select="translate( 'abcd', 'abcd', 'ab' )">ab</valueOf>
|
||||
<valueOf select="translate( 'abcd', 'cdab', 'cd' )">cd</valueOf>
|
||||
<valueOf select="translate( 'abcd', 'acbd', 'xy' )">xy</valueOf>
|
||||
<valueOf select="translate( 'abcd', 'abcdb', 'abcdb' )">abcd</valueOf>
|
||||
<valueOf select="translate( 'abcd', 'abcd', 'abcdb' )">abcd</valueOf>
|
||||
</context>
|
||||
|
||||
<context select="/">
|
||||
<valueOf select="substring('12345', 1.5, 2.6)">234</valueOf>
|
||||
<valueOf select="substring('12345', 0, 3)">12</valueOf>
|
||||
<valueOf select="substring('12345', 0 div 0, 3)"></valueOf>
|
||||
<valueOf select="substring('12345', 1, 0 div 0)"></valueOf>
|
||||
<valueOf select="substring('12345', -42, 1 div 0)">12345</valueOf>
|
||||
<valueOf select="substring('12345', -1 div 0, 1 div 0)"></valueOf>
|
||||
<valueOf select="substring('12345', 3)">345</valueOf>
|
||||
<valueOf select="substring('12345',1,15)">12345</valueOf>
|
||||
</context>
|
||||
|
||||
<!-- Some tests for the normalize-space() function -->
|
||||
|
||||
<context select="/">
|
||||
<valueOf select="normalize-space(' abc ')">abc</valueOf>
|
||||
<valueOf select="normalize-space(' a b c ')">a b c</valueOf>
|
||||
<valueOf select="normalize-space(' a 
 b 
 c ')">a b c</valueOf>
|
||||
<!-- Next test case addresses issue JAXEN-22 -->
|
||||
<valueOf select="normalize-space(' ')"></valueOf>
|
||||
<!-- Next test case addresses issue JAXEN-29 -->
|
||||
<valueOf select="normalize-space('')"></valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
|
||||
|
||||
<!-- test cases for String extension functions -->
|
||||
<document url="xml/web.xml">
|
||||
<context select="/web-app/servlet[1]">
|
||||
<valueOf select="upper-case( servlet-class )">SNOOPSERVLET</valueOf>
|
||||
<valueOf select="lower-case( servlet-class )">snoopservlet</valueOf>
|
||||
<valueOf select="upper-case( servlet-class, 'fr' )">SNOOPSERVLET</valueOf>
|
||||
<valueOf select="upper-case( servlet-class, 'fr-CA' )">SNOOPSERVLET</valueOf>
|
||||
<valueOf select="upper-case( servlet-class, 'es-ES-Traditional_WIN' )">SNOOPSERVLET</valueOf>
|
||||
<valueOf select="ends-with( servlet-class, 'Servlet' )">true</valueOf>
|
||||
<valueOf select="ends-with( servlet-class, 'S' )">false</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<!-- test cases for the lang() function -->
|
||||
<document url="xml/lang.xml">
|
||||
<context select="/">
|
||||
<test select="/e1/e2[lang('hr')]" count="0"/>
|
||||
<test select="/e1/e2/e3[lang('en')]" count="1"/>
|
||||
<test select="/e1/e2/e3[lang('en-US')]" count="1"/>
|
||||
<test select="/e1/e2/e3[lang('en-GB')]" count="0"/>
|
||||
<test select="/e1/e2/e3[lang('hu')]" count="2"/>
|
||||
<test select="/e1/e2/e3[lang('hu-HU')]" count="0"/>
|
||||
<test select="/e1/e2/e3[lang('es')]" count="1"/>
|
||||
<test select="/e1/e2/e3[lang('es-BR')]" count="0"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<!-- test namespace -->
|
||||
<document url="xml/namespaces.xml">
|
||||
<context select="/"
|
||||
xmlns:foo="http://fooNamespace/"
|
||||
xmlns:voo="http://fooNamespace/"
|
||||
xmlns:bar="http://barNamespace/"
|
||||
xmlns:alias="http://fooNamespace/">
|
||||
<test select="/*" count="1"/>
|
||||
<test select="/foo:a" count="1"/>
|
||||
<test select="/foo:a/b" count="1"/>
|
||||
<test select="/voo:a/b/c" count="1"/>
|
||||
<test select="/voo:a/bar:f" count="1"/>
|
||||
<test select="/*[namespace-uri()='http://fooNamespace/' and local-name()='a']" count="1"/>
|
||||
<test select="/*[local-name()='a' and namespace-uri()='http://fooNamespace/']/*[local-name()='x' and namespace-uri()='http://fooNamespace/']" count="1"/>
|
||||
<test select="/*[local-name()='a' and namespace-uri()='http://fooNamespace/']/*[local-name()='x' and namespace-uri()='http://fooNamespace/']/*[local-name()='y' and namespace-uri()='http://fooNamespace/']" count="1"/>
|
||||
</context>
|
||||
<!-- the prefix here and in the document have no relation; it's their
|
||||
namespace-uri binding that counts -->
|
||||
<context select="/" xmlns:foo="http://somethingElse/">
|
||||
<test select="/foo:a/b/c" count="0"/>
|
||||
</context>
|
||||
|
||||
<context select="/"
|
||||
xmlns:foo="http://fooNamespace/"
|
||||
xmlns:bar="http://barNamespace/"
|
||||
xmlns:alias="http://fooNamespace/">
|
||||
<valueOf select="/foo:a/b/c">Hello</valueOf>
|
||||
<valueOf select="/foo:a/foo:d/foo:e">Hey</valueOf>
|
||||
<valueOf select="/foo:a/alias:x/alias:y">Hey3</valueOf>
|
||||
<valueOf select="/foo:a/foo:x/foo:y">Hey3</valueOf>
|
||||
<valueOf select="/*[local-name()='a' and namespace-uri()='http://fooNamespace/']/*[local-name()='x' and namespace-uri()='http://fooNamespace/']/*[local-name()='y' and namespace-uri()='http://fooNamespace/']">Hey3</valueOf>
|
||||
</context>
|
||||
|
||||
</document>
|
||||
|
||||
<document url="xml/defaultNamespace.xml">
|
||||
<context select="/">
|
||||
<!-- NOTE: /a/b/c selects elements in no namespace only! -->
|
||||
<test select="/a/b/c" count="0"/>
|
||||
<!--
|
||||
The following test uses an unbound prefix 'x' and should throw an exception.
|
||||
Addresses issue JAXEN-18.
|
||||
Turns out this isn't really tested as the test didn't fail when the exception wasn't thrown.
|
||||
<test select="/x:a/x:b/x:c" count="0" exception="true"/>
|
||||
-->
|
||||
</context>
|
||||
<context select="/"
|
||||
xmlns:dummy="http://dummyNamespace/">
|
||||
<test select="/dummy:a/dummy:b/dummy:c" count="1"/>
|
||||
</context>
|
||||
</document>
|
||||
<document url="xml/text.xml">
|
||||
<context select="/">
|
||||
<test select="/foo/bar/text()" count="3"/>
|
||||
<valueOf select="normalize-space(/foo/bar/text())">baz</valueOf>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
<document url="xml/testNamespaces.xml">
|
||||
<context select="/">
|
||||
<!-- the root is not an element, so no namespaces -->
|
||||
<test select="namespace::*" count="0" debug="off"/>
|
||||
<test select="/namespace::*" count="0" debug="off"/>
|
||||
<test select="/Template/Application1/namespace::*" count="3" debug="off"/>
|
||||
<test select="/Template/Application2/namespace::*" count="3" debug="off"/>
|
||||
|
||||
<test select="//namespace::*" count="25" debug="off"/>
|
||||
</context>
|
||||
|
||||
<!--
|
||||
<context select="/Template/Application1">
|
||||
<test select="namespace::*" count="3" debug="off"/>
|
||||
<test select="/namespace::*" count="0" debug="off"/>
|
||||
<test select="/Template/Application1/namespace::*" count="3" debug="off"/>
|
||||
<test select="/Template/Application2/namespace::*" count="3" debug="off"/>
|
||||
<test select="//namespace::*" count="25" debug="off"/>
|
||||
<test select="//namespace::xplt" count="8" debug="off"/>
|
||||
<test xmlns:somethingelse="http://www.xxxx.com/"
|
||||
select="//namespace::somethingelse" count="0" debug="off"/>
|
||||
</context>
|
||||
-->
|
||||
</document>
|
||||
|
||||
<document url="xml/testNamespaces.xml">
|
||||
<context select="/">
|
||||
<!-- namespace nodes have their element as their parent -->
|
||||
<test select="/Template/namespace::xml/parent::Template" count="1"/>
|
||||
</context>
|
||||
<!-- namespace nodes can also be used as context nodes -->
|
||||
<context select="/Template/namespace::xml">
|
||||
<test select="parent::Template" count="1"/>
|
||||
</context>
|
||||
</document>
|
||||
|
||||
</tests>
|
|
@ -1,369 +0,0 @@
|
|||
<stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:var="http://jaxen.org/test-harness/var">
|
||||
<!-- this is what I used to generate XPathTestBase. After generating I fixed the illegal strings (its quicker
|
||||
than fixing the xsl for that few errors) and reformatted the code. Its unlikely this code will be needed
|
||||
again, its just in cvs for completeness -->
|
||||
<output method="text"/>
|
||||
<template match="/">
|
||||
<text>
|
||||
/*
|
||||
* $Header: /home/projects/jaxen/scm/jaxen/src/java/test/org/jaxen/XPathTestBase.java,v 1.32 2005/06/15 23:52:40 bewins Exp $
|
||||
* $Revision: 1.32 $
|
||||
* $Date: 2005/06/15 23:52:40 $
|
||||
*
|
||||
* ====================================================================
|
||||
*
|
||||
* Copyright (C) 2000-2002 bob mcwhirter & James Strachan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions, and the disclaimer that follows
|
||||
* these conditions in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* 3. The name "Jaxen" must not be used to endorse or promote products
|
||||
* derived from this software without prior written permission. For
|
||||
* written permission, please contact license@jaxen.org.
|
||||
*
|
||||
* 4. Products derived from this software may not be called "Jaxen", nor
|
||||
* may "Jaxen" appear in their name, without prior written permission
|
||||
* from the Jaxen Project Management (pm@jaxen.org).
|
||||
*
|
||||
* In addition, we request (but do not require) that you include in the
|
||||
* end-user documentation provided with the redistribution and/or in the
|
||||
* software itself an acknowledgement equivalent to the following:
|
||||
* "This product includes software developed by the
|
||||
* Jaxen Project (http://www.jaxen.org/)."
|
||||
* Alternatively, the acknowledgment may be graphical using the logos
|
||||
* available at http://www.jaxen.org/
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE Jaxen AUTHORS OR THE PROJECT
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* ====================================================================
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Jaxen Project and was originally
|
||||
* created by bob mcwhirter <bob@werken.com> and
|
||||
* James Strachan <jstrachan@apache.org>. For more information on the
|
||||
* Jaxen Project, please see <http://www.jaxen.org/>.
|
||||
*
|
||||
* $Id: XPathTestBase.java,v 1.32 2005/06/15 23:52:40 bewins Exp $
|
||||
*/
|
||||
|
||||
|
||||
package org.jaxen;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.jaxen.function.StringFunction;
|
||||
import org.jaxen.saxpath.helpers.XPathReaderFactory;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class XPathTestBase extends TestCase
|
||||
{
|
||||
protected static String VAR_URI = "http://jaxen.org/test-harness/var";
|
||||
protected static String TESTS_XML = "xml/test/tests.xml";
|
||||
|
||||
protected static boolean verbose = true;
|
||||
protected static boolean debug = true;
|
||||
private ContextSupport contextSupport;
|
||||
|
||||
public XPathTestBase(String name)
|
||||
{
|
||||
super( name );
|
||||
}
|
||||
|
||||
public void setUp() throws ParserConfigurationException
|
||||
{
|
||||
this.contextSupport = null;
|
||||
System.setProperty( XPathReaderFactory.DRIVER_PROPERTY,
|
||||
"" );
|
||||
log( "-----------------------------" );
|
||||
}
|
||||
|
||||
public void log(String text)
|
||||
{
|
||||
log( verbose,
|
||||
text );
|
||||
}
|
||||
|
||||
public void log(boolean actualVerbose,
|
||||
String text)
|
||||
{
|
||||
if ( ! actualVerbose )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println( text );
|
||||
}
|
||||
|
||||
protected void assertCountXPath(int expectedSize, Object context, String xpathStr) throws JaxenException {
|
||||
try
|
||||
{
|
||||
assertCountXPath2(expectedSize, context, xpathStr);
|
||||
}
|
||||
catch (UnsupportedAxisException e)
|
||||
{
|
||||
log ( debug,
|
||||
" ## SKIPPED -- Unsupported Axis" );
|
||||
}
|
||||
}
|
||||
|
||||
protected Object assertCountXPath2(int expectedSize, Object context, String xpathStr) throws JaxenException {
|
||||
log ( debug,
|
||||
" Select :: " + xpathStr );
|
||||
BaseXPath xpath = new BaseXPath( xpathStr );
|
||||
List results = xpath.selectNodes( getContext( context ) );
|
||||
log ( debug,
|
||||
" Expected Size :: " + expectedSize );
|
||||
log ( debug,
|
||||
" Result Size :: " + results.size() );
|
||||
|
||||
if ( expectedSize != results.size() )
|
||||
{
|
||||
log ( debug,
|
||||
" ## FAILED" );
|
||||
log ( debug,
|
||||
" ## xpath: " + xpath + " = " + xpath.debug() );
|
||||
|
||||
Iterator resultIter = results.iterator();
|
||||
|
||||
while ( resultIter.hasNext() )
|
||||
{
|
||||
log ( debug,
|
||||
" --> " + resultIter.next() );
|
||||
}
|
||||
}
|
||||
assertEquals( xpathStr,
|
||||
expectedSize,
|
||||
results.size() );
|
||||
if (expectedSize > 0) {
|
||||
return results.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void assertInvalidXPath(Object context, String xpathStr) throws JaxenException {
|
||||
try
|
||||
{
|
||||
log ( debug,
|
||||
" Select :: " + xpathStr );
|
||||
BaseXPath xpath = new BaseXPath( xpathStr );
|
||||
List results = xpath.selectNodes( getContext( context ) );
|
||||
log ( debug,
|
||||
" Result Size :: " + results.size() );
|
||||
fail("An exception was expected.");
|
||||
}
|
||||
catch (UnsupportedAxisException e)
|
||||
{
|
||||
log ( debug,
|
||||
" ## SKIPPED -- Unsupported Axis" );
|
||||
}
|
||||
catch (JaxenException e) {
|
||||
log (debug, " Caught expected exception "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertValueOfXPath(String expected, Object context, String xpathStr) throws JaxenException {
|
||||
try
|
||||
{
|
||||
BaseXPath xpath = new BaseXPath( xpathStr );
|
||||
Object node = xpath.evaluate( getContext( context ) );
|
||||
|
||||
String result = StringFunction.evaluate( node,
|
||||
getNavigator() );
|
||||
|
||||
log ( debug,
|
||||
" Select :: " + xpathStr );
|
||||
log ( debug,
|
||||
" Expected :: " + expected );
|
||||
log ( debug,
|
||||
" Result :: " + result );
|
||||
|
||||
if ( ! expected.equals( result ) )
|
||||
{
|
||||
log ( debug,
|
||||
" ## FAILED" );
|
||||
log ( debug,
|
||||
" ## xpath: " + xpath + " = " + xpath.debug() );
|
||||
}
|
||||
|
||||
assertEquals( xpathStr,
|
||||
expected,
|
||||
result );
|
||||
}
|
||||
catch (UnsupportedAxisException e)
|
||||
{
|
||||
log ( debug,
|
||||
" ## SKIPPED -- Unsupported Axis " );
|
||||
|
||||
}
|
||||
}
|
||||
protected Context getContext(Object contextNode)
|
||||
{
|
||||
Context context = new Context( getContextSupport() );
|
||||
|
||||
List list = new ArrayList( 1 );
|
||||
list.add( contextNode );
|
||||
context.setNodeSet( list );
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public ContextSupport getContextSupport()
|
||||
{
|
||||
if ( this.contextSupport == null )
|
||||
{
|
||||
this.contextSupport = new ContextSupport( new SimpleNamespaceContext(),
|
||||
XPathFunctionContext.getInstance(),
|
||||
new SimpleVariableContext(),
|
||||
getNavigator() );
|
||||
}
|
||||
|
||||
return this.contextSupport;
|
||||
}
|
||||
|
||||
public abstract Navigator getNavigator();
|
||||
|
||||
public abstract Object getDocument(String url) throws Exception;
|
||||
|
||||
public void testGetNodeType() throws FunctionCallException, UnsupportedAxisException
|
||||
{
|
||||
Navigator nav = getNavigator();
|
||||
Object document = nav.getDocument("xml/testNamespaces.xml");
|
||||
int count = 0;
|
||||
Iterator descendantOrSelfAxisIterator = nav.getDescendantOrSelfAxisIterator(document);
|
||||
while (descendantOrSelfAxisIterator.hasNext()) {
|
||||
Object node = descendantOrSelfAxisIterator.next();
|
||||
Iterator namespaceAxisIterator = nav.getNamespaceAxisIterator(node);
|
||||
while (namespaceAxisIterator.hasNext()) {
|
||||
count++;
|
||||
assertEquals("Node type mismatch", Pattern.NAMESPACE_NODE, nav.getNodeType(namespaceAxisIterator.next()));
|
||||
}
|
||||
}
|
||||
assertEquals(25, count);
|
||||
}
|
||||
|
||||
</text>
|
||||
<apply-templates select="node()|@*"/>
|
||||
<text>
|
||||
}
|
||||
</text>
|
||||
</template>
|
||||
<template match="context">
|
||||
<text>
|
||||
public void test</text><value-of select="generate-id()"/><text>() throws JaxenException {
|
||||
Navigator nav = getNavigator();
|
||||
String url = "</text><value-of select="../@url"/><text>";
|
||||
log( "Document [" + url + "]" );
|
||||
Object document = nav.getDocument(url);
|
||||
|
||||
XPath contextpath = new BaseXPath("</text><value-of select="@select"/><text>", nav);
|
||||
log( "Initial Context :: " + contextpath );
|
||||
List list = contextpath.selectNodes(document);
|
||||
</text>
|
||||
<if test="count(namespace::*) > count(../namespace::*)">
|
||||
<text>
|
||||
SimpleNamespaceContext nsContext = new SimpleNamespaceContext();</text>
|
||||
<for-each select="namespace::*[local-name() != 'var' and local-name() != 'xml']">
|
||||
<text>
|
||||
nsContext.addNamespace( "</text><value-of select="local-name()"/><text>", "</text><value-of select="."/><text>" );</text>
|
||||
</for-each>
|
||||
<text>
|
||||
getContextSupport().setNamespaceContext( nsContext );</text>
|
||||
</if>
|
||||
<if test="@*[namespace-uri() = 'http://jaxen.org/test-harness/var']">
|
||||
<text>
|
||||
SimpleVariableContext varContext = new SimpleVariableContext();</text>
|
||||
<for-each select="@*[namespace-uri() = 'http://jaxen.org/test-harness/var']">
|
||||
<text>
|
||||
varContext.setVariableValue(null, "</text><value-of select="local-name()"/><text>", "</text><value-of select="."/><text>" );</text>
|
||||
</for-each>
|
||||
<text>
|
||||
getContextSupport().setVariableContext( varContext );</text>
|
||||
</if>
|
||||
<text>
|
||||
Iterator iter = list.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Object context = iter.next();</text>
|
||||
<apply-templates select="node()|@*"/>
|
||||
<text>
|
||||
}
|
||||
}</text>
|
||||
</template>
|
||||
<template match="test[@exception]">
|
||||
<text>
|
||||
assertInvalidXPath(context, "</text><value-of select='@select'/><text>");</text>
|
||||
</template>
|
||||
<template match="test[valueOf]">
|
||||
<choose>
|
||||
<when test="@count">
|
||||
<text>
|
||||
try
|
||||
{
|
||||
Object result = assertCountXPath2(</text><value-of select="@count"/><text>, context, "</text><value-of select="@select"/><text>");</text>
|
||||
<for-each select="valueOf">
|
||||
<text>
|
||||
assertValueOfXPath("</text><value-of select="."/><text>", result, "</text><value-of select="@select"/><text>");</text>
|
||||
</for-each>
|
||||
<text>
|
||||
}
|
||||
catch (UnsupportedAxisException e)
|
||||
{
|
||||
log ( debug, " ## SKIPPED -- Unsupported Axis" );
|
||||
}</text>
|
||||
</when>
|
||||
<otherwise>
|
||||
<text>
|
||||
try
|
||||
{
|
||||
BaseXPath xpath = new BaseXPath( "</text><value-of select="@select"/><text>" );
|
||||
List results = xpath.selectNodes( getContext( context ) );
|
||||
Object result = results.get(0);</text>
|
||||
<for-each select="valueOf">
|
||||
<text>
|
||||
assertValueOfXPath("</text><value-of select="."/><text>", result, "</text><value-of select="@select"/><text>");</text>
|
||||
</for-each>
|
||||
<text>
|
||||
}
|
||||
catch (UnsupportedAxisException e)
|
||||
{
|
||||
log ( debug, " ## SKIPPED -- Unsupported Axis" );
|
||||
}</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</template>
|
||||
<template match="test">
|
||||
<text>
|
||||
assertCountXPath(</text><value-of select="@count"/><text>, context, "</text><value-of select="@select"/><text>");</text>
|
||||
</template>
|
||||
<template match="valueOf">
|
||||
<text>
|
||||
assertValueOfXPath("</text><value-of select="."/>", context, "<value-of select="@select"/><text>");</text>
|
||||
</template>
|
||||
<template match="comment()"><text>
|
||||
/*</text><value-of select="."/><text>
|
||||
*/</text>
|
||||
</template>
|
||||
<template match="node()|@*"><apply-templates select="node()|@*"/></template>
|
||||
</stylesheet>
|
|
@ -1,22 +0,0 @@
|
|||
<Template>
|
||||
<Application1 xmlns:xplt="http://www.xxxx.com/"
|
||||
xmlns:xpl="http://www.xxxx.com/"
|
||||
version="3.0"
|
||||
randomAttribute="foo"
|
||||
>
|
||||
<xpl:insertText/>
|
||||
<xplt:anyElement>
|
||||
<Name/>
|
||||
</xplt:anyElement>
|
||||
</Application1>
|
||||
|
||||
<Application2 xmlns:xplt="http://www.xxxx.com/"
|
||||
xmlns:xpl="http://www.xxxx.com/"
|
||||
version="3.0"
|
||||
>
|
||||
<xpl:insertText/>
|
||||
<xplt:anyElement>
|
||||
<Name/>
|
||||
</xplt:anyElement>
|
||||
</Application2>
|
||||
</Template>
|
|
@ -1,64 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
|
||||
"http://my.netscape.com/publish/formats/rss-0.91.dtd">
|
||||
<rss version="0.91">
|
||||
<channel>
|
||||
<title>xmlhack</title>
|
||||
<link>http://www.xmlhack.com</link>
|
||||
<description>Developer news from the XML community</description>
|
||||
<language>en-us</language>
|
||||
<copyright>Copyright 1999-2001, xmlhack team.</copyright>
|
||||
<managingEditor>editor@xmlhack.com</managingEditor>
|
||||
<webMaster>webmaster@xmlhack.com</webMaster>
|
||||
<image>
|
||||
<title>xmlhack</title>
|
||||
<url>http://www.xmlhack.com/images/mynetscape88.gif</url>
|
||||
<link>http://www.xmlhack.com</link>
|
||||
<width>88</width>
|
||||
<height>31</height>
|
||||
<description>News, opinions, tips and issues concerning XML development</description>
|
||||
</image>
|
||||
<item x='0'>
|
||||
<title>Experimental non-XML syntax for RELAX NG</title>
|
||||
<link>http://www.xmlhack.com/read.php?item=1343</link>
|
||||
<description>
|
||||
James Clark has announced the release of an experimental non-XML syntax for RELAX
|
||||
NG and a Java translator implementation that converts
|
||||
instances of the syntax into RELAX NG's XML syntax.
|
||||
</description>
|
||||
<category>Schemas</category>
|
||||
</item>
|
||||
<item x='1'>
|
||||
<title>Long-awaited entity-resolver Java classes finally released</title>
|
||||
<link>http://www.xmlhack.com/read.php?item=1342</link>
|
||||
<description>Norman Walsh has
|
||||
announced the release of SAX entityResolver() and JAXP
|
||||
URIResolver() Java
|
||||
classes he wrote to implement the OASIS XML Catalogs
|
||||
Committee Specification (in addition to the TR9401 and
|
||||
Apache XCatalogs specifications).
|
||||
</description>
|
||||
<category>SGML/XML</category>
|
||||
<category>Java</category>
|
||||
</item>
|
||||
<item x='3'>
|
||||
|
||||
<title>Beepcore-C framework released</title>
|
||||
<link>http://www.xmlhack.com/read.php?item=1341</link>
|
||||
<description>Invisible Worlds have announced the publication of Beepcore-C, an implementation of the BEEP framework written in C.</description>
|
||||
<category>Protocols</category>
|
||||
<category>C++</category>
|
||||
</item>
|
||||
<item>
|
||||
<title>SVG and XSL-FO by example</title>
|
||||
<link>http://www.xmlhack.com/read.php?item=1340</link>
|
||||
<description>Jirka Jirat has announced the
|
||||
addition of an XSL-FO and SVG examples repository to the Zvon developer
|
||||
reference site.</description>
|
||||
<category>SVG</category>
|
||||
<category>XSL-FO</category>
|
||||
</item>
|
||||
|
||||
|
||||
</channel>
|
||||
</rss>
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<foo>
|
||||
<bar>
|
||||
baz
|
||||
<cheese id="3"/>
|
||||
baz
|
||||
<cheese/>
|
||||
baz
|
||||
</bar>
|
||||
</foo>
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,590 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Rev 1.05
|
||||
Changed the <gram> element name to <pos>
|
||||
Added the g_gend attribute
|
||||
moved the s_inf element
|
||||
-->
|
||||
<!-- Rev 1.04
|
||||
Changes:
|
||||
Rename the project "JMdict" and add the g_lang attribute to the
|
||||
<gloss> entity - 08 May 1999
|
||||
Moved the <gram>, <field> and <misc> elements down to be in the
|
||||
<sense> region, as suggested by Chris Czeyka. I have also tidied up
|
||||
some of the "*" as he suggested. - 27 May 2000
|
||||
Added the re_nokanji element - Sep 2003.
|
||||
-->
|
||||
<!DOCTYPE JMdict [
|
||||
<!ELEMENT JMdict (entry*)>
|
||||
<!-- -->
|
||||
<!ELEMENT entry (ent_seq, k_ele*, r_ele+, info*, sense+)>
|
||||
<!-- Entries consist of kanji elements, reading elements,
|
||||
general information and sense elements. Each entry must have at
|
||||
least one reading element and one sense element. Others are optional.
|
||||
-->
|
||||
<!ELEMENT ent_seq (#PCDATA)>
|
||||
<!-- A unique numeric sequence number for each entry
|
||||
-->
|
||||
<!ELEMENT k_ele (keb, ke_inf*, ke_pri*)>
|
||||
<!-- The kanji element, or in its absence, the reading element, is
|
||||
the defining component of each entry.
|
||||
The overwhelming majority of entries will have a single kanji
|
||||
element associated with a word in Japanese. Where there are
|
||||
multiple kanji elements within an entry, they will be orthographical
|
||||
variants of the same word, either using variations in okurigana, or
|
||||
alternative and equivalent kanji. Common "mis-spellings" may be
|
||||
included, provided they are associated with appropriate information
|
||||
fields. Synonyms are not included; they may be indicated in the
|
||||
cross-reference field associated with the sense element.
|
||||
-->
|
||||
<!ELEMENT keb (#PCDATA)>
|
||||
<!-- This element will contain a word or short phrase in Japanese
|
||||
which is written using at least one kanji. The valid characters are
|
||||
kanji, kana, related characters such as chouon and kurikaeshi, and
|
||||
in exceptional cases, letters from other alphabets.
|
||||
-->
|
||||
<!ELEMENT ke_inf (#PCDATA)>
|
||||
<!-- This is a coded information field related specifically to the
|
||||
orthography of the keb, and will typically indicate some unusual
|
||||
aspect, such as okurigana irregularity.
|
||||
-->
|
||||
<!ELEMENT ke_pri (#PCDATA)>
|
||||
<!-- This and the equivalent re_pri field are provided to record
|
||||
information about the relative priority of the entry, and consist
|
||||
of codes indicating the word appears in various references which
|
||||
can be taken as an indication of the frequency with which the word
|
||||
is used. This field is intended for use either by applications which
|
||||
want to concentrate on entries of a particular priority, or to
|
||||
generate subset files.
|
||||
The current values in this field are:
|
||||
- news1/2: appears in the "wordfreq" file compiled by Alexandre Girardi
|
||||
from the Mainichi Shimbun. (See the Monash ftp archive for a copy.)
|
||||
Words in the first 12,000 in that file are marked "news1" and words
|
||||
in the second 12,000 are marked "news2".
|
||||
- ichi1/2: appears in the "Ichimango goi bunruishuu", Senmon Kyouiku
|
||||
Publishing, Tokyo, 1998.
|
||||
- spec1 and spec2: a small number of words use this marker when they
|
||||
are detected as being common, but are not included in other lists.
|
||||
- gai1: common loanwords, based on the wordfreq file.
|
||||
- nfxx: this is an indicator of frequency-of-use ranking in the
|
||||
wordfreq file. "xx" is the number of the set of 500 words in which
|
||||
the entry can be found, with "01" assigned to the first 500, "02"
|
||||
to the second, and so on.
|
||||
|
||||
The reason both the kanji and reading elements are tagged is because
|
||||
on occasions a priority is only associated with a particular
|
||||
kanji/reading pair.
|
||||
-->
|
||||
<!-- -->
|
||||
<!ELEMENT r_ele (reb, re_nokanji?, re_restr*, re_inf*, re_pri*)>
|
||||
<!-- The reading element typically contains the valid readings
|
||||
of the word(s) in the kanji element using modern kanadzukai.
|
||||
Where there are multiple reading elements, they will typically be
|
||||
alternative readings of the kanji element. In the absence of a
|
||||
kanji element, i.e. in the case of a word or phrase written
|
||||
entirely in kana, these elements will define the entry.
|
||||
-->
|
||||
<!ELEMENT reb (#PCDATA)>
|
||||
<!-- this element content is restricted to kana and related
|
||||
characters such as chouon and kurikaeshi. Kana usage will be
|
||||
consistent between the keb and reb elements; e.g. if the keb
|
||||
contains katakana, so too will the reb.
|
||||
-->
|
||||
<!ELEMENT re_nokanji (#PCDATA)>
|
||||
<!-- This element, which will usually have a null value, indicates
|
||||
that the reb, while associated with the keb, cannot be regarded
|
||||
as a true reading of the kanji. It is typically used for words
|
||||
such as foreign place names, gairaigo which can be in kanji or
|
||||
katakana, etc.
|
||||
-->
|
||||
<!ELEMENT re_restr (#PCDATA)>
|
||||
<!-- This element is used to indicate when the reading only applies
|
||||
to a subset of the keb elements in the entry. In its absence, all
|
||||
readings apply to all kanji elements. The contents of this element
|
||||
must exactly match those of one of the keb elements.
|
||||
-->
|
||||
<!ELEMENT re_inf (#PCDATA)>
|
||||
<!-- General coded information pertaining to the specific reading.
|
||||
Typically it will be used to indicate some unusual aspect of
|
||||
the reading. -->
|
||||
<!ELEMENT re_pri (#PCDATA)>
|
||||
<!-- See the comment on ke_pri above. -->
|
||||
<!-- -->
|
||||
<!ELEMENT info (lang*, dial*, links*, bibl*, etym*, audit*)>
|
||||
<!-- general coded information relating to the entry as a whole.-->
|
||||
<!ELEMENT lang (#PCDATA)>
|
||||
<!-- For loan-words, the ISO 639 two-letter code for the originating
|
||||
language. -->
|
||||
<!ELEMENT dial (#PCDATA)>
|
||||
<!-- For words specifically associated with regional dialects in
|
||||
Japanese, the entity code for that dialect, e.g. ksb for Kansaiben.
|
||||
-->
|
||||
<!ELEMENT bibl (bib_tag?, bib_txt?)>
|
||||
<!ELEMENT bib_tag (#PCDATA)>
|
||||
<!ELEMENT bib_txt (#PCDATA)>
|
||||
<!-- Bibliographic information about the entry. The bib_tag will a
|
||||
coded reference to an entry in an external bibliographic database.
|
||||
The bib_txt field may be used for brief (local) descriptions.-->
|
||||
<!ELEMENT etym (#PCDATA)>
|
||||
<!-- This field is used to hold information about the etymology
|
||||
of the entry. -->
|
||||
<!ELEMENT links (link_tag, link_desc, link_uri)>
|
||||
<!ELEMENT link_tag (#PCDATA)>
|
||||
<!ELEMENT link_desc (#PCDATA)>
|
||||
<!ELEMENT link_uri (#PCDATA)>
|
||||
<!-- This element holds details of linking information to
|
||||
entries in other electronic repositories. The link_tag will be
|
||||
coded to indicate the type of link (text, image, sound), the
|
||||
link_desc will provided a textual label for the link, and the
|
||||
link_uri contains the actual URI. -->
|
||||
<!ELEMENT audit (upd_date, upd_detl)>
|
||||
<!ELEMENT upd_date (#PCDATA)>
|
||||
<!ELEMENT upd_detl (#PCDATA)>
|
||||
<!-- The audit element will contain the date and other information
|
||||
about updates to the entry. Can be used to record the source of
|
||||
the material. -->
|
||||
<!-- -->
|
||||
<!ELEMENT sense (stagk*, stagr*, pos*, xref*, ant*, field*, misc*, s_inf*, gloss*, example*)>
|
||||
<!-- The sense element will record the translational equivalent
|
||||
of the Japanese word, plus other related information. Where there
|
||||
are several distinctly different meanings of the word, multiple
|
||||
sense elements will be employed.
|
||||
-->
|
||||
<!ELEMENT stagk (#PCDATA)>
|
||||
<!ELEMENT stagr (#PCDATA)>
|
||||
<!-- These elements, if present, indicate that the sense is restricted
|
||||
to the lexeme represented by the keb and/or reb. -->
|
||||
<!ELEMENT xref (#PCDATA)*>
|
||||
<!-- This element is used to indicate a cross-reference to another
|
||||
entry with a similar or related meaning or sense. The content of
|
||||
this element must exactly match that of a keb or reb element in
|
||||
another entry.
|
||||
-->
|
||||
<!ELEMENT ant (#PCDATA)*>
|
||||
<!-- This element is used to indicate another entry which is an
|
||||
antonym of the current entry/sense. The content of this element
|
||||
must exactly match that of a keb or reb element in another entry.
|
||||
-->
|
||||
<!ELEMENT pos (#PCDATA)>
|
||||
<!-- Part-of-speech information about the entry/sense. Should use
|
||||
appropriate entity codes.
|
||||
-->
|
||||
<!ELEMENT field (#PCDATA)>
|
||||
<!-- Information about the field of application of the entry. When
|
||||
absent, general application is implied. Entity coding for specific
|
||||
fields of application. -->
|
||||
<!ELEMENT misc (#PCDATA)>
|
||||
<!-- This element is used for other relevant information about
|
||||
the entry. -->
|
||||
<!ELEMENT gloss (#PCDATA | pri)*>
|
||||
<!-- Within each sense will be one or more "glosses", i.e.
|
||||
target-language words or phrases which are equivalents to the
|
||||
Japanese word. This element would normally be present, however it
|
||||
may be omitted in entries which are purely for a cross-reference.
|
||||
-->
|
||||
<!ATTLIST gloss g_lang CDATA "en">
|
||||
<!-- The g_lang attribute defines the target language of the
|
||||
gloss. It will be coded using the two-letter language code from
|
||||
the ISO 639 standard. When absent, the value "en" (i.e. English)
|
||||
is the default value. -->
|
||||
<!ATTLIST gloss g_gend CDATA #IMPLIED>
|
||||
<!-- The g_gend attribute defines the gender of the gloss (typically
|
||||
a noun in the target language. When absent, the gender is either
|
||||
not relevant or has yet to be provided.
|
||||
-->
|
||||
<!ELEMENT pri (#PCDATA)>
|
||||
<!-- These elements highlight particular target-language words which
|
||||
are strongly associated with the Japanese word. The purpose is to
|
||||
establish a set of target-language words which can effectively be
|
||||
used as head-words in a reverse target-language/Japanese relationship.
|
||||
-->
|
||||
<!ELEMENT example (#PCDATA)>
|
||||
<!-- The example elements provide for pairs of short Japanese and
|
||||
target-language phrases or sentences which exemplify the usage of the
|
||||
Japanese head-word and the target-language gloss. Words in example
|
||||
fields would typically not be indexed by a dictionary application.
|
||||
-->
|
||||
<!ELEMENT s_inf (#PCDATA)>
|
||||
<!-- The sense-information elements provided for additional
|
||||
information to be recorded about a sense. Typical usage would
|
||||
be to indicate such things as level of currency of a sense, the
|
||||
regional variations, etc.
|
||||
-->
|
||||
<!-- The following entity codes are used for common elements within the
|
||||
various information fields.
|
||||
-->
|
||||
<!ENTITY MA "martial arts term">
|
||||
<!ENTITY X "rude or X-rated term (not displayed in educational software)">
|
||||
<!ENTITY abbr "abbreviation">
|
||||
<!ENTITY adj "adjective (keiyoushi)">
|
||||
<!ENTITY adj-na "adjectival nouns or quasi-adjectives (keiyodoshi)">
|
||||
<!ENTITY adj-no "nouns which may take the genitive case particle `no'">
|
||||
<!ENTITY adj-pn "pre-noun adjectival (rentaishi)">
|
||||
<!ENTITY adj-t "`taru' adjective">
|
||||
<!ENTITY adv "adverb (fukushi)">
|
||||
<!ENTITY adv-n "adverbial noun">
|
||||
<!ENTITY adv-to "adverb taking the `to' particle">
|
||||
<!ENTITY arch "archaism">
|
||||
<!ENTITY ateji "ateji (phonetic) reading">
|
||||
<!ENTITY aux "auxiliary">
|
||||
<!ENTITY aux-v "auxiliary verb">
|
||||
<!ENTITY aux-adj "auxiliary adjective">
|
||||
<!ENTITY Buddh "Buddhist term">
|
||||
<!ENTITY chn "children's language">
|
||||
<!ENTITY col "colloquialism ">
|
||||
<!ENTITY comp "computer terminology">
|
||||
<!ENTITY conj "conjunction">
|
||||
<!ENTITY derog "derogatory">
|
||||
<!ENTITY ek "exclusively kanji">
|
||||
<!ENTITY exp "Expressions (phrases, clauses, etc.)">
|
||||
<!ENTITY fam "familiar language ">
|
||||
<!ENTITY fem "female term or language">
|
||||
<!ENTITY food "food term">
|
||||
<!ENTITY geom "geometry term">
|
||||
<!ENTITY gikun "gikun (meaning) reading">
|
||||
<!ENTITY gram "grammatical term">
|
||||
<!ENTITY hon "honorific or respectful (sonkeigo) language ">
|
||||
<!ENTITY hum "humble (kenjougo) language ">
|
||||
<!ENTITY iK "word containing irregular kanji usage">
|
||||
<!ENTITY id "idiomatic expression ">
|
||||
<!ENTITY ik "word containing irregular kana usage">
|
||||
<!ENTITY int "interjection (kandoushi)">
|
||||
<!ENTITY io "irregular okurigana usage">
|
||||
<!ENTITY iv "irregular verb">
|
||||
<!ENTITY ling "linguistics terminology">
|
||||
<!ENTITY m-sl "manga slang">
|
||||
<!ENTITY male "male term or language">
|
||||
<!ENTITY male-sl "male slang">
|
||||
<!ENTITY math "mathematics">
|
||||
<!ENTITY mil "military">
|
||||
<!ENTITY n "noun (common) (futsuumeishi)">
|
||||
<!ENTITY n-adv "adverbial noun (fukushitekimeishi)">
|
||||
<!ENTITY n-suf "noun, used as a suffix">
|
||||
<!ENTITY n-pref "noun, used as a prefix">
|
||||
<!ENTITY n-t "noun (temporal) (jisoumeishi)">
|
||||
<!ENTITY neg "negative (in a negative sentence, or with negative verb)">
|
||||
<!ENTITY neg-v "negative verb (when used with)">
|
||||
<!ENTITY num "numeric">
|
||||
<!ENTITY oK "word containing out-dated kanji ">
|
||||
<!ENTITY obs "obsolete term">
|
||||
<!ENTITY obsc "obscure term">
|
||||
<!ENTITY ok "out-dated or obsolete kana usage">
|
||||
<!ENTITY pol "polite (teineigo) language ">
|
||||
<!ENTITY pref "prefix ">
|
||||
<!ENTITY prt "particle ">
|
||||
<!ENTITY physics "physics terminology">
|
||||
<!ENTITY qv "quod vide (see another entry)">
|
||||
<!ENTITY rare "rare">
|
||||
<!ENTITY sl "slang">
|
||||
<!ENTITY suf "suffix ">
|
||||
<!ENTITY uK "word usually written using kanji alone ">
|
||||
<!ENTITY uk "word usually written using kana alone ">
|
||||
<!ENTITY v1 "Ichidan verb">
|
||||
<!ENTITY v5 "Godan verb (not completely classified)">
|
||||
<!ENTITY v5aru "Godan verb - -aru special class">
|
||||
<!ENTITY v5b "Godan verb with `bu' ending">
|
||||
<!ENTITY v5g "Godan verb with `gu' ending">
|
||||
<!ENTITY v5k "Godan verb with `ku' ending">
|
||||
<!ENTITY v5k-s "Godan verb - Iku/Yuku special class">
|
||||
<!ENTITY v5m "Godan verb with `mu' ending">
|
||||
<!ENTITY v5n "Godan verb with `nu' ending">
|
||||
<!ENTITY v5r "Godan verb with `ru' ending">
|
||||
<!ENTITY v5r-i "Godan verb with `ru' ending (irregular verb)">
|
||||
<!ENTITY v5s "Godan verb with `su' ending">
|
||||
<!ENTITY v5t "Godan verb with `tsu' ending">
|
||||
<!ENTITY v5u "Godan verb with `u' ending">
|
||||
<!ENTITY v5u-s "Godan verb with `u' ending (special class)">
|
||||
<!ENTITY v5uru "Godan verb - Uru old class verb (old form of Eru)">
|
||||
<!ENTITY vi "intransitive verb ">
|
||||
<!ENTITY vk "Kuru verb - special class">
|
||||
<!ENTITY vs "noun or participle which takes the aux. verb suru">
|
||||
<!ENTITY vs-s "suru verb - special class">
|
||||
<!ENTITY vs-i "suru verb - irregular">
|
||||
<!ENTITY vz "zuru verb - (alternative form of -jiru verbs)">
|
||||
<!ENTITY vt "transitive verb">
|
||||
<!ENTITY vulg "vulgar expression or word ">
|
||||
<!ENTITY mg "masculine gender">
|
||||
<!ENTITY fg "feminine gender">
|
||||
<!ENTITY ng "neuter gender">
|
||||
]>
|
||||
<!-- JMdict created: 2006-09-11 -->
|
||||
<JMdict>
|
||||
<!-- JMdict Japanese-Multilingual Dictionary file (XML format) -->
|
||||
<!-- Using V1.05 of the DTD -->
|
||||
<!-- Copyright J.W. Breen (jwb@csse.monash.edu.au) -->
|
||||
<entry>
|
||||
<ent_seq>1000000</ent_seq>
|
||||
<k_ele>
|
||||
<keb>ヽ</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>くりかえし</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>repetition mark in katakana</gloss>
|
||||
<gloss g_lang="de">Wiederholungszeichen für Katakana</gloss>
|
||||
<gloss g_lang="de">normalerweise nur in vertikaler Schreibweise verwendet</gloss>
|
||||
<gloss g_lang="fr">marque de la répétition dans katakana</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000010</ent_seq>
|
||||
<k_ele>
|
||||
<keb>ヾ</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>くりかえし</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>voiced repetition mark in katakana</gloss>
|
||||
<gloss g_lang="de">stimmhaftes Wiederholungszeichen für Katakana</gloss>
|
||||
<gloss g_lang="de">normalerweise nur in vertikaler Schreibweise verwendet</gloss>
|
||||
<gloss g_lang="fr">marque de la répétition sonore dans katakana</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000020</ent_seq>
|
||||
<k_ele>
|
||||
<keb>ゝ</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>くりかえし</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>repetition mark in hiragana</gloss>
|
||||
<gloss g_lang="de">Wiederholungszeichen für Hiragana</gloss>
|
||||
<gloss g_lang="de">normalerweise nur in vertikaler Schreibweise verwendet</gloss>
|
||||
<gloss g_lang="fr">marque de la répétition dans hiragana</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000030</ent_seq>
|
||||
<k_ele>
|
||||
<keb>ゞ</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>くりかえし</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>voiced repetition mark in hiragana</gloss>
|
||||
<gloss g_lang="de">stimmhaftes Wiederholungszeichen für Hiragana</gloss>
|
||||
<gloss g_lang="de">normalerweise nur in vertikaler Schreibweise verwendet</gloss>
|
||||
<gloss g_lang="fr">marque de la répétition sonore dans hiragana</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000040</ent_seq>
|
||||
<k_ele>
|
||||
<keb>〃</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>おなじく</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>ditto mark</gloss>
|
||||
<gloss g_lang="ru">знак "то же самое"</gloss>
|
||||
<gloss g_lang="de">Wiederholungszeichen in Tabellen</gloss>
|
||||
<gloss g_lang="fr">idem marque</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000050</ent_seq>
|
||||
<k_ele>
|
||||
<keb>仝</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>どうじょう</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>"as above" mark</gloss>
|
||||
<gloss g_lang="de">Abkürzung für "siehe oben"</gloss>
|
||||
<gloss g_lang="fr">comme au-dessus</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000060</ent_seq>
|
||||
<k_ele>
|
||||
<keb>々</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>くりかえし</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>repetition of kanji (sometimes voiced)</gloss>
|
||||
<gloss g_lang="de">Wiederholungszeichen für Kanji</gloss>
|
||||
<gloss g_lang="de">(Laut wird durch Wiederholung manchmal stimmhaft)</gloss>
|
||||
<gloss g_lang="fr">répétition de kanji(quelquefois a exprimé)</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000070</ent_seq>
|
||||
<k_ele>
|
||||
<keb>〆</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>しめ</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>end or closure mark</gloss>
|
||||
<gloss g_lang="de">Zeichen als eine Art Versiegelung über der zugeklebten Lasche auf der Rückseite eines Briefumschlages</gloss>
|
||||
<gloss g_lang="fr">fin ou marque de la fermeture</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000080</ent_seq>
|
||||
<k_ele>
|
||||
<keb>〇</keb>
|
||||
</k_ele>
|
||||
<k_ele>
|
||||
<keb>○</keb>
|
||||
<ke_inf>&iK;</ke_inf>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>かんすうじゼロ</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<xref>漢数字</xref>
|
||||
<xref>ゼロ</xref>
|
||||
<gloss>"kanji" zero</gloss>
|
||||
<gloss g_lang="de">Kanji-Ziffer für Null</gloss>
|
||||
<gloss g_lang="fr">kanji</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000090</ent_seq>
|
||||
<k_ele>
|
||||
<keb>○</keb>
|
||||
</k_ele>
|
||||
<k_ele>
|
||||
<keb>〇</keb>
|
||||
<ke_inf>&iK;</ke_inf>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>まる</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>circle (sometimes used for zero)</gloss>
|
||||
<gloss g_lang="ru">круг</gloss>
|
||||
<gloss g_lang="ru">но́ль</gloss>
|
||||
<gloss g_lang="de">Kreis</gloss>
|
||||
<gloss g_lang="de">Markierung für "richtig"</gloss>
|
||||
<gloss g_lang="de">Maru</gloss>
|
||||
<gloss g_lang="de">(ein japan. Schriftfont hat mindestens drei verschiedene Codierungen und Darstellungen für "maru")</gloss>
|
||||
<gloss g_lang="fr">entourez</gloss>
|
||||
<gloss g_lang="fr">mettez à zéro</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000100</ent_seq>
|
||||
<k_ele>
|
||||
<keb>ABC順</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>エービーシーじゅん</reb>
|
||||
</r_ele>
|
||||
<r_ele>
|
||||
<reb>ええびいしいじゅん</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>alphabetical order</gloss>
|
||||
<gloss g_lang="de">alphabetische Ordnung</gloss>
|
||||
<gloss g_lang="de">alphabetische Reihenfolge</gloss>
|
||||
<gloss g_lang="fr">ordre alphabétique</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000110</ent_seq>
|
||||
<k_ele>
|
||||
<keb>CDプレーヤー</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>シーディープレーヤー</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>CD player</gloss>
|
||||
<gloss g_lang="ru">CD плеер</gloss>
|
||||
<gloss g_lang="ru">проигрыватель компакт-дисков</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000120</ent_seq>
|
||||
<k_ele>
|
||||
<keb>Hな映画</keb>
|
||||
</k_ele>
|
||||
<k_ele>
|
||||
<keb>エッチな映画</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>エッチなえいが</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>pornographic film</gloss>
|
||||
<gloss>salacious film</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000130</ent_seq>
|
||||
<k_ele>
|
||||
<keb>N響</keb>
|
||||
</k_ele>
|
||||
<r_ele>
|
||||
<reb>エヌきょう</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<misc>&abbr;</misc>
|
||||
<gloss>NHK Symphony Orchestra</gloss>
|
||||
<gloss g_lang="fr">NHK Symphonie Orchestre(abbr)</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000140</ent_seq>
|
||||
<r_ele>
|
||||
<reb>Oバック</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>O-back</gloss>
|
||||
<gloss>skirt with peek-a-boo hole in rump</gloss>
|
||||
<gloss g_lang="fr">O En arrière</gloss>
|
||||
<gloss g_lang="fr">contournez avec coucou trou dans croupe</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
<entry>
|
||||
<ent_seq>1000150</ent_seq>
|
||||
<r_ele>
|
||||
<reb>RS232ケーブル</reb>
|
||||
</r_ele>
|
||||
<sense>
|
||||
<pos>&n;</pos>
|
||||
<gloss>rs232 cable</gloss>
|
||||
<gloss g_lang="fr">les rs232 câblent</gloss>
|
||||
<gloss g_lang="fr">(JF2)</gloss>
|
||||
</sense>
|
||||
</entry>
|
||||
</JMdict>
|
|
@ -1,678 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/css"
|
||||
href="../../documentation/documentation.css"
|
||||
?>
|
||||
<?xml-stylesheet type="text/xsl"
|
||||
href="../../documentation/documentation.xsl"
|
||||
?>
|
||||
<!DOCTYPE documentation SYSTEM "http://www.germane-software.com/software/documentation/documentation.dtd">
|
||||
<documentation>
|
||||
<head>
|
||||
<title>REXML Tutorial</title>
|
||||
|
||||
<version>$Revision: 1.1.2.1 $</version>
|
||||
|
||||
<date>*2001-296+594</date>
|
||||
|
||||
<home>http://www.germane-software.com/~ser/software/rexml</home>
|
||||
|
||||
<base></base>
|
||||
|
||||
<language>ruby</language>
|
||||
|
||||
<author email="ser@germane-software.com"
|
||||
href="http://www.germane-software.com/~ser">Sean Russell</author>
|
||||
</head>
|
||||
|
||||
<overview>
|
||||
<purpose lang="en">
|
||||
<p>This is a tutorial for using <link
|
||||
href="http://www.germane-software.com/~ser/software/rexml">REXML</link>,
|
||||
a pure Ruby XML processor.</p>
|
||||
</purpose>
|
||||
|
||||
<general>
|
||||
<p>REXML was inspired by the Electric XML library for Java, which
|
||||
features an easy-to-use API, small size, and speed. Hopefully, REXML,
|
||||
designed with the same philosophy, has these same features. I've tried
|
||||
to keep the API as intuitive as possible, and have followed the Ruby
|
||||
methodology for method naming and code flow, rather than mirroring the
|
||||
Java API.</p>
|
||||
|
||||
<p>REXML supports both tree and stream document parsing. Stream parsing
|
||||
is faster (about 1.5 times as fast). However, with stream parsing, you
|
||||
don't get access to features such as XPath.</p>
|
||||
|
||||
<p>The <link href="../doc/index.html">API</link> documentation also
|
||||
contains code snippits to help you learn how to use various methods.
|
||||
This tutorial serves as a starting point and quick guide to using
|
||||
REXML.</p>
|
||||
|
||||
<subsection title="Tree Parsing XML and accessing Elements">
|
||||
<p>We'll start with parsing an XML document</p>
|
||||
|
||||
<example>require "rexml/document"
|
||||
file = File.new( "mydoc.xml" )
|
||||
doc = REXML::Document.new file</example>
|
||||
|
||||
<p>Line 3 creates a new document and parses the supplied file. You can
|
||||
also do the following</p>
|
||||
|
||||
<example>require "rexml/document"
|
||||
include REXML # so that we don't have to prefix everything with REXML::...
|
||||
string = <<EOF
|
||||
<mydoc>
|
||||
<someelement attribute="nanoo">Text, text, text</someelement>
|
||||
</mydoc>
|
||||
EOF
|
||||
doc = Document.new string</example>
|
||||
|
||||
<p>So parsing a string is just as easy as parsing a file. For future
|
||||
examples, I'm going to omit both the <code>require</code> and
|
||||
<code>include</code> lines.</p>
|
||||
|
||||
<p>Once you have a document, you can access elements in that document
|
||||
in a number of ways:</p>
|
||||
|
||||
<list>
|
||||
<item>The <code>Element</code> class itself has
|
||||
<code>each_element_with_attribute</code>, a common way of accessing
|
||||
elements.</item>
|
||||
|
||||
<item>The attribute <code>Element.elements</code> is an
|
||||
<code>Elements</code> class instance which has the <code>each</code>
|
||||
and <code>[]</code> methods for accessing elements. Both methods can
|
||||
be supplied with an XPath for filtering, which makes them very
|
||||
powerful.</item>
|
||||
|
||||
<item>Since <code>Element</code> is a subclass of Parent, you can
|
||||
also access the element's children directly through the Array-like
|
||||
methods <code>Element[], Element.each, Element.find,
|
||||
Element.delete</code>. This is the fastest way of accessing
|
||||
children, but note that, being a true array, XPath searches are not
|
||||
supported, and that all of the element children are contained in
|
||||
this array, not just the Element children.</item>
|
||||
</list>
|
||||
|
||||
<p>Here are a few examples using these methods. First is the source
|
||||
document used in the examples. Save this as mydoc.xml before running
|
||||
any of the examples that require it:</p>
|
||||
|
||||
<example title="The source document"><inventory title="OmniCorp Store #45x10^3">
|
||||
<section name="health">
|
||||
<item upc="123456789" stock="12">
|
||||
<name>Invisibility Cream</name>
|
||||
<price>14.50</price>
|
||||
<description>Makes you invisible</description>
|
||||
</item>
|
||||
<item upc="445322344" stock="18">
|
||||
<name>Levitation Salve</name>
|
||||
<price>23.99</price>
|
||||
<description>Levitate yourself for up to 3 hours per application</description>
|
||||
</item>
|
||||
</section>
|
||||
<section name="food">
|
||||
<item upc="485672034" stock="653">
|
||||
<name>Blork and Freen Instameal</name>
|
||||
<price>4.95</price>
|
||||
<description>A tasty meal in a tablet; just add water</description>
|
||||
</item>
|
||||
<item upc="132957764" stock="44">
|
||||
<name>Grob winglets</name>
|
||||
<price>3.56</price>
|
||||
<description>Tender winglets of Grob. Just add water</description>
|
||||
</item>
|
||||
</section>
|
||||
</inventory></example>
|
||||
|
||||
<example title="Accessing Elements">doc = Document.new File.new("mydoc.xml")
|
||||
doc.elements.each("inventory/section") { |element| puts element.attributes["name"] }
|
||||
# -> health
|
||||
# -> food
|
||||
doc.elements.each("*/section/item") { |element| puts element.attributes["upc"] }
|
||||
# -> 123456789
|
||||
# -> 445322344
|
||||
# -> 485672034
|
||||
# -> 132957764
|
||||
root = doc.root
|
||||
puts root.attributes["title"]
|
||||
# -> OmniCorp Store #45x10^3
|
||||
puts root.elements["section/item[@stock='44']"].attributes["upc"]
|
||||
# -> 132957764
|
||||
puts root.elements["section"].attributes["name"]
|
||||
# -> health (returns the first encountered matching element)
|
||||
puts root.elements[1].attributes["name"]
|
||||
# -> health (returns the FIRST child element)
|
||||
root.detect {|node| node.kind_of? Element and node.attributes["name"] == "food" }</example>
|
||||
|
||||
<p>Notice the second-to-last line of code. Element children in REXML
|
||||
are indexed starting at 1, not 0. This is because XPath itself counts
|
||||
elements from 1, and REXML maintains this relationship; IE,
|
||||
<code>root.elements['*[1]'] == root.elements[1]</code>. The last line
|
||||
finds the first child element with the name of "food". As you can see
|
||||
in this example, accessing attributes is also straightforward.</p>
|
||||
|
||||
<p>You can also access xpaths directly via the XPath class.</p>
|
||||
|
||||
<example title="Using XPath"># The invisibility cream is the first <item>
|
||||
invisibility = XPath.first( doc, "//item" )
|
||||
# Prints out all of the prices
|
||||
XPath.each( doc, "//price") { |element| puts element.text }
|
||||
# Gets an array of all of the "name" elements in the document.
|
||||
names = XPath.match( doc, "//name" ) </example>
|
||||
|
||||
<p>Another way of getting an array of matching nodes is through
|
||||
Element.elements.to_a(). Although this is a method on elements, if
|
||||
passed an XPath it can return an array of arbitrary objects. This is
|
||||
due to the fact that XPath itself can return arbitrary nodes
|
||||
(Attribute nodes, Text nodes, and Element nodes).</p>
|
||||
|
||||
<example title="Using to_a()">all_elements = doc.elements.to_a
|
||||
all_children = doc.to_a
|
||||
all_upc_strings = doc.elements.to_a( "//item/attribute::upc" )
|
||||
all_name_elements = doc.elements.to_a( "//name" )</example>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Text Nodes">
|
||||
<p>REXML attempts to make the common case simple, but this means that
|
||||
the uncommon case can be complicated. This is especially true with
|
||||
Text nodes.</p>
|
||||
|
||||
<p>Text nodes have a lot of behavior, and in the case of internal
|
||||
entities, what you get may be different from what you expect. When
|
||||
REXML reads an XML document, in parses the DTD and creates an internal
|
||||
table of entities. If it finds any of these entities in the document,
|
||||
it replaces them with their values:</p>
|
||||
|
||||
<example title="Entity Replacement">doc = Document.new '<!DOCTYPE foo [
|
||||
<!ENTITY ent "replace">
|
||||
]><a>&ent;</a>'
|
||||
doc.root.text #-> "replace"
|
||||
</example>
|
||||
|
||||
<p>When you write the document back out, REXML replaces the values
|
||||
with the entity reference:</p>
|
||||
|
||||
<example>doc.to_s
|
||||
# Generates:
|
||||
# <!DOCTYPE foo [
|
||||
# <!ENTITY ent "replace">
|
||||
# ]><a>&ent;</a></example>
|
||||
|
||||
<p>But there's a problem. What happens if only some of the words are
|
||||
also entity reference values?</p>
|
||||
|
||||
<example>doc = Document.new '<!DOCTYPE foo [
|
||||
<!ENTITY ent "replace">
|
||||
]><a>replace &ent;</a>'
|
||||
doc.root.text #-> "replace replace"
|
||||
</example>
|
||||
|
||||
<p>Well, REXML does the only thing it can:</p>
|
||||
|
||||
<example>doc.to_s
|
||||
# Generates:
|
||||
# <!DOCTYPE foo [
|
||||
# <!ENTITY ent "replace">
|
||||
# ]><a>&ent; &ent;</a></example>
|
||||
|
||||
<p>This is probably not what you expect. However, when designing
|
||||
REXML, I had a choice between this behavior, and using immutable text
|
||||
nodes. The problem is that, if you can change the text in a node,
|
||||
REXML can never tell which tokens you want to have replaced with
|
||||
entities. There is a wrinkle: REXML will write what it gets in as long
|
||||
as you don't access the text. This is because REXML does lazy
|
||||
evaluation of entities. Therefore,</p>
|
||||
|
||||
<example title="Lazy Evaluation">doc = Document.new( '<!DOCTYPE foo
|
||||
[ <!ENTITY ent "replace"> ]><a>replace
|
||||
&ent;</a>' ) doc.to_s # Generates: # <!DOCTYPE foo [ #
|
||||
<!ENTITY ent "replace"> # ]><a><emphasis>replace
|
||||
&ent;</emphasis></a> doc.root.text #-> Now accessed,
|
||||
entities have been resolved doc.to_s # Generates: # <!DOCTYPE foo [
|
||||
# <!ENTITY ent "replace"> # ]><a><emphasis>&ent;
|
||||
&ent;</emphasis></a></example>
|
||||
|
||||
<p>There is a programmatic solution: <code>:raw</code>. If you set the
|
||||
<code>:raw</code> flag on any Text or Element node, the entities
|
||||
within that node will not be processed. This means that you'll have to
|
||||
deal with entities yourself:</p>
|
||||
|
||||
<example title="Entity Replacement">doc = Document.new('<!DOCTYPE
|
||||
foo [ <!ENTITY ent "replace"> ]><a>replace
|
||||
&ent;</a>',<emphasis>{:raw=>:all})</emphasis>
|
||||
doc.root.text #-> "replace &ent;" doc.to_s # Generates: #
|
||||
<!DOCTYPE foo [ # <!ENTITY ent "replace"> #
|
||||
]><a>replace &ent;</a></example>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Creating XML documents">
|
||||
<p>Again, there are a couple of mechanisms for creating XML documents
|
||||
in REXML. Adding elements by hand is faster than the convenience
|
||||
method, but which you use will probably be a matter of aesthetics.</p>
|
||||
|
||||
<example title="Creating elements">el = someelement.add_element "myel"
|
||||
# creates an element named "myel", adds it to "someelement", and returns it
|
||||
el2 = el.add_element "another", {"id"=>"10"}
|
||||
# does the same, but also sets attribute "id" of el2 to "10"
|
||||
el3 = Element.new "blah"
|
||||
el1.elements << el3
|
||||
el3.attributes["myid"] = "sean"
|
||||
# creates el3 "blah", adds it to el1, then sets attribute "myid" to "sean"</example>
|
||||
|
||||
<p>If you want to add text to an element, you can do it by either
|
||||
creating Text objects and adding them to the element, or by using the
|
||||
convenience method <code>text=</code></p>
|
||||
|
||||
<example title="Adding text">el1 = Element.new "myelement"
|
||||
el1.text = "Hello world!"
|
||||
# -> <myelement>Hello world!</myelement>
|
||||
el1.add_text "Hello dolly"
|
||||
# -> <myelement>Hello world!Hello dolly</element>
|
||||
el1.add Text.new("Goodbye")
|
||||
# -> <myelement>Hello world!Hello dollyGoodbye</element>
|
||||
el1 << Text.new(" cruel world")
|
||||
# -> <myelement>Hello world!Hello dollyGoodbye cruel world</element></example>
|
||||
|
||||
<p>But note that each of these text objects are still stored as
|
||||
separate objects; <code>el1.text</code> will return "Hello world!";
|
||||
<code>el1[2]</code> will return a Text object with the contents
|
||||
"Goodbye".</p>
|
||||
|
||||
<p>Please be aware that all text nodes in REXML are UTF-8 encoded, and
|
||||
all of your code must reflect this. You may input and output other
|
||||
encodings (UTF-8, UTF-16, ISO-8859-1, and UNILE are all supported,
|
||||
input and output), but within your program, you must pass REXML UTF-8
|
||||
strings.</p>
|
||||
|
||||
<p>I can't emphasize this enough, because people do have problems with
|
||||
this. REXML can't possibly alway guess correctly how your text is
|
||||
encoded, so it always assumes the text is UTF-8. It also does not warn
|
||||
you when you try to add text which isn't properly encoded, for the
|
||||
same reason. You must make sure that you are adding UTF-8 text.
|
||||
 If you're adding standard 7-bit ASCII, which is most common, you
|
||||
don't have to worry.  If you're using ISO-8859-1 text (characters
|
||||
above 0x80), you must convert it to UTF-8 before adding it to an
|
||||
element.  You can do this with the shard:
|
||||
<code>text.unpack("C*").pack("U*")</code>. If you ignore this warning
|
||||
and add 8-bit ASCII characters to your documents, your code may
|
||||
work... or it may not.  In either case, REXML is not at fault.
|
||||
You have been warned.</p>
|
||||
|
||||
<p>One last thing: alternate encoding output support only works from
|
||||
Document.write() and Document.to_s(). If you want to write out other
|
||||
nodes with a particular encoding, you must wrap your output object
|
||||
with Output:</p>
|
||||
|
||||
<example title="Encoded Output">e = Element.new "<a/>"
|
||||
e.text = "f\xfcr" # ISO-8859-1 'ü'
|
||||
o = ''
|
||||
e.write( Output.new( o, "ISO-8859-1" ) )
|
||||
</example>
|
||||
|
||||
<p>You can pass Output any of the supported encodings.</p>
|
||||
|
||||
<p>If you want to insert an element between two elements, you can use
|
||||
either the standard Ruby array notation, or
|
||||
<code>Parent.insert_before</code> and
|
||||
<code>Parent.insert_after</code>.</p>
|
||||
|
||||
<example title="Inserts">doc = Document.new "<a><one/><three/></a>"
|
||||
doc.root[1,0] = Element.new "two"
|
||||
# -> <a><one/><two/><three/></a>
|
||||
three = doc.elements["a/three"]
|
||||
doc.root.insert_after three, Element.new "four"
|
||||
# -> <a><one/><two/><three/><four/></a>
|
||||
# A convenience method allows you to insert before/after an XPath:
|
||||
doc.root.insert_after( "//one", Element.new("one-five") )
|
||||
# -> <a><one/><one-five/><two/><three/><four/></a>
|
||||
# Another convenience method allows you to insert after/before an element:
|
||||
four = doc.elements["//four"]
|
||||
four.previous_sibling = Element.new("three-five")
|
||||
# -> <a><one/><one-five/><two/><three/><three-five/><four/></a></example>
|
||||
|
||||
<p>The <code>raw</code> flag in the <code>Text</code> constructor can
|
||||
be used to tell REXML to leave strings which have entities defined for
|
||||
them alone.</p>
|
||||
|
||||
<example title="Raw text">doc = Document.new( "<?xml version='1.0?>
|
||||
<!DOCTYPE foo SYSTEM 'foo.dtd' [
|
||||
<!ENTITY % s "Sean">
|
||||
]>
|
||||
<a/>"
|
||||
t = Text.new( "Sean", false, nil, false )
|
||||
doc.root.text = t
|
||||
t.to_s # -> &s;
|
||||
t = Text.new( "Sean", false, nil, true )
|
||||
doc.root.text = t
|
||||
t.to_s # -> Sean</example>
|
||||
|
||||
<p>Note that, in all cases, the <code>value()</code> method returns
|
||||
the text with entities expanded, so the <code>raw</code> flag only
|
||||
affects the <code>to_s()</code> method. If the <code>raw</code> is set
|
||||
for a text node, then <code>to_s()</code> will not entities will not
|
||||
normalize (turn into entities) entity values. You can not create raw
|
||||
text nodes that contain illegal XML, so the following will generate a
|
||||
parse error:</p>
|
||||
|
||||
<example>t = Text.new( "&", false, nil, true )</example>
|
||||
|
||||
<p>You can also tell REXML to set the Text children of given elements
|
||||
to raw automatically, on parsing or creating:</p>
|
||||
|
||||
<example title="Automatic raw text handling">doc = REXML::Document.new( source, { :raw => %w{ tag1 tag2 tag3 } }</example>
|
||||
|
||||
<p>In this example, all tags named "tag1", "tag2", or "tag3" will have
|
||||
any Text children set to raw text. If you want to have all of the text
|
||||
processed as raw text, pass in the :all tag:</p>
|
||||
|
||||
<example title="Raw documents">doc = REXML::Document.new( source, { :raw => :all })</example>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Writing a tree">
|
||||
<p>There aren't many things that are more simple than writing a REXML
|
||||
tree. Simply pass an object that supports <code><<( String
|
||||
)</code> to the <code>write</code> method of any object. In Ruby, both
|
||||
IO instances (File) and String instances support <<.</p>
|
||||
|
||||
<example>doc.write $stdout
|
||||
output = ""
|
||||
doc.write output</example>
|
||||
|
||||
<p>If you want REXML to pretty-print output, pass <code>write()</code>
|
||||
an indent value greater than -1:</p>
|
||||
|
||||
<example title="Write with pretty-printing">doc.write( $stdout, 0 )</example>
|
||||
|
||||
<p>REXML will not, by default, write out the XML declaration unless
|
||||
you specifically ask for them. If a document is read that contains an
|
||||
XML declaration, that declaration <emphasis>will</emphasis> be written
|
||||
faithfully. The other way you can tell REXML to write the declaration
|
||||
is to specifically add the declaration:</p>
|
||||
|
||||
<example title="Adding an XML Declaration to a Document">doc = Document.new
|
||||
doc.add_element 'foo'
|
||||
doc.to_s #-> <foo/>
|
||||
doc << XMLDecl.new
|
||||
doc.to_s #-> <?xml version='1.0'?><foo/></example>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Iterating">
|
||||
<p>There are four main methods of iterating over children.
|
||||
<code>Element.each</code>, which iterates over all the children;
|
||||
<code>Element.elements.each</code>, which iterates over just the child
|
||||
Elements; <code>Element.next_element</code> and
|
||||
<code>Element.previous_element</code>, which can be used to fetch the
|
||||
next Element siblings; and <code>Element.next_sibling</code> and
|
||||
<code>Eleemnt.previous_sibling</code>, which fetches the next and
|
||||
previous siblings, regardless of type.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Stream Parsing">
|
||||
<p>REXML stream parsing requires you to supply a Listener class. When
|
||||
REXML encounters events in a document (tag start, text, etc.) it
|
||||
notifies your listener class of the event. You can supply any subset
|
||||
of the methods, but make sure you implement method_missing if you
|
||||
don't implement them all. A StreamListener module has been supplied as
|
||||
a template for you to use.</p>
|
||||
|
||||
<example title="Stream parsing">list = MyListener.new
|
||||
source = File.new "mydoc.xml"
|
||||
REXML::Document.parse_stream(source, list)</example>
|
||||
|
||||
<p>Stream parsing in REXML is much like SAX, where events are
|
||||
generated when the parser encounters them in the process of parsing
|
||||
the document. When a tag is encountered, the stream listener's
|
||||
<code>tag_start()</code> method is called. When the tag end is
|
||||
encountered, <code>tag_end()</code> is called. When text is
|
||||
encountered, <code>text()</code> is called, and so on, until the end
|
||||
of the stream is reached. One other note: the method
|
||||
<code>entity()</code> is called when an <code>&entity;</code> is
|
||||
encountered in text, and only then.</p>
|
||||
|
||||
<p>Please look at the <link
|
||||
href="../doc/classes/REXML/StreamListener.html">StreamListener
|
||||
API</link> for more information.<footnote>You must generate the API
|
||||
documentation with rdoc or download the API documentation from the
|
||||
REXML website for this documentation.</footnote></p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Whitespace">
|
||||
<p>By default, REXML respects whitespace in your document. In many
|
||||
applications, you want the parser to compress whitespace in your
|
||||
document. In these cases, you have to tell the parser which elements
|
||||
you want to respect whitespace in by passing a context to the
|
||||
parser:</p>
|
||||
|
||||
<example title="Compressing whitespace">doc = REXML::Document.new( source, { :compress_whitespace => %w{ tag1 tag2 tag3 } }</example>
|
||||
|
||||
<p>Whitespace for tags "tag1", "tag2", and "tag3" will be compressed;
|
||||
all other tags will have their whitespace respected. Like :raw, you
|
||||
can set :compress_whitespace to :all, and have all elements have their
|
||||
whitespace compressed.</p>
|
||||
|
||||
<p>You may also use the tag <code>:respect_whitespace</code>, which
|
||||
flip-flops the behavior. If you use <code>:respect_whitespace</code>
|
||||
for one or more tags, only those elements will have their whitespace
|
||||
respected; all other tags will have their whitespace compressed.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Automatic Entity Processing">
|
||||
<p>REXML does some automatic processing of entities for your
|
||||
convenience. The processed entities are &, <, >, ", and '.
|
||||
If REXML finds any of these characters in Text or Attribute values, it
|
||||
automatically turns them into entity references when it writes them
|
||||
out. Additionally, when REXML finds any of these entity references in
|
||||
a document source, it converts them to their character equivalents.
|
||||
All other entity references are left unprocessed. If REXML finds an
|
||||
&, <, or > in the document source, it will generate a
|
||||
parsing error.</p>
|
||||
|
||||
<example title="Entity processing">bad_source = "<a>Cats & dogs</a>"
|
||||
good_source = "<a>Cats &amp; &#100;ogs</a>"
|
||||
doc = REXML::Document.new bad_source
|
||||
# Generates a parse error
|
||||
doc = REXML::Document.new good_source
|
||||
puts doc.root.text
|
||||
# -> "Cats & &#100;ogs"
|
||||
doc.root.write $stdout
|
||||
# -> "<a>Cats &amp; &#100;ogs</a>"
|
||||
doc.root.attributes["m"] = "x'y\"z"
|
||||
puts doc.root.attributes["m"]
|
||||
# -> "x'y\"z"
|
||||
doc.root.write $stdout
|
||||
# -> "<a m='x&apos;y&quot;z'>Cats &amp; &#100;ogs</a>"</example>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Namespaces">
|
||||
<p>Namespaces are fully supported in REXML and within the XPath
|
||||
parser. There are a few caveats when using XPath, however:</p>
|
||||
|
||||
<list>
|
||||
<item>If you don't supply a namespace mapping, the default namespace
|
||||
mapping of the context element is used. This has its limitations,
|
||||
but is convenient for most purposes.</item>
|
||||
|
||||
<item>If you need to supply a namespace mapping, you must use the
|
||||
XPath methods <code>each</code>, <code>first</code>, and
|
||||
<code>match</code> and pass them the mapping.</item>
|
||||
</list>
|
||||
|
||||
<example title="Using namespaces">source = "<a xmlns:x='foo' xmlns:y='bar'><x:b id='1'/><y:b id='2'/></a>"
|
||||
doc = Document.new source
|
||||
doc.elements["/a/x:b"].attributes["id"] # -> '1'
|
||||
XPath.first(doc, "/a/m:b", {"m"=>"bar"}).attributes["id"] # -> '2'
|
||||
doc.elements["//x:b"].prefix # -> 'x'
|
||||
doc.elements["//x:b"].namespace # -> 'foo'
|
||||
XPath.first(doc, "//m:b", {"m"=>"bar"}).prefix # -> 'y'</example>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Pull parsing">
|
||||
<p>The pull parser API is not yet stable. When it settles down, I'll
|
||||
fill in this section. For now, you'll have to bite the bullet and read
|
||||
the <link
|
||||
href="http://www.germane-software.com/software/rexml_doc/classes/REXML/PullParser.html">PullParser</link>
|
||||
API docs. Ignore the PullListener class; it is a private helper
|
||||
class.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="SAX2 Stream Parsing">
|
||||
<p>The original REXML stream parsing API is very minimal. This also
|
||||
means that it is fairly fast. For a more complex, more "standard" API,
|
||||
REXML also includes a streaming parser with a SAX2+ API. This API
|
||||
differs from SAX2 in a couple of ways, such as having more filters and
|
||||
multiple notification mechanisms, but the core API is SAX2.</p>
|
||||
|
||||
<p>The two classes in the SAX2 API are <link
|
||||
href="http://www.germane-software.com/software/rexml_doc/classes/REXML/SAX2Parser.html"><code>SAX2Parser</code></link>
|
||||
and <link
|
||||
href="http://www.germane-software.com/software/rexml_doc/classes/REXML/SAX2Listener.html"><code>SAX2Listener</code></link>.
|
||||
You can use the parser in one of five ways, depending on your needs.
|
||||
Three of the ways are useful if you are filtering for a small number
|
||||
of events in the document, such as just printing out the names of all
|
||||
of the elements in a document, or getting all of the text in a
|
||||
document. The other two ways are for more complex processing, where
|
||||
you want to be notified of multiple events. The first three involve
|
||||
Procs, and the last two involve listeners. The listener mechanisms are
|
||||
very similar to the original REXML streaming API, with the addition of
|
||||
filtering options, and are faster than the proc mechanisms.</p>
|
||||
|
||||
<p>An example is worth a thousand words, so we'll just take a look at
|
||||
a small example of each of the mechanisms. The first example involves
|
||||
printing out only the text content of a document.</p>
|
||||
|
||||
<example title="Filtering for Events with Procs">require 'rexml/sax2parser'
|
||||
parser = REXML::SAX2Parser.new( File.new( 'documentation.xml' ) )
|
||||
parser.listen( :characters ) {|text| puts text }
|
||||
parser.parse</example>
|
||||
|
||||
<p>In this example, we tell the parser to call our block for every
|
||||
<code>characters</code> event. "characters" is what SAX2 calls Text
|
||||
nodes. The event is identified by the symbol <code>:characters</code>.
|
||||
There are a number of these events, including
|
||||
<code>:element_start</code>, <code>:end_prefix_mapping</code>, and so
|
||||
on; the events are named after the methods in the
|
||||
<code>SAX2Listener</code> API, so refer to that document for a
|
||||
complete list.</p>
|
||||
|
||||
<p>You can additionally filter for particular elements by passing an
|
||||
array of tag names to the <code>listen</code> method. In further
|
||||
examples, we will not include the <code>require</code> or parser
|
||||
construction lines, as they are the same for all of these
|
||||
examples.</p>
|
||||
|
||||
<example title="Filtering for Events on Particular Elements with Procs">parser.listen( :characters, %w{ changelog todo } ) {|text| puts text }
|
||||
parser.parse</example>
|
||||
|
||||
<p>In this example, only the text content of changelog and todo
|
||||
elements will be printed. The array of tag names can also contain
|
||||
regular expressions which the element names will be matched
|
||||
against.</p>
|
||||
|
||||
<p>Finally, as a shortcut, if you do not pass a symbol to the listen
|
||||
method, it will default to <code>:element_start</code></p>
|
||||
|
||||
<example title="Default Events">parser.listen( %w{ item }) do |uri,localname,qname,attributes|
|
||||
puts attributes['version']
|
||||
end
|
||||
parser.parse</example>
|
||||
|
||||
<p>This example prints the "version" attribute of all "item" elements
|
||||
in the document. Notice that the number of arguments passed to the
|
||||
block is larger than for <code>:text</code>; again, check the
|
||||
SAX2Listener API for a list of what arguments are passed the blocks
|
||||
for a given event.</p>
|
||||
|
||||
<p>The last two mechanisms for parsing use the SAX2Listener API. Like
|
||||
StreamListener, SAX2Listener is a <code>module</code>, so you can
|
||||
<code>include</code> it in your class to give you an adapter. To use
|
||||
the listener model, create a class that implements some of the
|
||||
SAX2Listener methods, or all of them if you don't include the
|
||||
SAX2Listener model. Add them to a parser as you would blocks, and when
|
||||
the parser is run, the methods will be called when events occur.
|
||||
Listeners do not use event symbols, but they can filter on element
|
||||
names.</p>
|
||||
|
||||
<example title="Filtering for Events with Listeners">listener1 = MySAX2Listener.new
|
||||
listener2 = MySAX2Listener.new
|
||||
parser.listen( listener1 )
|
||||
parser.listen( %{ changelog, todo, credits }, listener2 )
|
||||
parser.parse</example>
|
||||
|
||||
<p>In the previous example, <code>listener1</code> will be notified of
|
||||
all events that occur, and <code>listener2</code> will only be
|
||||
notified of events that occur in <code>changelog</code>,
|
||||
<code>todo</code>, and <code>credits</code> elements. We also see that
|
||||
multiple listeners can be added to the same parser; multiple blocks
|
||||
can also be added, and listeners and blocks can be mixed together.</p>
|
||||
|
||||
<p>There is, as yet, no mechanism for recursion. Two upcoming features
|
||||
of the SAX2 API will be the ability to filter based on an XPath, and
|
||||
the ability to specify filtering on an elemnt and all of its
|
||||
descendants.</p>
|
||||
|
||||
<p><em>WARNING:</em> The SAX2 API for dealing with doctype (DTD)
|
||||
events almost <em>certainly</em> will change.</p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Convenience methods">
|
||||
<p>Michael Neumann contributed some convenience functions for nodes,
|
||||
and they are general enough that I've included. Michael's use-case
|
||||
examples follow: <example title="Node convenience functions">#
|
||||
Starting with +root_node+, we recursively look for a node with the
|
||||
given # +tag+, the given +attributes+ (a Hash) and whoose text equals
|
||||
or matches the # +text+ string or regular expression. # # To find the
|
||||
following node: # # <td class='abc'>text</td> # # We use:
|
||||
# # find_node(root, 'td', {'class' => 'abc'}, "text") # # Returns
|
||||
+nil+ if no matching node was found. def find_node(root_node, tag,
|
||||
attributes, text) root_node.find_first_recursive {|node| node.name ==
|
||||
tag and attributes.all? {|attr, val| node.attributes[attr] == val} and
|
||||
text === node.text } end # # Extract specific columns (specified by
|
||||
the position of it's corresponding # header column) from a table. # #
|
||||
Given the following table: # # <table> # <tr> #
|
||||
<td>A</td> # <td>B</td> #
|
||||
<td>C</td> # </tr> # <tr> #
|
||||
<td>A.1</td> # <td>B.1</td> #
|
||||
<td>C.1</td> # </tr> # <tr> #
|
||||
<td>A.2</td> # <td>B.2</td> #
|
||||
<td>C.2</td> # </tr> # </table> # # To extract
|
||||
the first (A) and last (C) column: # # extract_from_table(root_node,
|
||||
["A", "C"]) # # And you get this as result: # # [ # ["A.1", "C.1"], #
|
||||
["A.2", "C.2"] # ] # def extract_from_table(root_node, headers) #
|
||||
extract and collect all header nodes header_nodes = headers.collect {
|
||||
|header| find_node(root_node, 'td', {}, header) } raise "some headers
|
||||
not found" if header_nodes.compact.size < headers.size # assert
|
||||
that all headers have the same parent 'header_row', which is the row #
|
||||
in which the header_nodes are contained. 'table' is the surrounding
|
||||
table tag. header_row = header_nodes.first.parent table =
|
||||
header_row.parent raise "different parents" unless header_nodes.all?
|
||||
{|n| n.parent == header_row} # we now iterate over all rows in the
|
||||
table that follows the header_row. # for each row we collect the
|
||||
elements at the same positions as the header_nodes. # this is what we
|
||||
finally return from the method. (header_row.index_in_parent+1 ..
|
||||
table.elements.size).collect do |inx| row = table.elements[inx]
|
||||
header_nodes.collect { |n| row.elements[ n.index_in_parent ].text }
|
||||
end end</example></p>
|
||||
</subsection>
|
||||
|
||||
<subsection title="Conclusion">
|
||||
<p>This isn't everything there is to REXML, but it should be enough to
|
||||
get started. Check the <link href="../doc/index.html">API
|
||||
documentation</link><footnote>You must generate the API documentation
|
||||
with rdoc or download the API documentation from the REXML website for
|
||||
this documentation.</footnote> for particulars and more examples.
|
||||
There are plenty of unit tests in the <code>test/</code> directory,
|
||||
and these are great sources of working examples.</p>
|
||||
</subsection>
|
||||
</general>
|
||||
</overview>
|
||||
|
||||
<credits>
|
||||
<p>Among the people who've contributed to this document are:</p>
|
||||
|
||||
<list>
|
||||
<item><link href="mailto:deicher@sandia.gov">Eichert, Diana</link> (bug
|
||||
fix)</item>
|
||||
</list>
|
||||
</credits>
|
||||
</documentation>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<root a="1" _a="2">
|
||||
<b>1</b>
|
||||
<_b>2</_b>
|
||||
</root>
|
||||
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче