1# frozen_string_literal: false
2require "time"
3
4class Time
5  class << self
6    unless respond_to?(:w3cdtf)
7      # This method converts a W3CDTF string date/time format to Time object.
8      #
9      # The W3CDTF format is defined here: http://www.w3.org/TR/NOTE-datetime
10      #
11      #   Time.w3cdtf('2003-02-15T13:50:05-05:00')
12      #   # => 2003-02-15 10:50:05 -0800
13      #   Time.w3cdtf('2003-02-15T13:50:05-05:00').class
14      #   # => Time
15      def w3cdtf(date)
16        if /\A\s*
17            (-?\d+)-(\d\d)-(\d\d)
18            (?:T
19            (\d\d):(\d\d)(?::(\d\d))?
20            (\.\d+)?
21            (Z|[+-]\d\d:\d\d)?)?
22            \s*\z/ix =~ date and (($5 and $8) or (!$5 and !$8))
23          datetime = [$1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i]
24          usec = 0
25          usec = $7.to_f * 1000000 if $7
26          zone = $8
27          if zone
28            off = zone_offset(zone, datetime[0])
29            datetime = apply_offset(*(datetime + [off]))
30            datetime << usec
31            time = Time.utc(*datetime)
32            force_zone!(time, zone, off)
33            time
34          else
35            datetime << usec
36            Time.local(*datetime)
37          end
38        else
39          raise ArgumentError.new("invalid date: #{date.inspect}")
40        end
41      end
42    end
43  end
44
45  unless method_defined?(:w3cdtf)
46    # This method converts a Time object to a String. The String contains the
47    # time in W3CDTF date/time format.
48    #
49    # The W3CDTF format is defined here: http://www.w3.org/TR/NOTE-datetime
50    #
51    #  Time.now.w3cdtf
52    #  # => "2013-08-26T14:12:10.817124-07:00"
53    def w3cdtf
54      if usec.zero?
55        fraction_digits = 0
56      else
57        fraction_digits = strftime('%6N').index(/0*\z/)
58      end
59      xmlschema(fraction_digits)
60    end
61  end
62end
63
64
65require "English"
66require_relative "utils"
67require_relative "converter"
68require_relative "xml-stylesheet"
69
70module RSS
71
72  # The current version of RSS
73  VERSION = "0.2.7"
74
75  # The URI of the RSS 1.0 specification
76  URI = "http://purl.org/rss/1.0/"
77
78  DEBUG = false # :nodoc:
79
80  # The basic error all other RSS errors stem from. Rescue this error if you
81  # want to handle any given RSS error and you don't care about the details.
82  class Error < StandardError; end
83
84  # RSS, being an XML-based format, has namespace support. If two namespaces are
85  # declared with the same name, an OverlappedPrefixError will be raised.
86  class OverlappedPrefixError < Error
87    attr_reader :prefix
88    def initialize(prefix)
89      @prefix = prefix
90    end
91  end
92
93  # The InvalidRSSError error is the base class for a variety of errors
94  # related to a poorly-formed RSS feed. Rescue this error if you only
95  # care that a file could be invalid, but don't care how it is invalid.
96  class InvalidRSSError < Error; end
97
98  # Since RSS is based on XML, it must have opening and closing tags that
99  # match. If they don't, a MissingTagError will be raised.
100  class MissingTagError < InvalidRSSError
101    attr_reader :tag, :parent
102    def initialize(tag, parent)
103      @tag, @parent = tag, parent
104      super("tag <#{tag}> is missing in tag <#{parent}>")
105    end
106  end
107
108  # Some tags must only exist a specific number of times in a given RSS feed.
109  # If a feed has too many occurrences of one of these tags, a TooMuchTagError
110  # will be raised.
111  class TooMuchTagError < InvalidRSSError
112    attr_reader :tag, :parent
113    def initialize(tag, parent)
114      @tag, @parent = tag, parent
115      super("tag <#{tag}> is too much in tag <#{parent}>")
116    end
117  end
118
119  # Certain attributes are required on specific tags in an RSS feed. If a feed
120  # is missing one of these attributes, a MissingAttributeError is raised.
121  class MissingAttributeError < InvalidRSSError
122    attr_reader :tag, :attribute
123    def initialize(tag, attribute)
124      @tag, @attribute = tag, attribute
125      super("attribute <#{attribute}> is missing in tag <#{tag}>")
126    end
127  end
128
129  # RSS does not allow for free-form tag names, so if an RSS feed contains a
130  # tag that we don't know about, an UnknownTagError is raised.
131  class UnknownTagError < InvalidRSSError
132    attr_reader :tag, :uri
133    def initialize(tag, uri)
134      @tag, @uri = tag, uri
135      super("tag <#{tag}> is unknown in namespace specified by uri <#{uri}>")
136    end
137  end
138
139  # Raised when an unexpected tag is encountered.
140  class NotExpectedTagError < InvalidRSSError
141    attr_reader :tag, :uri, :parent
142    def initialize(tag, uri, parent)
143      @tag, @uri, @parent = tag, uri, parent
144      super("tag <{#{uri}}#{tag}> is not expected in tag <#{parent}>")
145    end
146  end
147  # For backward compatibility :X
148  NotExceptedTagError = NotExpectedTagError # :nodoc:
149
150  # Attributes are in key-value form, and if there's no value provided for an
151  # attribute, a NotAvailableValueError will be raised.
152  class NotAvailableValueError < InvalidRSSError
153    attr_reader :tag, :value, :attribute
154    def initialize(tag, value, attribute=nil)
155      @tag, @value, @attribute = tag, value, attribute
156      message = "value <#{value}> of "
157      message << "attribute <#{attribute}> of " if attribute
158      message << "tag <#{tag}> is not available."
159      super(message)
160    end
161  end
162
163  # Raised when an unknown conversion error occurs.
164  class UnknownConversionMethodError < Error
165    attr_reader :to, :from
166    def initialize(to, from)
167      @to = to
168      @from = from
169      super("can't convert to #{to} from #{from}.")
170    end
171  end
172  # for backward compatibility
173  UnknownConvertMethod = UnknownConversionMethodError # :nodoc:
174
175  # Raised when a conversion failure occurs.
176  class ConversionError < Error
177    attr_reader :string, :to, :from
178    def initialize(string, to, from)
179      @string = string
180      @to = to
181      @from = from
182      super("can't convert #{@string} to #{to} from #{from}.")
183    end
184  end
185
186  # Raised when a required variable is not set.
187  class NotSetError < Error
188    attr_reader :name, :variables
189    def initialize(name, variables)
190      @name = name
191      @variables = variables
192      super("required variables of #{@name} are not set: #{@variables.join(', ')}")
193    end
194  end
195
196  # Raised when a RSS::Maker attempts to use an unknown maker.
197  class UnsupportedMakerVersionError < Error
198    attr_reader :version
199    def initialize(version)
200      @version = version
201      super("Maker doesn't support version: #{@version}")
202    end
203  end
204
205  module BaseModel
206    include Utils
207
208    def install_have_child_element(tag_name, uri, occurs, name=nil, type=nil)
209      name ||= tag_name
210      add_need_initialize_variable(name)
211      install_model(tag_name, uri, occurs, name)
212
213      writer_type, reader_type = type
214      def_corresponded_attr_writer name, writer_type
215      def_corresponded_attr_reader name, reader_type
216      install_element(name) do |n, elem_name|
217        <<-EOC
218        if @#{n}
219          "\#{@#{n}.to_s(need_convert, indent)}"
220        else
221          ''
222        end
223EOC
224      end
225    end
226    alias_method(:install_have_attribute_element, :install_have_child_element)
227
228    def install_have_children_element(tag_name, uri, occurs, name=nil, plural_name=nil)
229      name ||= tag_name
230      plural_name ||= "#{name}s"
231      add_have_children_element(name, plural_name)
232      add_plural_form(name, plural_name)
233      install_model(tag_name, uri, occurs, plural_name, true)
234
235      def_children_accessor(name, plural_name)
236      install_element(name, "s") do |n, elem_name|
237        <<-EOC
238        rv = []
239        @#{n}.each do |x|
240          value = "\#{x.to_s(need_convert, indent)}"
241          rv << value if /\\A\\s*\\z/ !~ value
242        end
243        rv.join("\n")
244EOC
245      end
246    end
247
248    def install_text_element(tag_name, uri, occurs, name=nil, type=nil,
249                             disp_name=nil)
250      name ||= tag_name
251      disp_name ||= name
252      self::ELEMENTS << name unless self::ELEMENTS.include?(name)
253      add_need_initialize_variable(name)
254      install_model(tag_name, uri, occurs, name)
255
256      def_corresponded_attr_writer(name, type, disp_name)
257      def_corresponded_attr_reader(name, type || :convert)
258      install_element(name) do |n, elem_name|
259        <<-EOC
260        if respond_to?(:#{n}_content)
261          content = #{n}_content
262        else
263          content = @#{n}
264        end
265        if content
266          rv = "\#{indent}<#{elem_name}>"
267          value = html_escape(content)
268          if need_convert
269            rv << convert(value)
270          else
271            rv << value
272          end
273            rv << "</#{elem_name}>"
274          rv
275        else
276          ''
277        end
278EOC
279      end
280    end
281
282    def install_date_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil)
283      name ||= tag_name
284      type ||= :w3cdtf
285      disp_name ||= name
286      self::ELEMENTS << name
287      add_need_initialize_variable(name)
288      install_model(tag_name, uri, occurs, name)
289
290      # accessor
291      convert_attr_reader name
292      date_writer(name, type, disp_name)
293
294      install_element(name) do |n, elem_name|
295        <<-EOC
296        if @#{n}
297          rv = "\#{indent}<#{elem_name}>"
298          value = html_escape(@#{n}.#{type})
299          if need_convert
300            rv << convert(value)
301          else
302            rv << value
303          end
304            rv << "</#{elem_name}>"
305          rv
306        else
307          ''
308        end
309EOC
310      end
311
312    end
313
314    private
315    def install_element(name, postfix="")
316      elem_name = name.sub('_', ':')
317      method_name = "#{name}_element#{postfix}"
318      add_to_element_method(method_name)
319      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
320      def #{method_name}(need_convert=true, indent='')
321        #{yield(name, elem_name)}
322      end
323      private :#{method_name}
324EOC
325    end
326
327    def inherit_convert_attr_reader(*attrs)
328      attrs.each do |attr|
329        module_eval(<<-EOC, *get_file_and_line_from_caller(2))
330        def #{attr}_without_inherit
331          convert(@#{attr})
332        end
333
334        def #{attr}
335          if @#{attr}
336            #{attr}_without_inherit
337          elsif @parent
338            @parent.#{attr}
339          else
340            nil
341          end
342        end
343EOC
344      end
345    end
346
347    def uri_convert_attr_reader(*attrs)
348      attrs.each do |attr|
349        module_eval(<<-EOC, *get_file_and_line_from_caller(2))
350        def #{attr}_without_base
351          convert(@#{attr})
352        end
353
354        def #{attr}
355          value = #{attr}_without_base
356          return nil if value.nil?
357          if /\\A[a-z][a-z0-9+.\\-]*:/i =~ value
358            value
359          else
360            "\#{base}\#{value}"
361          end
362        end
363EOC
364      end
365    end
366
367    def convert_attr_reader(*attrs)
368      attrs.each do |attr|
369        module_eval(<<-EOC, *get_file_and_line_from_caller(2))
370        def #{attr}
371          convert(@#{attr})
372        end
373EOC
374      end
375    end
376
377    def explicit_clean_other_attr_reader(*attrs)
378      attrs.each do |attr|
379        module_eval(<<-EOC, __FILE__, __LINE__ + 1)
380          attr_reader(:#{attr})
381          def #{attr}?
382            ExplicitCleanOther.parse(@#{attr})
383          end
384        EOC
385      end
386    end
387
388    def yes_other_attr_reader(*attrs)
389      attrs.each do |attr|
390        module_eval(<<-EOC, __FILE__, __LINE__ + 1)
391          attr_reader(:#{attr})
392          def #{attr}?
393            Utils::YesOther.parse(@#{attr})
394          end
395        EOC
396      end
397    end
398
399    def csv_attr_reader(*attrs)
400      separator = nil
401      if attrs.last.is_a?(Hash)
402        options = attrs.pop
403        separator = options[:separator]
404      end
405      separator ||= ", "
406      attrs.each do |attr|
407        module_eval(<<-EOC, __FILE__, __LINE__ + 1)
408          attr_reader(:#{attr})
409          def #{attr}_content
410            if @#{attr}.nil?
411              @#{attr}
412            else
413              @#{attr}.join(#{separator.dump})
414            end
415          end
416        EOC
417      end
418    end
419
420    def date_writer(name, type, disp_name=name)
421      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
422      def #{name}=(new_value)
423        if new_value.nil?
424          @#{name} = new_value
425        elsif new_value.kind_of?(Time)
426          @#{name} = new_value.dup
427        else
428          if @do_validate
429            begin
430              @#{name} = Time.__send__('#{type}', new_value)
431            rescue ArgumentError
432              raise NotAvailableValueError.new('#{disp_name}', new_value)
433            end
434          else
435            @#{name} = nil
436            if /\\A\\s*\\z/ !~ new_value.to_s
437              begin
438                unless Date._parse(new_value, false).empty?
439                  @#{name} = Time.parse(new_value)
440                end
441              rescue ArgumentError
442              end
443            end
444          end
445        end
446
447        # Is it need?
448        if @#{name}
449          class << @#{name}
450            undef_method(:to_s)
451            alias_method(:to_s, :#{type})
452          end
453        end
454
455      end
456EOC
457    end
458
459    def integer_writer(name, disp_name=name)
460      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
461      def #{name}=(new_value)
462        if new_value.nil?
463          @#{name} = new_value
464        else
465          if @do_validate
466            begin
467              @#{name} = Integer(new_value)
468            rescue ArgumentError
469              raise NotAvailableValueError.new('#{disp_name}', new_value)
470            end
471          else
472            @#{name} = new_value.to_i
473          end
474        end
475      end
476EOC
477    end
478
479    def positive_integer_writer(name, disp_name=name)
480      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
481      def #{name}=(new_value)
482        if new_value.nil?
483          @#{name} = new_value
484        else
485          if @do_validate
486            begin
487              tmp = Integer(new_value)
488              raise ArgumentError if tmp <= 0
489              @#{name} = tmp
490            rescue ArgumentError
491              raise NotAvailableValueError.new('#{disp_name}', new_value)
492            end
493          else
494            @#{name} = new_value.to_i
495          end
496        end
497      end
498EOC
499    end
500
501    def boolean_writer(name, disp_name=name)
502      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
503      def #{name}=(new_value)
504        if new_value.nil?
505          @#{name} = new_value
506        else
507          if @do_validate and
508              ![true, false, "true", "false"].include?(new_value)
509            raise NotAvailableValueError.new('#{disp_name}', new_value)
510          end
511          if [true, false].include?(new_value)
512            @#{name} = new_value
513          else
514            @#{name} = new_value == "true"
515          end
516        end
517      end
518EOC
519    end
520
521    def text_type_writer(name, disp_name=name)
522      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
523      def #{name}=(new_value)
524        if @do_validate and
525            !["text", "html", "xhtml", nil].include?(new_value)
526          raise NotAvailableValueError.new('#{disp_name}', new_value)
527        end
528        @#{name} = new_value
529      end
530EOC
531    end
532
533    def content_writer(name, disp_name=name)
534      klass_name = "self.class::#{Utils.to_class_name(name)}"
535      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
536      def #{name}=(new_value)
537        if new_value.is_a?(#{klass_name})
538          @#{name} = new_value
539        else
540          @#{name} = #{klass_name}.new
541          @#{name}.content = new_value
542        end
543      end
544EOC
545    end
546
547    def explicit_clean_other_writer(name, disp_name=name)
548      module_eval(<<-EOC, __FILE__, __LINE__ + 1)
549        def #{name}=(value)
550          value = (value ? "yes" : "no") if [true, false].include?(value)
551          @#{name} = value
552        end
553      EOC
554    end
555
556    def yes_other_writer(name, disp_name=name)
557      module_eval(<<-EOC, __FILE__, __LINE__ + 1)
558        def #{name}=(new_value)
559          if [true, false].include?(new_value)
560            new_value = new_value ? "yes" : "no"
561          end
562          @#{name} = new_value
563        end
564      EOC
565    end
566
567    def csv_writer(name, disp_name=name)
568      module_eval(<<-EOC, __FILE__, __LINE__ + 1)
569        def #{name}=(new_value)
570          @#{name} = Utils::CSV.parse(new_value)
571        end
572      EOC
573    end
574
575    def csv_integer_writer(name, disp_name=name)
576      module_eval(<<-EOC, __FILE__, __LINE__ + 1)
577        def #{name}=(new_value)
578          @#{name} = Utils::CSV.parse(new_value) {|v| Integer(v)}
579        end
580      EOC
581    end
582
583    def def_children_accessor(accessor_name, plural_name)
584      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
585      def #{plural_name}
586        @#{accessor_name}
587      end
588
589      def #{accessor_name}(*args)
590        if args.empty?
591          @#{accessor_name}.first
592        else
593          @#{accessor_name}[*args]
594        end
595      end
596
597      def #{accessor_name}=(*args)
598        receiver = self.class.name
599        warn("Don't use `\#{receiver}\##{accessor_name} = XXX'/" \
600             "`\#{receiver}\#set_#{accessor_name}(XXX)'. " \
601             "Those APIs are not sense of Ruby. " \
602             "Use `\#{receiver}\##{plural_name} << XXX' instead of them.", uplevel: 1)
603        if args.size == 1
604          @#{accessor_name}.push(args[0])
605        else
606          @#{accessor_name}.__send__("[]=", *args)
607        end
608      end
609      alias_method(:set_#{accessor_name}, :#{accessor_name}=)
610EOC
611    end
612  end
613
614  module SetupMaker
615    def setup_maker(maker)
616      target = maker_target(maker)
617      unless target.nil?
618        setup_maker_attributes(target)
619        setup_maker_element(target)
620        setup_maker_elements(target)
621      end
622    end
623
624    private
625    def maker_target(maker)
626      nil
627    end
628
629    def setup_maker_attributes(target)
630    end
631
632    def setup_maker_element(target)
633      self.class.need_initialize_variables.each do |var|
634        value = __send__(var)
635        next if value.nil?
636        if value.respond_to?("setup_maker") and
637            !not_need_to_call_setup_maker_variables.include?(var)
638          value.setup_maker(target)
639        else
640          setter = "#{var}="
641          if target.respond_to?(setter)
642            target.__send__(setter, value)
643          end
644        end
645      end
646    end
647
648    def not_need_to_call_setup_maker_variables
649      []
650    end
651
652    def setup_maker_elements(parent)
653      self.class.have_children_elements.each do |name, plural_name|
654        if parent.respond_to?(plural_name)
655          target = parent.__send__(plural_name)
656          __send__(plural_name).each do |elem|
657            elem.setup_maker(target)
658          end
659        end
660      end
661    end
662  end
663
664  class Element
665    extend BaseModel
666    include Utils
667    extend Utils::InheritedReader
668    include SetupMaker
669
670    INDENT = "  "
671
672    MUST_CALL_VALIDATORS = {}
673    MODELS = []
674    GET_ATTRIBUTES = []
675    HAVE_CHILDREN_ELEMENTS = []
676    TO_ELEMENT_METHODS = []
677    NEED_INITIALIZE_VARIABLES = []
678    PLURAL_FORMS = {}
679
680    class << self
681      def must_call_validators
682        inherited_hash_reader("MUST_CALL_VALIDATORS")
683      end
684      def models
685        inherited_array_reader("MODELS")
686      end
687      def get_attributes
688        inherited_array_reader("GET_ATTRIBUTES")
689      end
690      def have_children_elements
691        inherited_array_reader("HAVE_CHILDREN_ELEMENTS")
692      end
693      def to_element_methods
694        inherited_array_reader("TO_ELEMENT_METHODS")
695      end
696      def need_initialize_variables
697        inherited_array_reader("NEED_INITIALIZE_VARIABLES")
698      end
699      def plural_forms
700        inherited_hash_reader("PLURAL_FORMS")
701      end
702
703      def inherited_base
704        ::RSS::Element
705      end
706
707      def inherited(klass)
708        klass.const_set(:MUST_CALL_VALIDATORS, {})
709        klass.const_set(:MODELS, [])
710        klass.const_set(:GET_ATTRIBUTES, [])
711        klass.const_set(:HAVE_CHILDREN_ELEMENTS, [])
712        klass.const_set(:TO_ELEMENT_METHODS, [])
713        klass.const_set(:NEED_INITIALIZE_VARIABLES, [])
714        klass.const_set(:PLURAL_FORMS, {})
715
716        tag_name = klass.name.split(/::/).last
717        tag_name[0, 1] = tag_name[0, 1].downcase
718        klass.instance_variable_set(:@tag_name, tag_name)
719        klass.instance_variable_set(:@have_content, false)
720      end
721
722      def install_must_call_validator(prefix, uri)
723        self::MUST_CALL_VALIDATORS[uri] = prefix
724      end
725
726      def install_model(tag, uri, occurs=nil, getter=nil, plural=false)
727        getter ||= tag
728        if m = self::MODELS.find {|t, u, o, g, p| t == tag and u == uri}
729          m[2] = occurs
730        else
731          self::MODELS << [tag, uri, occurs, getter, plural]
732        end
733      end
734
735      def install_get_attribute(name, uri, required=true,
736                                type=nil, disp_name=nil,
737                                element_name=nil)
738        disp_name ||= name
739        element_name ||= name
740        writer_type, reader_type = type
741        def_corresponded_attr_writer name, writer_type, disp_name
742        def_corresponded_attr_reader name, reader_type
743        if type == :boolean and /^is/ =~ name
744          alias_method "#{$POSTMATCH}?", name
745        end
746        self::GET_ATTRIBUTES << [name, uri, required, element_name]
747        add_need_initialize_variable(disp_name)
748      end
749
750      def def_corresponded_attr_writer(name, type=nil, disp_name=nil)
751        disp_name ||= name
752        case type
753        when :integer
754          integer_writer name, disp_name
755        when :positive_integer
756          positive_integer_writer name, disp_name
757        when :boolean
758          boolean_writer name, disp_name
759        when :w3cdtf, :rfc822, :rfc2822
760          date_writer name, type, disp_name
761        when :text_type
762          text_type_writer name, disp_name
763        when :content
764          content_writer name, disp_name
765        when :explicit_clean_other
766          explicit_clean_other_writer name, disp_name
767        when :yes_other
768          yes_other_writer name, disp_name
769        when :csv
770          csv_writer name
771        when :csv_integer
772          csv_integer_writer name
773        else
774          attr_writer name
775        end
776      end
777
778      def def_corresponded_attr_reader(name, type=nil)
779        case type
780        when :inherit
781          inherit_convert_attr_reader name
782        when :uri
783          uri_convert_attr_reader name
784        when :explicit_clean_other
785          explicit_clean_other_attr_reader name
786        when :yes_other
787          yes_other_attr_reader name
788        when :csv
789          csv_attr_reader name
790        when :csv_integer
791          csv_attr_reader name, :separator => ","
792        else
793          convert_attr_reader name
794        end
795      end
796
797      def content_setup(type=nil, disp_name=nil)
798        writer_type, reader_type = type
799        def_corresponded_attr_writer :content, writer_type, disp_name
800        def_corresponded_attr_reader :content, reader_type
801        @have_content = true
802      end
803
804      def have_content?
805        @have_content
806      end
807
808      def add_have_children_element(variable_name, plural_name)
809        self::HAVE_CHILDREN_ELEMENTS << [variable_name, plural_name]
810      end
811
812      def add_to_element_method(method_name)
813        self::TO_ELEMENT_METHODS << method_name
814      end
815
816      def add_need_initialize_variable(variable_name)
817        self::NEED_INITIALIZE_VARIABLES << variable_name
818      end
819
820      def add_plural_form(singular, plural)
821        self::PLURAL_FORMS[singular] = plural
822      end
823
824      def required_prefix
825        nil
826      end
827
828      def required_uri
829        ""
830      end
831
832      def need_parent?
833        false
834      end
835
836      def install_ns(prefix, uri)
837        if self::NSPOOL.has_key?(prefix)
838          raise OverlappedPrefixError.new(prefix)
839        end
840        self::NSPOOL[prefix] = uri
841      end
842
843      def tag_name
844        @tag_name
845      end
846    end
847
848    attr_accessor :parent, :do_validate
849
850    def initialize(do_validate=true, attrs=nil)
851      @parent = nil
852      @converter = nil
853      if attrs.nil? and (do_validate.is_a?(Hash) or do_validate.is_a?(Array))
854        do_validate, attrs = true, do_validate
855      end
856      @do_validate = do_validate
857      initialize_variables(attrs || {})
858    end
859
860    def tag_name
861      self.class.tag_name
862    end
863
864    def full_name
865      tag_name
866    end
867
868    def converter=(converter)
869      @converter = converter
870      targets = children.dup
871      self.class.have_children_elements.each do |variable_name, plural_name|
872        targets.concat(__send__(plural_name))
873      end
874      targets.each do |target|
875        target.converter = converter unless target.nil?
876      end
877    end
878
879    def convert(value)
880      if @converter
881        @converter.convert(value)
882      else
883        value
884      end
885    end
886
887    def valid?(ignore_unknown_element=true)
888      validate(ignore_unknown_element)
889      true
890    rescue RSS::Error
891      false
892    end
893
894    def validate(ignore_unknown_element=true)
895      do_validate = @do_validate
896      @do_validate = true
897      validate_attribute
898      __validate(ignore_unknown_element)
899    ensure
900      @do_validate = do_validate
901    end
902
903    def validate_for_stream(tags, ignore_unknown_element=true)
904      validate_attribute
905      __validate(ignore_unknown_element, tags, false)
906    end
907
908    def to_s(need_convert=true, indent='')
909      if self.class.have_content?
910        return "" if !empty_content? and !content_is_set?
911        rv = tag(indent) do |next_indent|
912          if empty_content?
913            ""
914          else
915            xmled_content
916          end
917        end
918      else
919        rv = tag(indent) do |next_indent|
920          self.class.to_element_methods.collect do |method_name|
921            __send__(method_name, false, next_indent)
922          end
923        end
924      end
925      rv = convert(rv) if need_convert
926      rv
927    end
928
929    def have_xml_content?
930      false
931    end
932
933    def need_base64_encode?
934      false
935    end
936
937    def set_next_element(tag_name, next_element)
938      klass = next_element.class
939      prefix = ""
940      prefix << "#{klass.required_prefix}_" if klass.required_prefix
941      key = "#{prefix}#{tag_name.gsub(/-/, '_')}"
942      if self.class.plural_forms.has_key?(key)
943        ary = __send__("#{self.class.plural_forms[key]}")
944        ary << next_element
945      else
946        __send__("#{key}=", next_element)
947      end
948    end
949
950    protected
951    def have_required_elements?
952      self.class::MODELS.all? do |tag, uri, occurs, getter|
953        if occurs.nil? or occurs == "+"
954          child = __send__(getter)
955          if child.is_a?(Array)
956            children = child
957            children.any? {|c| c.have_required_elements?}
958          else
959            not child.nil?
960          end
961        else
962          true
963        end
964      end
965    end
966
967    private
968    def initialize_variables(attrs)
969      normalized_attrs = {}
970      attrs.each do |key, value|
971        normalized_attrs[key.to_s] = value
972      end
973      self.class.need_initialize_variables.each do |variable_name|
974        value = normalized_attrs[variable_name.to_s]
975        if value
976          __send__("#{variable_name}=", value)
977        else
978          instance_variable_set("@#{variable_name}", nil)
979        end
980      end
981      initialize_have_children_elements
982      @content = normalized_attrs["content"] if self.class.have_content?
983    end
984
985    def initialize_have_children_elements
986      self.class.have_children_elements.each do |variable_name, plural_name|
987        instance_variable_set("@#{variable_name}", [])
988      end
989    end
990
991    def tag(indent, additional_attrs={}, &block)
992      next_indent = indent + INDENT
993
994      attrs = collect_attrs
995      return "" if attrs.nil?
996
997      return "" unless have_required_elements?
998
999      attrs.update(additional_attrs)
1000      start_tag = make_start_tag(indent, next_indent, attrs.dup)
1001
1002      if block
1003        content = block.call(next_indent)
1004      else
1005        content = []
1006      end
1007
1008      if content.is_a?(String)
1009        content = [content]
1010        start_tag << ">"
1011        end_tag = "</#{full_name}>"
1012      else
1013        content = content.reject{|x| x.empty?}
1014        if content.empty?
1015          return "" if attrs.empty?
1016          end_tag = "/>"
1017        else
1018          start_tag << ">\n"
1019          end_tag = "\n#{indent}</#{full_name}>"
1020        end
1021      end
1022
1023      start_tag + content.join("\n") + end_tag
1024    end
1025
1026    def make_start_tag(indent, next_indent, attrs)
1027      start_tag = ["#{indent}<#{full_name}"]
1028      unless attrs.empty?
1029        start_tag << attrs.collect do |key, value|
1030          %Q[#{h key}="#{h value}"]
1031        end.join("\n#{next_indent}")
1032      end
1033      start_tag.join(" ")
1034    end
1035
1036    def collect_attrs
1037      attrs = {}
1038      _attrs.each do |name, required, alias_name|
1039        value = __send__(alias_name || name)
1040        return nil if required and value.nil?
1041        next if value.nil?
1042        return nil if attrs.has_key?(name)
1043        attrs[name] = value
1044      end
1045      attrs
1046    end
1047
1048    def tag_name_with_prefix(prefix)
1049      "#{prefix}:#{tag_name}"
1050    end
1051
1052    # For backward compatibility
1053    def calc_indent
1054      ''
1055    end
1056
1057    def children
1058      rv = []
1059      self.class.models.each do |name, uri, occurs, getter|
1060        value = __send__(getter)
1061        next if value.nil?
1062        value = [value] unless value.is_a?(Array)
1063        value.each do |v|
1064          rv << v if v.is_a?(Element)
1065        end
1066      end
1067      rv
1068    end
1069
1070    def _tags
1071      rv = []
1072      self.class.models.each do |name, uri, occurs, getter, plural|
1073        value = __send__(getter)
1074        next if value.nil?
1075        if plural and value.is_a?(Array)
1076          rv.concat([[uri, name]] * value.size)
1077        else
1078          rv << [uri, name]
1079        end
1080      end
1081      rv
1082    end
1083
1084    def _attrs
1085      self.class.get_attributes.collect do |name, uri, required, element_name|
1086        [element_name, required, name]
1087      end
1088    end
1089
1090    def __validate(ignore_unknown_element, tags=_tags, recursive=true)
1091      if recursive
1092        children.compact.each do |child|
1093          child.validate
1094        end
1095      end
1096      must_call_validators = self.class.must_call_validators
1097      tags = tag_filter(tags.dup)
1098      p tags if DEBUG
1099      must_call_validators.each do |uri, prefix|
1100        _validate(ignore_unknown_element, tags[uri], uri)
1101        meth = "#{prefix}_validate"
1102        if !prefix.empty? and respond_to?(meth, true)
1103          __send__(meth, ignore_unknown_element, tags[uri], uri)
1104        end
1105      end
1106    end
1107
1108    def validate_attribute
1109      _attrs.each do |a_name, required, alias_name|
1110        value = instance_variable_get("@#{alias_name || a_name}")
1111        if required and value.nil?
1112          raise MissingAttributeError.new(tag_name, a_name)
1113        end
1114        __send__("#{alias_name || a_name}=", value)
1115      end
1116    end
1117
1118    def _validate(ignore_unknown_element, tags, uri, models=self.class.models)
1119      count = 1
1120      do_redo = false
1121      not_shift = false
1122      tag = nil
1123      models = models.find_all {|model| model[1] == uri}
1124      element_names = models.collect {|model| model[0]}
1125      if tags
1126        tags_size = tags.size
1127        tags = tags.sort_by {|x| element_names.index(x) || tags_size}
1128      end
1129
1130      models.each_with_index do |model, i|
1131        name, _, occurs, = model
1132
1133        if DEBUG
1134          p "before"
1135          p tags
1136          p model
1137        end
1138
1139        if not_shift
1140          not_shift = false
1141        elsif tags
1142          tag = tags.shift
1143        end
1144
1145        if DEBUG
1146          p "mid"
1147          p count
1148        end
1149
1150        case occurs
1151        when '?'
1152          if count > 2
1153            raise TooMuchTagError.new(name, tag_name)
1154          else
1155            if name == tag
1156              do_redo = true
1157            else
1158              not_shift = true
1159            end
1160          end
1161        when '*'
1162          if name == tag
1163            do_redo = true
1164          else
1165            not_shift = true
1166          end
1167        when '+'
1168          if name == tag
1169            do_redo = true
1170          else
1171            if count > 1
1172              not_shift = true
1173            else
1174              raise MissingTagError.new(name, tag_name)
1175            end
1176          end
1177        else
1178          if name == tag
1179            if models[i+1] and models[i+1][0] != name and
1180                tags and tags.first == name
1181              raise TooMuchTagError.new(name, tag_name)
1182            end
1183          else
1184            raise MissingTagError.new(name, tag_name)
1185          end
1186        end
1187
1188        if DEBUG
1189          p "after"
1190          p not_shift
1191          p do_redo
1192          p tag
1193        end
1194
1195        if do_redo
1196          do_redo = false
1197          count += 1
1198          redo
1199        else
1200          count = 1
1201        end
1202
1203      end
1204
1205      if !ignore_unknown_element and !tags.nil? and !tags.empty?
1206        raise NotExpectedTagError.new(tags.first, uri, tag_name)
1207      end
1208
1209    end
1210
1211    def tag_filter(tags)
1212      rv = {}
1213      tags.each do |tag|
1214        rv[tag[0]] = [] unless rv.has_key?(tag[0])
1215        rv[tag[0]].push(tag[1])
1216      end
1217      rv
1218    end
1219
1220    def empty_content?
1221      false
1222    end
1223
1224    def content_is_set?
1225      if have_xml_content?
1226        __send__(self.class.xml_getter)
1227      else
1228        content
1229      end
1230    end
1231
1232    def xmled_content
1233      if have_xml_content?
1234        __send__(self.class.xml_getter).to_s
1235      else
1236        _content = content
1237        _content = [_content].pack("m0") if need_base64_encode?
1238        h(_content)
1239      end
1240    end
1241  end
1242
1243  module RootElementMixin
1244
1245    include XMLStyleSheetMixin
1246
1247    attr_reader :output_encoding
1248    attr_reader :feed_type, :feed_subtype, :feed_version
1249    attr_accessor :version, :encoding, :standalone
1250    def initialize(feed_version, version=nil, encoding=nil, standalone=nil)
1251      super()
1252      @feed_type = nil
1253      @feed_subtype = nil
1254      @feed_version = feed_version
1255      @version = version || '1.0'
1256      @encoding = encoding
1257      @standalone = standalone
1258      @output_encoding = nil
1259    end
1260
1261    def feed_info
1262      [@feed_type, @feed_version, @feed_subtype]
1263    end
1264
1265    def output_encoding=(enc)
1266      @output_encoding = enc
1267      self.converter = Converter.new(@output_encoding, @encoding)
1268    end
1269
1270    def setup_maker(maker)
1271      maker.version = version
1272      maker.encoding = encoding
1273      maker.standalone = standalone
1274
1275      xml_stylesheets.each do |xss|
1276        xss.setup_maker(maker)
1277      end
1278
1279      super
1280    end
1281
1282    def to_feed(type, &block)
1283      Maker.make(type) do |maker|
1284        setup_maker(maker)
1285        block.call(maker) if block
1286      end
1287    end
1288
1289    def to_rss(type, &block)
1290      to_feed("rss#{type}", &block)
1291    end
1292
1293    def to_atom(type, &block)
1294      to_feed("atom:#{type}", &block)
1295    end
1296
1297    def to_xml(type=nil, &block)
1298      if type.nil? or same_feed_type?(type)
1299        to_s
1300      else
1301        to_feed(type, &block).to_s
1302      end
1303    end
1304
1305    private
1306    def same_feed_type?(type)
1307      if /^(atom|rss)?(\d+\.\d+)?(?::(.+))?$/i =~ type
1308        feed_type = ($1 || @feed_type).downcase
1309        feed_version = $2 || @feed_version
1310        feed_subtype = $3 || @feed_subtype
1311        [feed_type, feed_version, feed_subtype] == feed_info
1312      else
1313        false
1314      end
1315    end
1316
1317    def tag(indent, attrs={}, &block)
1318      rv = super(indent, ns_declarations.merge(attrs), &block)
1319      return rv if rv.empty?
1320      "#{xmldecl}#{xml_stylesheet_pi}#{rv}"
1321    end
1322
1323    def xmldecl
1324      rv = %Q[<?xml version="#{@version}"]
1325      if @output_encoding or @encoding
1326        rv << %Q[ encoding="#{@output_encoding or @encoding}"]
1327      end
1328      rv << %Q[ standalone="yes"] if @standalone
1329      rv << "?>\n"
1330      rv
1331    end
1332
1333    def ns_declarations
1334      decls = {}
1335      self.class::NSPOOL.collect do |prefix, uri|
1336        prefix = ":#{prefix}" unless prefix.empty?
1337        decls["xmlns#{prefix}"] = uri
1338      end
1339      decls
1340    end
1341
1342    def maker_target(target)
1343      target
1344    end
1345  end
1346end
1347