summaryrefslogtreecommitdiff
path: root/lib/rss
diff options
context:
space:
mode:
author(no author) <(no author)@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-04-06 08:02:56 +0000
committer(no author) <(no author)@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-04-06 08:02:56 +0000
commit5923a2c0e770515a9c7144c50e09a32350695b00 (patch)
tree66882c2ac60d7d1b25bb49ee476cb829695aa906 /lib/rss
parent8e773df6d3b68caf2e56e6c75a8e48bf2ccc1bd3 (diff)
This commit was manufactured by cvs2svn to create branch 'ruby_1_8'.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@6109 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rss')
-rw-r--r--lib/rss/0.9.rb409
-rw-r--r--lib/rss/2.0.rb147
-rw-r--r--lib/rss/converter.rb154
-rw-r--r--lib/rss/syndication.rb83
-rw-r--r--lib/rss/taxonomy.rb32
-rw-r--r--lib/rss/utils.rb17
-rw-r--r--lib/rss/xml-stylesheet.rb94
-rw-r--r--lib/rss/xmlscanner.rb102
8 files changed, 1038 insertions, 0 deletions
diff --git a/lib/rss/0.9.rb b/lib/rss/0.9.rb
new file mode 100644
index 0000000000..bb3cc23beb
--- /dev/null
+++ b/lib/rss/0.9.rb
@@ -0,0 +1,409 @@
+require "rss/parser"
+
+module RSS
+
+ module RSS09
+ NSPOOL = {}
+ ELEMENTS = []
+ end
+
+ class Rss < Element
+
+ include RSS09
+ include RootElementMixin
+ include XMLStyleSheetMixin
+
+ [
+ ["channel", nil],
+ ].each do |tag, occurs|
+ install_model(tag, occurs)
+ end
+
+ %w(channel).each do |x|
+ install_have_child_element(x)
+ end
+
+ attr_accessor :rss_version, :version, :encoding, :standalone
+
+ def initialize(rss_version, version=nil, encoding=nil, standalone=nil)
+ super
+ end
+
+ def items
+ if @channel
+ @channel.items
+ else
+ []
+ end
+ end
+
+ def image
+ if @channel
+ @channel.image
+ else
+ nil
+ end
+ end
+
+ def to_s(convert=true)
+ rv = <<-EOR
+#{xmldecl}
+#{xml_stylesheet_pi}<rss version="#{@rss_version}"#{ns_declaration}>
+#{channel_element(false)}
+#{other_element(false, "\t")}
+</rss>
+EOR
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ end
+
+ private
+ def children
+ [@channel]
+ end
+
+ class Channel < Element
+
+ include RSS09
+
+ [
+ ["title", nil],
+ ["link", nil],
+ ["description", nil],
+ ["language", nil],
+ ["copyright", "?"],
+ ["managingEditor", "?"],
+ ["webMaster", "?"],
+ ["rating", "?"],
+ ["docs", "?"],
+ ["skipDays", "?"],
+ ["skipHours", "?"],
+ ].each do |x, occurs|
+ install_text_element(x)
+ install_model(x, occurs)
+ end
+
+ [
+ ["pubDate", "?"],
+ ["lastBuildDate", "?"],
+ ].each do |x, occurs|
+ install_date_element(x, 'rfc822')
+ install_model(x, occurs)
+ end
+
+ [
+ ["image", nil],
+ ["textInput", "?"],
+ ["cloud", "?"]
+ ].each do |x, occurs|
+ install_have_child_element(x)
+ install_model(x, occurs)
+ end
+
+ [
+ ["item", "*"]
+ ].each do |x, occurs|
+ install_have_children_element(x)
+ install_model(x, occurs)
+ end
+
+ def initialize()
+ super()
+ end
+
+ def to_s(convert=true)
+ rv = <<-EOT
+ <channel>
+ #{title_element(false)}
+ #{link_element(false)}
+ #{description_element(false)}
+ #{language_element(false)}
+ #{copyright_element(false)}
+ #{managingEditor_element(false)}
+ #{webMaster_element(false)}
+ #{rating_element(false)}
+ #{pubDate_element(false)}
+ #{lastBuildDate_element(false)}
+ #{docs_element(false)}
+ #{skipDays_element(false)}
+ #{skipHours_element(false)}
+ #{image_element(false)}
+#{item_elements(false)}
+ #{textInput_element(false)}
+#{other_element(false, "\t\t")}
+ </channel>
+EOT
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ end
+
+ private
+ def children
+ [@image, @textInput, @cloud, *@item]
+ end
+
+ class Image < Element
+
+ include RSS09
+
+ %w(url title link width height description).each do |x|
+ install_text_element(x)
+ end
+
+ def to_s(convert=true)
+ rv = <<-EOT
+ <image>
+ #{url_element(false)}
+ #{title_element(false)}
+ #{link_element(false)}
+ #{width_element(false)}
+ #{height_element(false)}
+ #{description_element(false)}
+#{other_element(false, "\t\t\t\t")}
+ </image>
+EOT
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ end
+
+ end
+
+ class Cloud < Element
+
+ include RSS09
+
+ [
+ ["domain", nil, false],
+ ["port", nil, false],
+ ["path", nil, false],
+ ["registerProcedure", nil, false],
+ ["protocol", nil ,false],
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required)
+ end
+
+ def to_s(convert=true)
+ rv = <<-EOT
+ <cloud
+ domain="#{h @domain}"
+ port="#{h @port}"
+ path="#{h @path}"
+ registerProcedure="#{h @registerProcedure}"
+ protocol="#{h @protocol}"/>
+EOT
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ end
+
+ end
+
+ class Item < Element
+
+ include RSS09
+
+ %w(title link description author comments).each do |x|
+ install_text_element(x)
+ end
+
+ %w(category source enclosure).each do |x|
+ install_have_child_element(x)
+ end
+
+ [
+ ["title", '?'],
+ ["link", '?'],
+ ["description", '?'],
+ ["author", '?'],
+ ["comments", '?'],
+ ["category", '?'],
+ ["source", '?'],
+ ["enclosure", '?'],
+ ].each do |tag, occurs|
+ install_model(tag, occurs)
+ end
+
+ def to_s(convert=true)
+ rv = <<-EOT
+ <item>
+ #{title_element(false)}
+ #{link_element(false)}
+ #{description_element(false)}
+ #{author_element(false)}
+ #{category_element(false)}
+ #{comments_element(false)}
+ #{enclosure_element(false)}
+ #{source_element(false)}
+#{other_element(false, "\t\t\t\t")}
+ </item>
+EOT
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ end
+
+ class Source < Element
+
+ include RSS09
+
+ [
+ ["url", nil, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required)
+ end
+
+ content_setup
+
+ def initialize(url=nil, content=nil)
+ super()
+ @url = url
+ @content = content
+ end
+
+ def to_s(convert=true)
+ if @url
+ rv = %Q! <source url="#{@url}">!
+ rv << %Q!#{@content}</source>!
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ else
+ ''
+ end
+ end
+
+ private
+ def _attrs
+ [
+ ["url", true]
+ ]
+ end
+
+ end
+
+ class Enclosure < Element
+
+ include RSS09
+
+ [
+ ["url", nil, true],
+ ["length", nil, true],
+ ["type", nil, true],
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required)
+ end
+
+ def initialize(url=nil, length=nil, type=nil)
+ super()
+ @url = url
+ @length = length
+ @type = type
+ end
+
+ def to_s(convert=true)
+ if @url and @length and @type
+ rv = %Q!<enclosure url="#{h @url}" !
+ rv << %Q!length="#{h @length}" type="#{h @type}"/>!
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ else
+ ''
+ end
+ end
+
+ private
+ def _attrs
+ [
+ ["url", true],
+ ["length", true],
+ ["type", true],
+ ]
+ end
+
+ end
+
+ class Category < Element
+
+ include RSS09
+
+ [
+ ["domain", nil, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required)
+ end
+
+ content_setup
+
+ def initialize(domain=nil, content=nil)
+ super()
+ @domain = domain
+ @content = content
+ end
+
+ def to_s(convert=true)
+ if @domain
+ rv = %Q!<category domain="#{h @domain}">!
+ rv << %Q!#{h @content}</category>!
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ else
+ ''
+ end
+ end
+
+ private
+ def _attrs
+ [
+ ["domain", true]
+ ]
+ end
+
+ end
+
+ end
+
+ class TextInput < Element
+
+ include RSS09
+
+ %w(title description name link).each do |x|
+ install_text_element(x)
+ end
+
+ def to_s(convert=true)
+ rv = <<-EOT
+ <textInput>
+ #{title_element(false)}
+ #{description_element(false)}
+ #{name_element(false)}
+ #{link_element(false)}
+#{other_element(false, "\t\t\t\t")}
+ </textInput>
+EOT
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ end
+
+ end
+
+ end
+
+ end
+
+ RSS09::ELEMENTS.each do |x|
+ BaseListener.install_get_text_element(x, nil, "#{x}=")
+ end
+
+ module ListenerMixin
+ private
+ def start_rss(tag_name, prefix, attrs, ns)
+ check_ns(tag_name, prefix, ns, nil)
+
+ @rss = Rss.new(attrs['version'], @version, @encoding, @standalone)
+ @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
diff --git a/lib/rss/2.0.rb b/lib/rss/2.0.rb
new file mode 100644
index 0000000000..c83fb2c393
--- /dev/null
+++ b/lib/rss/2.0.rb
@@ -0,0 +1,147 @@
+require "rss/0.9"
+
+module RSS
+
+ class Rss
+
+# URI = "http://backend.userland.com/rss2"
+
+# install_ns('', URI)
+
+# def self.required_uri
+# URI
+# end
+
+ class Channel
+
+# def self.required_uri
+# URI
+# end
+
+ %w(generator ttl).each do |x|
+ install_text_element(x)
+ end
+
+ %w(category).each do |x|
+ install_have_child_element(x)
+ end
+
+ [
+ ["image", "?"],
+ ].each do |x, occurs|
+ install_model(x, occurs)
+ end
+
+ def other_element(convert, indent='')
+ rv = <<-EOT
+#{indent}#{category_element(convert)}
+#{indent}#{generator_element(convert)}
+#{indent}#{ttl_element(convert)}
+EOT
+ rv << super
+ end
+
+ Category = Item::Category
+# def Category.required_uri
+# URI
+# end
+
+ class Item
+
+# def self.required_uri
+# URI
+# end
+
+ [
+ ["pubDate", '?'],
+ ].each do |x, occurs|
+ install_date_element(x, 'rfc822')
+ install_model(x, occurs)
+ end
+
+ [
+ ["guid", '?'],
+ ].each do |x, occurs|
+ install_have_child_element(x)
+ install_model(x, occurs)
+ end
+
+ def other_element(convert, indent='')
+ rv = <<-EOT
+#{indent}#{pubDate_element(false)}
+#{indent}#{guid_element(false)}
+EOT
+ rv << super
+ end
+
+ class Guid < Element
+
+ include RSS09
+
+# def self.required_uri
+# URI
+# end
+
+ [
+ ["isPermaLink", nil, false]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required)
+ end
+
+ content_setup
+
+ def initialize(isPermaLink=nil, content=nil)
+ super()
+ @isPermaLink = isPermaLink
+ @content = content
+ end
+
+ def to_s(convert=true)
+ if @content
+ rv = %Q!<guid!
+ rv << %Q! isPermaLink="#{h @isPermaLink}"! if @isPermaLink
+ rv << %Q!>#{h @content}</guid>!
+ rv = @converter.convert(rv) if convert and @converter
+ rv
+ else
+ ''
+ end
+ end
+
+ private
+ def _attrs
+ [
+ ["isPermaLink", false]
+ ]
+ end
+
+ end
+
+ end
+
+ end
+
+ end
+
+ RSS09::ELEMENTS.each do |x|
+# BaseListener.install_get_text_element(x, Rss::URI, "#{x}=")
+ BaseListener.install_get_text_element(x, nil, "#{x}=")
+ end
+
+ module ListenerMixin
+ private
+ alias start_rss09 start_rss
+ def start_rss(tag_name, prefix, attrs, ns)
+# check_ns(tag_name, prefix, ns, Rss::URI)
+
+ @rss = Rss.new(attrs['version'], @version, @encoding, @standalone)
+ @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
diff --git a/lib/rss/converter.rb b/lib/rss/converter.rb
new file mode 100644
index 0000000000..9a62431a9e
--- /dev/null
+++ b/lib/rss/converter.rb
@@ -0,0 +1,154 @@
+require "rss/utils"
+
+module RSS
+
+ class Converter
+
+ include Utils
+
+ def initialize(to_enc, from_enc=nil)
+ to_enc = to_enc.downcase.gsub(/-/, '_')
+ from_enc ||= 'utf-8'
+ from_enc = from_enc.downcase.gsub(/-/, '_')
+ if to_enc == from_enc
+ def_same_enc()
+ else
+ if respond_to?("def_to_#{to_enc}_from_#{from_enc}")
+ send("def_to_#{to_enc}_from_#{from_enc}")
+ else
+ def_else_enc(to_enc, from_enc)
+ end
+ end
+ end
+
+ def convert(value)
+ value
+ end
+
+ def def_convert(depth=0)
+ instance_eval(<<-EOC, *get_file_and_line_from_caller(depth))
+ def convert(value)
+ if value.kind_of?(String)
+ #{yield('value')}
+ else
+ value
+ end
+ end
+ EOC
+ end
+
+ def def_iconv_convert(to_enc, from_enc, depth=0)
+ begin
+ require "iconv"
+ def_convert(depth+1) do |value|
+ <<-EOC
+ @iconv ||= Iconv.new("#{to_enc}", "#{from_enc}")
+ begin
+ @iconv.iconv(#{value})
+ rescue Iconv::Failure
+ raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}")
+ end
+ EOC
+ end
+ rescue LoadError, ArgumentError, SystemCallError
+ raise UnknownConversionMethodError.new(to_enc, from_enc)
+ end
+ end
+
+ def def_else_enc(to_enc, from_enc)
+ raise UnknownConversionMethodError.new(to_enc, from_enc)
+ end
+
+ def def_same_enc()
+ def_convert do |value|
+ value
+ end
+ end
+
+ def def_uconv_convert_if_can(meth, to_enc, from_enc)
+ begin
+ require "uconv"
+ def_convert(1) do |value|
+ <<-EOC
+ begin
+ Uconv.#{meth}(#{value})
+ rescue Uconv::Error
+ raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}")
+ end
+ EOC
+ end
+ rescue LoadError
+ def_iconv_convert(to_enc, from_enc, 1)
+ end
+ end
+
+ def def_to_euc_jp_from_utf_8
+ def_uconv_convert_if_can('u8toeuc', 'EUC-JP', 'UTF-8')
+ end
+
+ def def_to_utf_8_from_euc_jp
+ def_uconv_convert_if_can('euctou8', 'UTF-8', 'EUC-JP')
+ end
+
+ def def_to_shift_jis_from_utf_8
+ def_uconv_convert_if_can('u8tosjis', 'Shift_JIS', 'UTF-8')
+ end
+
+ def def_to_utf_8_from_shift_jis
+ def_uconv_convert_if_can('sjistou8', 'UTF-8', 'Shift_JIS')
+ end
+
+ def def_to_euc_jp_from_shift_jis
+ require "nkf"
+ def_convert do |value|
+ "NKF.nkf('-Se', #{value})"
+ end
+ end
+
+ def def_to_shift_jis_from_euc_jp
+ require "nkf"
+ def_convert do |value|
+ "NKF.nkf('-Es', #{value})"
+ end
+ end
+
+ def def_to_euc_jp_from_iso_2022_jp
+ require "nkf"
+ def_convert do |value|
+ "NKF.nkf('-Je', #{value})"
+ end
+ end
+
+ def def_to_iso_2022_jp_from_euc_jp
+ require "nkf"
+ def_convert do |value|
+ "NKF.nkf('-Ej', #{value})"
+ end
+ end
+
+ def def_to_utf_8_from_iso_8859_1
+ def_convert do |value|
+ "#{value}.unpack('C*').pack('U*')"
+ end
+ end
+
+ def def_to_iso_8859_1_from_utf_8
+ def_convert do |value|
+ <<-EOC
+ array_utf8 = #{value}.unpack('U*')
+ array_enc = []
+ array_utf8.each do |num|
+ if num <= 0xFF
+ array_enc << num
+ else
+ array_enc.concat "&\#\#{num};".unpack('C*')
+ end
+ end
+ array_enc.pack('C*')
+ EOC
+ end
+ end
+
+ end
+
+end
diff --git a/lib/rss/syndication.rb b/lib/rss/syndication.rb
new file mode 100644
index 0000000000..8c10688b57
--- /dev/null
+++ b/lib/rss/syndication.rb
@@ -0,0 +1,83 @@
+require "rss/1.0"
+
+module RSS
+
+ SY_PREFIX = 'sy'
+ SY_URI = "http://purl.org/rss/1.0/modules/syndication/"
+
+ RDF.install_ns(SY_PREFIX, SY_URI)
+
+ module SyndicationModel
+
+ extend BaseModel
+
+ ELEMENTS = []
+
+ def self.included(mod)
+ mod.module_eval(<<-EOC)
+ %w(updatePeriod updateFrequency).each do |x|
+ install_text_element("\#{SY_PREFIX}_\#{x}")
+ end
+
+ %w(updateBase).each do |x|
+ install_date_element("\#{SY_PREFIX}_\#{x}", 'w3cdtf', x)
+ end
+
+ alias_method(:_sy_updatePeriod=, :sy_updatePeriod=)
+ def sy_updatePeriod=(new_value)
+ new_value = new_value.strip
+ validate_sy_updatePeriod(new_value) if @do_validate
+ self._sy_updatePeriod = new_value
+ end
+
+ alias_method(:_sy_updateFrequency=, :sy_updateFrequency=)
+ def sy_updateFrequency=(new_value)
+ new_value = new_value.strip
+ validate_sy_updateFrequency(new_value) if @do_validate
+ self._sy_updateFrequency = new_value.to_i
+ end
+ EOC
+ end
+
+ def sy_validate(tags)
+ counter = {}
+ ELEMENTS.each do |x|
+ counter[x] = 0
+ end
+
+ tags.each do |tag|
+ key = "#{SY_PREFIX}_#{tag}"
+ raise UnknownTagError.new(tag, SY_URI) unless counter.has_key?(key)
+ counter[key] += 1
+ raise TooMuchTagError.new(tag, tag_name) if counter[key] > 1
+ end
+ end
+
+ private
+ SY_UPDATEPERIOD_AVAILABLE_VALUES = %w(hourly daily weekly monthly yearly)
+ def validate_sy_updatePeriod(value)
+ unless SY_UPDATEPERIOD_AVAILABLE_VALUES.include?(value)
+ raise NotAvailableValueError.new("updatePeriod", value)
+ end
+ end
+
+ SY_UPDATEFREQUENCY_AVAILABLE_RE = /\A\s*\+?\d+\s*\z/
+ def validate_sy_updateFrequency(value)
+ if SY_UPDATEFREQUENCY_AVAILABLE_RE !~ value
+ raise NotAvailableValueError.new("updateFrequency", value)
+ end
+ end
+
+ end
+
+ class RDF
+ class Channel; include SyndicationModel; end
+ end
+
+ prefix_size = SY_PREFIX.size + 1
+ SyndicationModel::ELEMENTS.uniq!
+ SyndicationModel::ELEMENTS.each do |x|
+ BaseListener.install_get_text_element(x[prefix_size..-1], SY_URI, "#{x}=")
+ end
+
+end
diff --git a/lib/rss/taxonomy.rb b/lib/rss/taxonomy.rb
new file mode 100644
index 0000000000..5b11d338e0
--- /dev/null
+++ b/lib/rss/taxonomy.rb
@@ -0,0 +1,32 @@
+# Experimental
+
+require "rss/1.0"
+
+module RSS
+
+ TAXO_PREFIX = "taxo"
+ TAXO_NS = "http://purl.org/rss/1.0/modules/taxonomy/"
+
+ Element.install_ns(TAXO_PREFIX, TAXO_NS)
+
+ TAXO_ELEMENTS = []
+
+ %w(link).each do |x|
+ if const_defined? :Listener
+ Listener.install_get_text_element(x, TAXO_NS, "#{TAXO_PREFIX}_#{x}=")
+ end
+ TAXO_ELEMENTS << "#{TAXO_PREFIX}_#{x}"
+ end
+
+ module TaxonomyModel
+ attr_writer(*%w(title description creator subject publisher
+ contributor date format identifier source
+ language relation coverage rights).collect{|x| "#{TAXO_PREFIX}_#{x}"})
+ end
+
+ class Channel; extend TaxonomyModel; end
+ class Item; extend TaxonomyModel; end
+ class Image; extend TaxonomyModel; end
+ class TextInput; extend TaxonomyModel; end
+
+end
diff --git a/lib/rss/utils.rb b/lib/rss/utils.rb
new file mode 100644
index 0000000000..ae6f69bcf1
--- /dev/null
+++ b/lib/rss/utils.rb
@@ -0,0 +1,17 @@
+module RSS
+
+ module Utils
+
+ def get_file_and_line_from_caller(i=0)
+ file, line, = caller[i].split(':')
+ [file, line.to_i]
+ end
+
+ def html_escape(s)
+ s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
+ end
+ alias h html_escape
+
+ end
+
+end
diff --git a/lib/rss/xml-stylesheet.rb b/lib/rss/xml-stylesheet.rb
new file mode 100644
index 0000000000..7862c4f278
--- /dev/null
+++ b/lib/rss/xml-stylesheet.rb
@@ -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
diff --git a/lib/rss/xmlscanner.rb b/lib/rss/xmlscanner.rb
new file mode 100644
index 0000000000..4ab997062d
--- /dev/null
+++ b/lib/rss/xmlscanner.rb
@@ -0,0 +1,102 @@
+require 'xmlscan/scanner'
+
+module RSS
+
+ class XMLScanParser < BaseParser
+
+ private
+ def listener
+ XMLScanListener
+ end
+
+ def _parse
+ begin
+ XMLScan::XMLScanner.new(@listener).parse(@rss)
+ rescue XMLScan::Error => e
+ raise NotWellFormedError.new(e.lineno){e.message}
+ end
+ end
+
+ end
+
+ class XMLScanListener < BaseListener
+
+ include XMLScan::Visitor
+ include ListenerMixin
+
+ ENTITIES = {
+ 'lt' => '<',
+ 'gt' => '>',
+ 'amp' => '&',
+ 'quot' => '"',
+ 'apos' => '\''
+ }
+
+ def on_xmldecl_version(str)
+ @version = str
+ end
+
+ def on_xmldecl_encoding(str)
+ @encoding = str
+ end
+
+ def on_xmldecl_standalone(str)
+ @standalone = str
+ end
+
+ def on_xmldecl_end
+ xmldecl(@version, @encoding, @standalone)
+ end
+
+ alias_method(:on_pi, :instruction)
+ alias_method(:on_chardata, :text)
+ alias_method(:on_cdata, :text)
+
+ def on_etag(name)
+ tag_end(name)
+ end
+
+ def on_entityref(ref)
+ text(ENTITIES[ref])
+ end
+
+ def on_charref(code)
+ text([code].pack('U'))
+ end
+
+ alias_method(:on_charref_hex, :on_charref)
+
+ def on_stag(name)
+ @attrs = {}
+ end
+
+ def on_attribute(name)
+ @attrs[name] = @current_attr = ''
+ end
+
+ def on_attr_value(str)
+ @current_attr << str
+ end
+
+ def on_attr_entityref(ref)
+ @current_attr << ENTITIES[ref]
+ end
+
+ def on_attr_charref(code)
+ @current_attr << [code].pack('U')
+ end
+
+ alias_method(:on_attr_charref_hex, :on_attr_charref)
+
+ def on_stag_end(name)
+ tag_start(name, @attrs)
+ end
+
+ def on_stag_end_empty(name)
+ tag_start(name, @attrs)
+ tag_end(name)
+ end
+
+ end
+
+end