* test/rss/test_xml-stylesheet.rb: added tests for xml-stylesheet.

* lib/rss/xml-stylesheet.rb: added xml-stylesheet parsing
  function.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5989 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
kou 2004-03-21 09:20:47 +00:00
Родитель 2639d6dc80
Коммит 7ead69e5b3
15 изменённых файлов: 385 добавлений и 85 удалений

Просмотреть файл

@ -1,3 +1,10 @@
Sun Mar 21 18:15:29 2004 Kouhei Sutou <kou@cozmixng.org>
* test/rss/test_xml-stylesheet.rb: added tests for xml-stylesheet.
* lib/rss/xml-stylesheet.rb: added xml-stylesheet parsing
function.
Sat Mar 20 23:51:03 2004 WATANABE Hirofumi <eban@ruby-lang.org> Sat Mar 20 23:51:03 2004 WATANABE Hirofumi <eban@ruby-lang.org>
* eval.c (rb_require_safe): preserve old ruby_errinfo. * eval.c (rb_require_safe): preserve old ruby_errinfo.

Просмотреть файл

@ -10,6 +10,8 @@ module RSS
class Rss < Element class Rss < Element
include RSS09 include RSS09
include RootElementMixin
include XMLStyleSheetMixin
[ [
["channel", nil], ["channel", nil],
@ -24,16 +26,7 @@ module RSS
attr_accessor :rss_version, :version, :encoding, :standalone attr_accessor :rss_version, :version, :encoding, :standalone
def initialize(rss_version, version=nil, encoding=nil, standalone=nil) def initialize(rss_version, version=nil, encoding=nil, standalone=nil)
super() super
@rss_version = rss_version
@version = version || '1.0'
@encoding = encoding
@standalone = standalone
end
def output_encoding=(enc)
@output_encoding = enc
self.converter = Converter.new(@output_encoding, @encoding)
end end
def items def items
@ -55,7 +48,7 @@ module RSS
def to_s(convert=true) def to_s(convert=true)
rv = <<-EOR rv = <<-EOR
#{xmldecl} #{xmldecl}
<rss version="#{@rss_version}"#{ns_declaration}> #{xml_stylesheet_pi}<rss version="#{@rss_version}"#{ns_declaration}>
#{channel_element(false)} #{channel_element(false)}
#{other_element(false, "\t")} #{other_element(false, "\t")}
</rss> </rss>
@ -65,25 +58,6 @@ EOR
end end
private private
def xmldecl
rv = "<?xml version='#{@version}'"
if @output_encoding or @encoding
rv << " encoding='#{@output_encoding or @encoding}'"
end
rv << " standalone='#{@standalone}'" if @standalone
rv << '?>'
rv
end
def ns_declaration
rv = ''
NSPOOL.each do |prefix, uri|
prefix = ":#{prefix}" unless prefix.empty?
rv << %Q|\n\txmlns#{prefix}="#{uri}"|
end
rv
end
def children def children
[@channel] [@channel]
end end
@ -423,6 +397,7 @@ EOT
check_ns(tag_name, prefix, ns, nil) check_ns(tag_name, prefix, ns, nil)
@rss = Rss.new(attrs['version'], @version, @encoding, @standalone) @rss = Rss.new(attrs['version'], @version, @encoding, @standalone)
@rss.xml_stylesheets = @xml_stylesheets
@last_element = @rss @last_element = @rss
@proc_stack.push Proc.new { |text, tags| @proc_stack.push Proc.new { |text, tags|
@rss.validate_for_stream(tags) if @do_validate @rss.validate_for_stream(tags) if @do_validate

Просмотреть файл

@ -10,6 +10,8 @@ module RSS
class RDF < Element class RDF < Element
include RSS10 include RSS10
include RootElementMixin
include XMLStyleSheetMixin
class << self class << self
@ -45,23 +47,13 @@ module RSS
attr_accessor :rss_version, :version, :encoding, :standalone attr_accessor :rss_version, :version, :encoding, :standalone
def initialize(version=nil, encoding=nil, standalone=nil) def initialize(version=nil, encoding=nil, standalone=nil)
super() super('1.0', version, encoding, standalone)
@rss_version = '1.0'
@version = version || '1.0'
@encoding = encoding
@standalone = standalone
@output_encoding = nil
end
def output_encoding=(enc)
@output_encoding = enc
self.converter = Converter.new(@output_encoding, @encoding)
end end
def to_s(convert=true) def to_s(convert=true)
rv = <<-EORDF rv = <<-EORDF
#{xmldecl} #{xmldecl}
<#{PREFIX}:RDF#{ns_declaration}> #{xml_stylesheet_pi}<#{PREFIX}:RDF#{ns_declaration}>
#{channel_element(false)} #{channel_element(false)}
#{image_element(false)} #{image_element(false)}
#{item_elements(false)} #{item_elements(false)}
@ -74,25 +66,6 @@ EORDF
end end
private private
def xmldecl
rv = %Q[<?xml version="#{@version}"]
if @output_encoding or @encoding
rv << %Q[ encoding="#{@output_encoding or @encoding}"]
end
rv << %Q[ standalone="#{@standalone}"] if @standalone
rv << '?>'
rv
end
def ns_declaration
rv = ''
self.class::NSPOOL.each do |prefix, uri|
prefix = ":#{prefix}" unless prefix.empty?
rv << %Q|\n\txmlns#{prefix}="#{html_escape(uri)}"|
end
rv
end
def rdf_validate(tags) def rdf_validate(tags)
_validate(tags, []) _validate(tags, [])
end end
@ -647,4 +620,19 @@ EOT
BaseListener.install_get_text_element(x, URI, "#{x}=") BaseListener.install_get_text_element(x, URI, "#{x}=")
end end
module ListenerMixin
private
def start_RDF(tag_name, prefix, attrs, ns)
check_ns(tag_name, prefix, ns, RDF::URI)
@rss = RDF.new(@version, @encoding, @standalone)
@rss.do_validate = @do_validate
@rss.xml_stylesheets = @xml_stylesheets
@last_element = @rss
@proc_stack.push Proc.new { |text, tags|
@rss.validate_for_stream(tags) if @do_validate
}
end
end
end end

Просмотреть файл

@ -135,6 +135,7 @@ EOT
# check_ns(tag_name, prefix, ns, Rss::URI) # check_ns(tag_name, prefix, ns, Rss::URI)
@rss = Rss.new(attrs['version'], @version, @encoding, @standalone) @rss = Rss.new(attrs['version'], @version, @encoding, @standalone)
@rss.xml_stylesheets = @xml_stylesheets
@last_element = @rss @last_element = @rss
@proc_stack.push Proc.new { |text, tags| @proc_stack.push Proc.new { |text, tags|
@rss.validate_for_stream(tags) if @do_validate @rss.validate_for_stream(tags) if @do_validate

Просмотреть файл

@ -25,8 +25,8 @@ module RSS
value value
end end
def def_convert() def def_convert(depth=0)
instance_eval(<<-EOC, *get_file_and_line_from_caller(0)) instance_eval(<<-EOC, *get_file_and_line_from_caller(depth))
def convert(value) def convert(value)
if value.kind_of?(String) if value.kind_of?(String)
#{yield('value')} #{yield('value')}
@ -37,10 +37,10 @@ module RSS
EOC EOC
end end
def def_iconv_convert(to_enc, from_enc) def def_iconv_convert(to_enc, from_enc, depth=0)
begin begin
require "iconv" require "iconv"
def_convert do |value| def_convert(depth+1) do |value|
<<-EOC <<-EOC
@iconv ||= Iconv.new("#{to_enc}", "#{from_enc}") @iconv ||= Iconv.new("#{to_enc}", "#{from_enc}")
begin begin
@ -68,7 +68,7 @@ module RSS
def def_uconv_convert_if_can(meth, to_enc, from_enc) def def_uconv_convert_if_can(meth, to_enc, from_enc)
begin begin
require "uconv" require "uconv"
def_convert do |value| def_convert(1) do |value|
<<-EOC <<-EOC
begin begin
Uconv.#{meth}(#{value}) Uconv.#{meth}(#{value})
@ -78,7 +78,7 @@ module RSS
EOC EOC
end end
rescue LoadError rescue LoadError
def_iconv_convert(to_enc, from_enc) def_iconv_convert(to_enc, from_enc, 1)
end end
end end

Просмотреть файл

@ -191,12 +191,22 @@ module RSS
@proc_stack = [] @proc_stack = []
@last_element = nil @last_element = nil
@version = @encoding = @standalone = nil @version = @encoding = @standalone = nil
@xml_stylesheets = []
end end
def xmldecl(version, encoding, standalone) def xmldecl(version, encoding, standalone)
@version, @encoding, @standalone = version, encoding, standalone @version, @encoding, @standalone = version, encoding, standalone
end end
def instruction(name, content)
if name == "xml-stylesheet"
params = parse_pi_content(content)
if params.has_key?("href")
@xml_stylesheets << XMLStyleSheet.new(*params)
end
end
end
def tag_start(name, attributes) def tag_start(name, attributes)
@text_stack.push('') @text_stack.push('')
@ -204,7 +214,7 @@ module RSS
attrs = {} attrs = {}
attributes.each do |n, v| attributes.each do |n, v|
if n =~ /\Axmlns:?/ if n =~ /\Axmlns:?/
ns[$'] = v # $' is post match ns[$POSTMATCH] = v
else else
attrs[n] = v attrs[n] = v
end end
@ -238,15 +248,13 @@ module RSS
private private
def start_RDF(tag_name, prefix, attrs, ns) CONTENT_PATTERN = /\s*([^=]+)=(["'])([^\2]+?)\2/
check_ns(tag_name, prefix, ns, RDF::URI) def parse_pi_content(content)
params = {}
@rss = RDF.new(@version, @encoding, @standalone) content.scan(CONTENT_PATTERN) do |name, quote, value|
@rss.do_validate = @do_validate params[name] = value
@last_element = @rss end
@proc_stack.push Proc.new { |text, tags| params
@rss.validate_for_stream(tags) if @do_validate
}
end end
def start_else_element(local, prefix, attrs, ns) def start_else_element(local, prefix, attrs, ns)

Просмотреть файл

@ -1,7 +1,9 @@
require "time" require "time"
require "English"
require "rss/utils" require "rss/utils"
require "rss/converter" require "rss/converter"
require "rss/xml-stylesheet"
module RSS module RSS
@ -365,7 +367,6 @@ EOC
def initialize(do_validate=true) def initialize(do_validate=true)
@converter = nil @converter = nil
@output_encoding = nil
@do_validate = do_validate @do_validate = do_validate
initialize_variables initialize_variables
end end
@ -561,4 +562,44 @@ EOC
end end
module RootElementMixin
attr_reader :output_encoding
def initialize(rss_version, version=nil, encoding=nil, standalone=nil)
super()
@rss_version = rss_version
@version = version || '1.0'
@encoding = encoding
@standalone = standalone
@output_encoding = nil
end
def output_encoding=(enc)
@output_encoding = enc
self.converter = Converter.new(@output_encoding, @encoding)
end
private
def xmldecl
rv = %Q[<?xml version="#{@version}"]
if @output_encoding or @encoding
rv << %Q[ encoding="#{@output_encoding or @encoding}"]
end
rv << %Q[ standalone="#{@standalone}"] if @standalone
rv << '?>'
rv
end
def ns_declaration
rv = ''
self.class::NSPOOL.each do |prefix, uri|
prefix = ":#{prefix}" unless prefix.empty?
rv << %Q|\n\txmlns#{prefix}="#{html_escape(uri)}"|
end
rv
end
end
end end

Просмотреть файл

@ -3,10 +3,8 @@ module RSS
module Utils module Utils
def get_file_and_line_from_caller(i=0) def get_file_and_line_from_caller(i=0)
tmp = caller[i].split(':') file, line, = caller[i].split(':')
line = tmp.pop.to_i [file, line.to_i]
file = tmp.join(':')
[file, line]
end end
def html_escape(s) def html_escape(s)

94
lib/rss/xml-stylesheet.rb Normal file
Просмотреть файл

@ -0,0 +1,94 @@
require "rss/utils"
module RSS
module XMLStyleSheetMixin
attr_accessor :xml_stylesheets
def initialize(*args)
super
@xml_stylesheets = []
end
private
def xml_stylesheet_pi
xsss = @xml_stylesheets.collect do |xss|
pi = xss.to_s
pi = nil if /\A\s*\z/ =~ pi
pi
end.compact
xsss.push("") unless xsss.empty?
xsss.join("\n")
end
end
class XMLStyleSheet
include Utils
ATTRIBUTES = %w(href type title media charset alternate)
GUESS_TABLE = {
"xsl" => "text/xsl",
"css" => "text/css",
}
attr_accessor(*ATTRIBUTES)
attr_accessor(:do_validate)
def initialize(*attrs)
@do_validate = true
ATTRIBUTES.each do |attr|
self.send("#{attr}=", nil)
end
vars = ATTRIBUTES.dup
vars.unshift(:do_validate)
attrs.each do |name, value|
if vars.include?(name.to_s)
self.send("#{name}=", value)
end
end
end
def to_s
rv = ""
if @href
rv << %Q[<?xml-stylesheet]
ATTRIBUTES.each do |name|
if self.send(name)
rv << %Q[ #{name}="#{h self.send(name)}"]
end
end
rv << %Q[?>]
end
rv
end
remove_method(:href=)
def href=(value)
@href = value
if @href and @type.nil?
@type = guess_type(@href)
end
@href
end
remove_method(:alternate=)
def alternate=(value)
if value.nil? or /\A(?:yes|no)\z/ =~ value
@alternate = value
else
if @do_validate
args = ["?xml-stylesheet?", %Q[alternate="#{value}"]]
raise NotAvailableValueError.new(*args)
end
end
@alternate
end
private
def guess_type(filename)
/\.([^.]+)/ =~ filename
GUESS_TABLE[$1]
end
end
end

Просмотреть файл

@ -1,3 +1,9 @@
begin
require "xml/parser"
rescue LoadError
require "xmlparser"
end
begin begin
require "xml/encoding-ja" require "xml/encoding-ja"
rescue LoadError rescue LoadError
@ -15,7 +21,7 @@ module RSS
class REXMLLikeXMLParser < ::XML::Parser class REXMLLikeXMLParser < ::XML::Parser
include XML::Encoding_ja include ::XML::Encoding_ja
def listener=(listener) def listener=(listener)
@listener = listener @listener = listener
@ -37,6 +43,10 @@ module RSS
@listener.xmldecl(version, encoding, standalone == 1) @listener.xmldecl(version, encoding, standalone == 1)
end end
def processingInstruction(target, content)
@listener.instruction(target, content)
end
end end
class XMLParserParser < BaseParser class XMLParserParser < BaseParser
@ -51,7 +61,7 @@ module RSS
parser = REXMLLikeXMLParser.new parser = REXMLLikeXMLParser.new
parser.listener = @listener parser.listener = @listener
parser.parse(@rss) parser.parse(@rss)
rescue XMLParserError => e rescue ::XML::Parser::Error => e
raise NotWellFormedError.new(parser.line){e.message} raise NotWellFormedError.new(parser.line){e.message}
end end
end end

Просмотреть файл

@ -48,6 +48,7 @@ module RSS
xmldecl(@version, @encoding, @standalone) xmldecl(@version, @encoding, @standalone)
end end
alias_method(:on_pi, :instruction)
alias_method(:on_chardata, :text) alias_method(:on_chardata, :text)
alias_method(:on_cdata, :text) alias_method(:on_cdata, :text)

Просмотреть файл

@ -100,6 +100,15 @@ EOI
EOT EOT
end end
def make_sample_RDF
make_RDF(<<-EOR)
#{make_channel}
#{make_image}
#{make_item}
#{make_textinput}
EOR
end
def make_Rss2(content=nil, xmlns=[]) def make_Rss2(content=nil, xmlns=[])
<<-EORSS <<-EORSS
#{make_xmldecl} #{make_xmldecl}

Просмотреть файл

@ -85,6 +85,45 @@ module Test
end end
end end
def assert_xml_stylesheet_attrs(xsl, attrs)
_wrap_assertion do
normalized_attrs = {}
attrs.each do |name, value|
normalized_attrs[name.to_s] = value
end
::RSS::XMLStyleSheet::ATTRIBUTES.each do |name|
assert_equal(normalized_attrs[name], xsl.send(name))
end
end
end
def assert_xml_stylesheet(target, xsl, attrs)
_wrap_assertion do
if attrs.has_key?(:href)
if !attrs.has_key?(:type) and attrs.has_key?(:guess_type)
attrs[:type] = attrs[:guess_type]
end
assert_equal("xml-stylesheet", target)
assert_xml_stylesheet_attrs(xsl, attrs)
else
assert_nil(target)
assert_equal("", xsl.to_s)
end
end
end
def assert_xml_stylesheet_pis(attrs_ary)
rdf = ::RSS::RDF.new()
xss_strs = []
attrs_ary.each do |attrs|
xss = ::RSS::XMLStyleSheet.new(*attrs)
xss_strs.push(xss.to_s)
rdf.xml_stylesheets.push(xss)
end
pi_str = rdf.to_s.gsub(/<\?xml .*\n/, "").gsub(/\s*<rdf:RDF.*\z/m, "")
assert_equal(xss_strs.join("\n"), pi_str)
end
end end
end end
end end

Просмотреть файл

@ -38,8 +38,28 @@ class TestCore < Test::Unit::TestCase
end end
def test_channel def test_not_displayed_xml_stylesheets
rdf = RDF.new()
plain_rdf = rdf.to_s
3.times do
rdf.xml_stylesheets.push(XMLStyleSheet.new)
assert_equal(plain_rdf, rdf.to_s)
end
end
def test_xml_stylesheets
[
[{:href => "a.xsl", :type => "text/xsl"}],
[
{:href => "a.xsl", :type => "text/xsl"},
{:href => "a.css", :type => "text/css"},
],
].each do |attrs_ary|
assert_xml_stylesheet_pis(attrs_ary)
end
end
def test_channel
about = "http://hoge.com" about = "http://hoge.com"
title = "fugafuga" title = "fugafuga"
link = "http://hoge.com" link = "http://hoge.com"

Просмотреть файл

@ -0,0 +1,109 @@
# -*- tab-width: 2 -*- vim: ts=2
require "test/unit"
require "rexml/document"
require "rss/1.0"
require "rss/xml-stylesheet"
require "common"
class TestXMLStyleSheet < Test::Unit::TestCase
include TestRSSMixin
def test_accessor
[
{:href => "a.xsl", :type => "text/xsl"},
{:media => "print", :title => "FOO"},
{:charset => "UTF-8", :alternate => "yes"},
].each do |attrs|
assert_xml_stylesheet_attrs(XMLStyleSheet.new(*attrs), attrs)
end
end
def test_to_s
[
{:href => "a.xsl", :type => "text/xsl"},
{:type => "text/xsl"},
{:href => "a.xsl", :guess_type => "text/xsl"},
{:href => "a.css", :type => "text/css"},
{:href => "a.css", :type => "text/xsl",
:guess_type => "text/css"},
{:href => "a.xsl", :type => "text/xsl",
:title => "sample", :media => "printer",
:charset => "UTF-8", :alternate => "yes"},
{:href => "a.css", :guess_type => "text/css",
:alternate => "no"},
{:type => "text/xsl", :title => "sample",
:media => "printer", :charset => "UTF-8",
:alternate => "yes"},
].each do |attrs|
target, contents = parse_pi(XMLStyleSheet.new(*attrs).to_s)
assert_xml_stylesheet(target, XMLStyleSheet.new(*contents), attrs)
end
end
def test_bad_alternate
%w(a ___ ??? BAD_ALTERNATE).each do |value|
xss = XMLStyleSheet.new
assert_raise(NotAvailableValueError) do
xss.alternate = value
end
xss.do_validate = false
assert_nothing_raised do
xss.alternate = value
end
assert_nil(xss.alternate)
end
end
def test_parse
[
[{:href => "a.xsl", :type => "text/xsl"},],
[{:media => "print", :title => "FOO"},],
[{:charset => "UTF-8", :alternate => "yes"},],
[{:href => "a.xsl", :type => "text/xsl"},
{:type => "text/xsl"},
{:href => "a.xsl", :guess_type => "text/xsl"},
{:href => "a.css", :type => "text/css"},
{:href => "a.css", :type => "text/xsl",
:guess_type => "text/css"},
{:href => "a.xsl", :type => "text/xsl",
:title => "sample", :media => "printer",
:charset => "UTF-8", :alternate => "yes"},
{:href => "a.css", :guess_type => "text/css",
:alternate => "no"},
{:type => "text/xsl", :title => "sample",
:media => "printer", :charset => "UTF-8",
:alternate => "yes"},],
].each do |xsss|
doc = REXML::Document.new(make_sample_RDF)
root = doc.root
xsss.each do |xss|
content = xss.collect do |key, name|
%Q[#{key}="#{name}"]
end.join(" ")
pi = REXML::Instruction.new("xml-stylesheet", content)
root.previous_sibling = pi
end
rss = Parser.parse(doc.to_s)
have_href_xsss = xsss.find_all {|xss| xss.has_key?(:href)}
assert_equal(have_href_xsss.size, rss.xml_stylesheets.size)
rss.xml_stylesheets.each_with_index do |stylesheet, i|
target, = parse_pi(stylesheet.to_s)
assert_xml_stylesheet(target, stylesheet, have_href_xsss[i])
end
end
end
def parse_pi(pi)
/\A\s*<\?(\S+)([^(?:\?>)]+)\?>\s*\z/ =~ pi
target = $1
dummy = REXML::Document.new("<dummy #{$2}/>").root
contents = {}
dummy.attributes.each do |name, value|
contents[name] = value
end
[target, contents]
end
end