root/lang/ruby/nario/rskit/lib/ruby/1.8/rss/rss.rb @ 17197

Revision 17197, 20.8 kB (checked in by authorNari, 6 years ago)

add rskit

Line 
1require "time"
2
3class Time
4  class << self
5    unless respond_to?(:w3cdtf)
6      def w3cdtf(date)
7        if /\A\s*
8            (-?\d+)-(\d\d)-(\d\d)
9            (?:T
10            (\d\d):(\d\d)(?::(\d\d))?
11            (\.\d+)?
12            (Z|[+-]\d\d:\d\d)?)?
13            \s*\z/ix =~ date and (($5 and $8) or (!$5 and !$8))
14          datetime = [$1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i]
15          datetime << $7.to_f * 1000000 if $7
16          if $8
17            Time.utc(*datetime) - zone_offset($8)
18          else
19            Time.local(*datetime)
20          end
21        else
22          raise ArgumentError.new("invalid date: #{date.inspect}")
23        end
24      end
25    end
26  end
27
28  unless instance_methods.include?("w3cdtf")
29    alias w3cdtf iso8601
30  end
31end
32
33module Enumerable
34  unless instance_methods.include?("sort_by")
35    def sort_by
36      collect do |x|
37        [yield(x), x]
38      end.sort do |x, y|
39        x[0] <=> y[0]
40      end.collect! do |x|
41        x[1]
42      end
43    end
44  end
45end
46
47class Hash
48  unless instance_methods.include?("merge")
49    def merge(other)
50      dup.update(other)
51    end
52  end
53end
54
55require "English"
56require "rss/utils"
57require "rss/converter"
58require "rss/xml-stylesheet"
59
60module RSS
61
62  VERSION = "0.1.5"
63
64  URI = "http://purl.org/rss/1.0/"
65
66  DEBUG = false
67
68  class Error < StandardError; end
69
70  class OverlappedPrefixError < Error
71    attr_reader :prefix
72    def initialize(prefix)
73      @prefix = prefix
74    end
75  end
76
77  class InvalidRSSError < Error; end
78
79  class MissingTagError < InvalidRSSError
80    attr_reader :tag, :parent
81    def initialize(tag, parent)
82      @tag, @parent = tag, parent
83      super("tag <#{tag}> is missing in tag <#{parent}>")
84    end
85  end
86
87  class TooMuchTagError < InvalidRSSError
88    attr_reader :tag, :parent
89    def initialize(tag, parent)
90      @tag, @parent = tag, parent
91      super("tag <#{tag}> is too much in tag <#{parent}>")
92    end
93  end
94
95  class MissingAttributeError < InvalidRSSError
96    attr_reader :tag, :attribute
97    def initialize(tag, attribute)
98      @tag, @attribute = tag, attribute
99      super("attribute <#{attribute}> is missing in tag <#{tag}>")
100    end
101  end
102
103  class UnknownTagError < InvalidRSSError
104    attr_reader :tag, :uri
105    def initialize(tag, uri)
106      @tag, @uri = tag, uri
107      super("tag <#{tag}> is unknown in namespace specified by uri <#{uri}>")
108    end
109  end
110
111  class NotExceptedTagError < InvalidRSSError
112    attr_reader :tag, :parent
113    def initialize(tag, parent)
114      @tag, @parent = tag, parent
115      super("tag <#{tag}> is not expected in tag <#{parent}>")
116    end
117  end
118
119  class NotAvailableValueError < InvalidRSSError
120    attr_reader :tag, :value
121    def initialize(tag, value)
122      @tag, @value = tag, value
123      super("value <#{value}> of tag <#{tag}> is not available.")
124    end
125  end
126
127  class UnknownConversionMethodError < Error
128    attr_reader :to, :from
129    def initialize(to, from)
130      @to = to
131      @from = from
132      super("can't convert to #{to} from #{from}.")
133    end
134  end
135  # for backward compatibility
136  UnknownConvertMethod = UnknownConversionMethodError
137
138  class ConversionError < Error
139    attr_reader :string, :to, :from
140    def initialize(string, to, from)
141      @string = string
142      @to = to
143      @from = from
144      super("can't convert #{@string} to #{to} from #{from}.")
145    end
146  end
147
148  class NotSetError < Error
149    attr_reader :name, :variables
150    def initialize(name, variables)
151      @name = name
152      @variables = variables
153      super("required variables of #{@name} are not set: #{@variables.join(', ')}")
154    end
155  end
156 
157  module BaseModel
158
159    include Utils
160
161    def install_have_child_element(name)
162      add_need_initialize_variable(name)
163
164      attr_accessor name
165      install_element(name) do |n, elem_name|
166        <<-EOC
167        if @#{n}
168          "\#{@#{n}.to_s(need_convert, indent)}"
169        else
170          ''
171        end
172EOC
173      end
174    end
175    alias_method(:install_have_attribute_element, :install_have_child_element)
176
177    def install_have_children_element(name, plural_name=nil)
178      plural_name ||= "#{name}s"
179      add_have_children_element(name, plural_name)
180      add_plural_form(name, plural_name)
181     
182      def_children_accessor(name, plural_name)
183      install_element(name, "s") do |n, elem_name|
184        <<-EOC
185        rv = []
186        @#{n}.each do |x|
187          value = "\#{x.to_s(need_convert, indent)}"
188          rv << value if /\\A\\s*\\z/ !~ value
189        end
190        rv.join("\n")
191EOC
192      end
193    end
194
195    def install_text_element(name)
196      self::ELEMENTS << name
197      add_need_initialize_variable(name)
198
199      attr_writer name
200      convert_attr_reader name
201      install_element(name) do |n, elem_name|
202        <<-EOC
203        if @#{n}
204          rv = "\#{indent}<#{elem_name}>"
205          value = html_escape(@#{n})
206          if need_convert
207            rv << convert(value)
208          else
209            rv << value
210          end
211            rv << "</#{elem_name}>"
212          rv
213        else
214          ''
215        end
216EOC
217      end
218    end
219
220    def install_date_element(name, type, disp_name=name)
221      self::ELEMENTS << name
222      add_need_initialize_variable(name)
223
224      # accessor
225      convert_attr_reader name
226      date_writer(name, type, disp_name)
227     
228      install_element(name) do |n, elem_name|
229        <<-EOC
230        if @#{n}
231          rv = "\#{indent}<#{elem_name}>"
232          value = html_escape(@#{n}.#{type})
233          if need_convert
234            rv << convert(value)
235          else
236            rv << value
237          end
238            rv << "</#{elem_name}>"
239          rv
240        else
241          ''
242        end
243EOC
244      end
245
246    end
247
248    private
249    def install_element(name, postfix="")
250      elem_name = name.sub('_', ':')
251      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
252      def #{name}_element#{postfix}(need_convert=true, indent='')
253        #{yield(name, elem_name)}
254      end
255      private :#{name}_element#{postfix}
256EOC
257    end
258
259    def convert_attr_reader(*attrs)
260      attrs.each do |attr|
261        attr = attr.id2name if attr.kind_of?(Integer)
262        module_eval(<<-EOC, *get_file_and_line_from_caller(2))
263        def #{attr}
264          if @converter
265            @converter.convert(@#{attr})
266          else
267            @#{attr}
268          end
269        end
270EOC
271      end
272    end
273
274    def date_writer(name, type, disp_name=name)
275      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
276      def #{name}=(new_value)
277        if new_value.nil? or new_value.kind_of?(Time)
278          @#{name} = new_value
279        else
280          if @do_validate
281            begin
282              @#{name} = Time.send('#{type}', new_value)
283            rescue ArgumentError
284              raise NotAvailableValueError.new('#{disp_name}', new_value)
285            end
286          else
287            @#{name} = nil
288            if /\\A\\s*\\z/ !~ new_value.to_s
289              begin
290                @#{name} = Time.parse(new_value)
291              rescue ArgumentError
292              end
293            end
294          end
295        end
296
297        # Is it need?
298        if @#{name}
299          class << @#{name}
300            undef_method(:to_s)
301            alias_method(:to_s, :#{type})
302          end
303        end
304
305      end
306EOC
307    end
308
309    def def_children_accessor(accessor_name, plural_name)
310      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
311      def #{plural_name}
312        @#{accessor_name}
313      end
314
315      def #{accessor_name}(*args)
316        if args.empty?
317          @#{accessor_name}.first
318        else
319          @#{accessor_name}.send("[]", *args)
320        end
321      end
322
323      def #{accessor_name}=(*args)
324        warn("Warning:\#{caller.first.sub(/:in `.*'\z/, '')}: " \
325             "Don't use `#{accessor_name} = XXX'/`set_#{accessor_name}(XXX)'. " \
326             "Those APIs are not sense of Ruby. " \
327             "Use `#{plural_name} << XXX' instead of them.")
328        if args.size == 1
329          @#{accessor_name}.push(args[0])
330        else
331          @#{accessor_name}.send("[]=", *args)
332        end
333      end
334      alias_method(:set_#{accessor_name}, :#{accessor_name}=)
335EOC
336    end
337
338    def def_content_only_to_s
339      module_eval(<<-EOC, *get_file_and_line_from_caller(2))
340      def to_s(need_convert=true, indent=calc_indent)
341        if @content
342          rv = tag(indent) do |next_indent|
343            h(@content)
344          end
345          rv = convert(rv) if need_convert
346          rv
347        else
348          ""
349        end
350      end
351EOC
352    end
353   
354  end
355
356  class Element
357
358    extend BaseModel
359    include Utils
360
361    INDENT = "  "
362   
363    MUST_CALL_VALIDATORS = {}
364    MODEL = []
365    GET_ATTRIBUTES = []
366    HAVE_CHILDREN_ELEMENTS = []
367    NEED_INITIALIZE_VARIABLES = []
368    PLURAL_FORMS = {}
369   
370    class << self
371
372      def must_call_validators
373        MUST_CALL_VALIDATORS
374      end
375      def model
376        MODEL
377      end
378      def get_attributes
379        GET_ATTRIBUTES
380      end
381      def have_children_elements
382        HAVE_CHILDREN_ELEMENTS
383      end
384      def need_initialize_variables
385        NEED_INITIALIZE_VARIABLES
386      end
387      def plural_forms
388        PLURAL_FORMS
389      end
390
391     
392      def inherited(klass)
393        klass.const_set("MUST_CALL_VALIDATORS", {})
394        klass.const_set("MODEL", [])
395        klass.const_set("GET_ATTRIBUTES", [])
396        klass.const_set("HAVE_CHILDREN_ELEMENTS", [])
397        klass.const_set("NEED_INITIALIZE_VARIABLES", [])
398        klass.const_set("PLURAL_FORMS", {})
399
400        klass.module_eval(<<-EOC)
401        public
402       
403        @tag_name = name.split(/::/).last
404        @tag_name[0,1] = @tag_name[0,1].downcase
405        @indent_size = name.split(/::/).size - 2
406        @have_content = false
407
408        def self.must_call_validators
409          super.merge(MUST_CALL_VALIDATORS)
410        end
411        def self.model
412          MODEL + super
413        end
414        def self.get_attributes
415          GET_ATTRIBUTES + super
416        end
417        def self.have_children_elements
418          HAVE_CHILDREN_ELEMENTS + super
419        end
420        def self.need_initialize_variables
421          NEED_INITIALIZE_VARIABLES + super
422        end
423        def self.plural_forms
424          super.merge(PLURAL_FORMS)
425        end
426
427     
428        def self.install_must_call_validator(prefix, uri)
429          MUST_CALL_VALIDATORS[uri] = prefix
430        end
431       
432        def self.install_model(tag, occurs=nil)
433          if m = MODEL.find {|t, o| t == tag}
434            m[1] = occurs
435          else
436            MODEL << [tag, occurs]
437          end
438        end
439
440        def self.install_get_attribute(name, uri, required=true)
441          attr_writer name
442          convert_attr_reader name
443          GET_ATTRIBUTES << [name, uri, required]
444        end
445
446        def self.content_setup
447          attr_writer :content
448          convert_attr_reader :content
449          def_content_only_to_s
450          @have_content = true
451        end
452
453        def self.have_content?
454          @have_content
455        end
456
457        def self.add_have_children_element(variable_name, plural_name)
458          HAVE_CHILDREN_ELEMENTS << [variable_name, plural_name]
459        end
460       
461        def self.add_need_initialize_variable(variable_name)
462          NEED_INITIALIZE_VARIABLES << variable_name
463        end
464       
465        def self.add_plural_form(singular, plural)
466          PLURAL_FORMS[singular] = plural
467        end
468       
469        EOC
470      end
471
472      def required_prefix
473        nil
474      end
475
476      def required_uri
477        nil
478      end
479     
480      def install_ns(prefix, uri)
481        if self::NSPOOL.has_key?(prefix)
482          raise OverlappedPrefixError.new(prefix)
483        end
484        self::NSPOOL[prefix] = uri
485      end
486
487      def tag_name
488        @tag_name
489      end
490     
491      def indent_size
492        @indent_size
493      end
494     
495    end
496
497    attr_accessor :do_validate
498
499    def initialize(do_validate=true)
500      @converter = nil
501      @do_validate = do_validate
502      initialize_variables
503    end
504
505    def tag_name
506      self.class.tag_name
507    end
508
509    def full_name
510      tag_name
511    end
512   
513    def indent_size
514      self.class.indent_size
515    end
516   
517    def converter=(converter)
518      @converter = converter
519      targets = children.dup
520      self.class.have_children_elements.each do |variable_name, plural_name|
521        targets.concat(__send__(plural_name))
522      end
523      targets.each do |target|
524        target.converter = converter unless target.nil?
525      end
526    end
527
528    def convert(value)
529      if @converter
530        @converter.convert(value)
531      else
532        value
533      end
534    end
535   
536    def validate
537      validate_attribute
538      __validate
539    end
540   
541    def validate_for_stream(tags)
542      validate_attribute
543      __validate(tags, false)
544    end
545
546    def setup_maker(maker)
547      target = maker_target(maker)
548      unless target.nil?
549        setup_maker_attributes(target)
550        setup_maker_element(target)
551        setup_maker_elements(target)
552      end
553    end
554   
555    private
556    def initialize_variables
557      self.class.need_initialize_variables.each do |variable_name|
558        instance_eval("@#{variable_name} = nil")
559      end
560      initialize_have_children_elements
561      @content = "" if self.class.have_content?
562    end
563
564    def initialize_have_children_elements
565      self.class.have_children_elements.each do |variable_name, plural_name|
566        instance_eval("@#{variable_name} = []")
567      end
568    end
569
570    def tag(indent, additional_attrs=[], &block)
571      next_indent = indent + INDENT
572
573      attrs = collect_attrs
574      return "" if attrs.nil?
575
576      attrs += additional_attrs
577      start_tag = make_start_tag(indent, next_indent, attrs)
578
579      if block
580        content = block.call(next_indent)
581      else
582        content = []
583      end
584
585      if content.is_a?(String)
586        content = [content]
587        start_tag << ">"
588        end_tag = "</#{full_name}>"
589      else
590        content = content.reject{|x| x.empty?}
591        if content.empty?
592          end_tag = "/>"
593        else
594          start_tag << ">\n"
595          end_tag = "\n#{indent}</#{full_name}>"
596        end
597      end
598     
599      start_tag + content.join("\n") + end_tag
600    end
601
602    def make_start_tag(indent, next_indent, attrs)
603      start_tag = ["#{indent}<#{full_name}"]
604      unless attrs.empty?
605        start_tag << attrs.collect do |key, value|
606          %Q[#{h key}="#{h value}"]
607        end.join("\n#{next_indent}")
608      end
609      start_tag.join(" ")
610    end
611
612    def collect_attrs
613      _attrs.collect do |name, required, alias_name|
614        value = __send__(alias_name || name)
615        return nil if required and value.nil?
616        [name, value]
617      end.reject do |name, value|
618        value.nil?
619      end
620    end
621   
622    def tag_name_with_prefix(prefix)
623      "#{prefix}:#{tag_name}"
624    end
625   
626    def calc_indent
627      INDENT * (self.class.indent_size)
628    end
629
630    def maker_target(maker)
631      nil
632    end
633   
634    def setup_maker_attributes(target)
635    end
636   
637    def setup_maker_element(target)
638      self.class.need_initialize_variables.each do |var|
639        value = __send__(var)
640        if value.respond_to?("setup_maker") and
641            !not_need_to_call_setup_maker_variables.include?(var)
642          value.setup_maker(target)
643        else
644          setter = "#{var}="
645          if target.respond_to?(setter)
646            target.__send__(setter, value)
647          end
648        end
649      end
650    end
651
652    def not_need_to_call_setup_maker_variables
653      []
654    end
655   
656    def setup_maker_elements(parent)
657      self.class.have_children_elements.each do |name, plural_name|
658        real_name = name.sub(/^[^_]+_/, '')
659        if parent.respond_to?(plural_name)
660          target = parent.__send__(plural_name)
661          __send__(plural_name).each do |elem|
662            elem.setup_maker(target)
663          end
664        end
665      end
666    end
667
668    def set_next_element(prefix, tag_name, next_element)
669      klass = next_element.class
670      prefix = ""
671      prefix << "#{klass.required_prefix}_" if klass.required_prefix
672      key = "#{prefix}#{tag_name}"
673      if self.class.plural_forms.has_key?(key)
674        ary = __send__("#{self.class.plural_forms[key]}")
675        ary << next_element
676      else
677        __send__("#{prefix}#{tag_name}=", next_element)
678      end
679    end
680   
681    # not String class children.
682    def children
683      []
684    end
685
686    # default #validate() argument.
687    def _tags
688      []
689    end
690
691    def _attrs
692      []
693    end
694
695    def __validate(tags=_tags, recursive=true)
696      if recursive
697        children.compact.each do |child|
698          child.validate
699        end
700      end
701      must_call_validators = self.class.must_call_validators
702      tags = tag_filter(tags.dup)
703      p tags if DEBUG
704      self.class::NSPOOL.each do |prefix, uri|
705        if tags.has_key?(uri) and !must_call_validators.has_key?(uri)
706          meth = "#{prefix}_validate"
707          send(meth, tags[uri]) if respond_to?(meth, true)
708        end
709      end
710      must_call_validators.each do |uri, prefix|
711        send("#{prefix}_validate", tags[uri])
712      end
713    end
714
715    def validate_attribute
716      _attrs.each do |a_name, required, alias_name|
717        if required and __send__(alias_name || a_name).nil?
718          raise MissingAttributeError.new(tag_name, a_name)
719        end
720      end
721    end
722
723    def other_element(need_convert, indent='')
724      rv = []
725      private_methods.each do |meth|
726        if /\A([^_]+)_[^_]+_elements?\z/ =~ meth and
727            self.class::NSPOOL.has_key?($1)
728          res = __send__(meth, need_convert)
729          rv << "#{indent}#{res}" if /\A\s*\z/ !~ res
730        end
731      end
732      rv.join("\n")
733    end
734
735    def _validate(tags, model=self.class.model)
736      count = 1
737      do_redo = false
738      not_shift = false
739      tag = nil
740      element_names = model.collect {|elem| elem[0]}
741      if tags
742        tags_size = tags.size
743        tags = tags.sort_by {|x| element_names.index(x) || tags_size}
744      end
745
746      model.each_with_index do |elem, i|
747
748        if DEBUG
749          p "before"
750          p tags
751          p elem
752        end
753
754        if not_shift
755          not_shift = false
756        elsif tags
757          tag = tags.shift
758        end
759
760        if DEBUG
761          p "mid"
762          p count
763        end
764
765        case elem[1]
766        when '?'
767          if count > 2
768            raise TooMuchTagError.new(elem[0], tag_name)
769          else
770            if elem[0] == tag
771              do_redo = true
772            else
773              not_shift = true
774            end
775          end
776        when '*'
777          if elem[0] == tag
778            do_redo = true
779          else
780            not_shift = true
781          end
782        when '+'
783          if elem[0] == tag
784            do_redo = true
785          else
786            if count > 1
787              not_shift = true
788            else
789              raise MissingTagError.new(elem[0], tag_name)
790            end
791          end
792        else
793          if elem[0] == tag
794            if model[i+1] and model[i+1][0] != elem[0] and
795                tags and tags.first == elem[0]
796              raise TooMuchTagError.new(elem[0], tag_name)
797            end
798          else
799            raise MissingTagError.new(elem[0], tag_name)
800          end
801        end
802
803        if DEBUG
804          p "after"
805          p not_shift
806          p do_redo
807          p tag
808        end
809
810        if do_redo
811          do_redo = false
812          count += 1
813          redo
814        else
815          count = 1
816        end
817
818      end
819
820      if !tags.nil? and !tags.empty?
821        raise NotExceptedTagError.new(tag, tag_name)
822      end
823
824    end
825
826    def tag_filter(tags)
827      rv = {}
828      tags.each do |tag|
829        rv[tag[0]] = [] unless rv.has_key?(tag[0])
830        rv[tag[0]].push(tag[1])
831      end
832      rv
833    end
834
835  end
836
837  module RootElementMixin
838
839    include XMLStyleSheetMixin
840   
841    attr_reader :output_encoding
842
843    def initialize(rss_version, version=nil, encoding=nil, standalone=nil)
844      super()
845      @rss_version = rss_version
846      @version = version || '1.0'
847      @encoding = encoding
848      @standalone = standalone
849      @output_encoding = nil
850    end
851
852    def output_encoding=(enc)
853      @output_encoding = enc
854      self.converter = Converter.new(@output_encoding, @encoding)
855    end
856
857    def setup_maker(maker)
858      maker.version = version
859      maker.encoding = encoding
860      maker.standalone = standalone
861
862      xml_stylesheets.each do |xss|
863        xss.setup_maker(maker)
864      end
865
866      setup_maker_elements(maker)
867    end
868   
869    private
870    def tag(indent, attrs, &block)
871      rv = xmldecl + xml_stylesheet_pi
872      rv << super(indent, attrs, &block)
873      rv
874    end
875
876    def xmldecl
877      rv = %Q[<?xml version="#{@version}"]
878      if @output_encoding or @encoding
879        rv << %Q[ encoding="#{@output_encoding or @encoding}"]
880      end
881      rv << %Q[ standalone="yes"] if @standalone
882      rv << "?>\n"
883      rv
884    end
885   
886    def ns_declarations
887      self.class::NSPOOL.collect do |prefix, uri|
888        prefix = ":#{prefix}" unless prefix.empty?
889        ["xmlns#{prefix}", uri]
890      end
891    end
892   
893    def setup_maker_elements(maker)
894      channel.setup_maker(maker) if channel
895      image.setup_maker(maker) if image
896      textinput.setup_maker(maker) if textinput
897      items.each do |item|
898        item.setup_maker(maker)
899      end
900    end
901  end
902
903end
Note: See TracBrowser for help on using the browser.