root/platform/tdiary/lib/json.rb

Revision 3326, 21.1 kB (checked in by hsbt, 12 months ago)

platform/tdiary/*: import from official repos.

Line 
1# = json - JSON library for Ruby
2#
3# == Description
4#
5# == Author
6#
7# Florian Frank <mailto:flori@ping.de>
8#
9# == License
10#
11# This is free software; you can redistribute it and/or modify it under the
12# terms of the GNU General Public License Version 2 as published by the Free
13# Software Foundation: www.gnu.org/copyleft/gpl.html
14#
15# == Download
16#
17# The latest version of this library can be downloaded at
18#
19# * http://rubyforge.org/frs?group_id=953
20#
21# Online Documentation should be located at
22#
23# * http://json.rubyforge.org
24#
25# == Examples
26#
27# To create a JSON string from a ruby data structure, you
28# can call JSON.unparse (or JSON.generate) like that:
29#
30#  json = JSON.unparse [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
31#  # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
32#
33# It's also possible to call the #to_json method directly.
34#
35#  json = [1, 2, {"a"=>3.141}, false, true, nil, 4..10].to_json
36#  # => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
37#
38# To get back a ruby data structure, you have to call
39# JSON.parse on the JSON string:
40#
41#  JSON.parse json
42#  # => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
43#
44# Note, that the range from the original data structure is a simple
45# string now. The reason for this is, that JSON doesn't support ranges
46# or arbitrary classes. In this case the json library falls back to call
47# Object#to_json, which is the same as #to_s.to_json.
48#
49# It's possible to extend JSON to support serialization of arbitray classes by
50# simply implementing a more specialized version of the #to_json method, that
51# should return a JSON object (a hash converted to JSON with #to_json)
52# like this (don't forget the *a for all the arguments):
53#
54#  class Range
55#    def to_json(*a)
56#      {
57#        'json_class'   => self.class.name,
58#        'data'         => [ first, last, exclude_end? ]
59#      }.to_json(*a)
60#    end
61#  end
62#
63# The hash key 'json_class' is the class, that will be asked to deserialize the
64# JSON representation later. In this case it's 'Range', but any namespace of
65# the form 'A::B' or '::A::B' will do. All other keys are arbitrary and can be
66# used to store the necessary data to configure the object to be deserialized.
67#
68# If a the key 'json_class' is found in a JSON object, the JSON parser checks
69# if the given class responds to the json_create class method. If so, it is
70# called with the JSON object converted to a Ruby hash. So a range can
71# be deserialized by implementing Range.json_create like this:
72#
73#  class Range
74#    def self.json_create(o)
75#      new(*o['data'])
76#    end
77#  end
78#
79# Now it possible to serialize/deserialize ranges as well:
80#
81#  json = JSON.unparse [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
82#  # => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
83#  JSON.parse json
84#  # => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
85#
86# JSON.unparse always creates the shortes possible string representation of a
87# ruby data structure in one line. This good for data storage or network
88# protocols, but not so good for humans to read. Fortunately there's
89# also JSON.pretty_unparse (or JSON.pretty_generate) that creates a more
90# readable output:
91#
92#  puts JSON.pretty_unparse([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
93#  [
94#    1,
95#    2,
96#    {
97#      "a": 3.141
98#    },
99#    false,
100#    true,
101#    null,
102#    {
103#      "json_class": "Range",
104#      "data": [
105#        4,
106#        10,
107#        false
108#      ]
109#    }
110#  ]
111#
112# There are also the methods Kernel#j for unparse, and Kernel#jj for
113# pretty_unparse output to the console, that work analogous to Kernel#p and
114# Kernel#pp.
115#
116
117require 'strscan'
118
119# This module is the namespace for all the JSON related classes. It also
120# defines some module functions to expose a nicer API to users, instead
121# of using the parser and other classes directly.
122module JSON
123  # The base exception for JSON errors.
124  JSONError             = Class.new StandardError
125
126  # This exception is raise, if a parser error occurs.
127  ParserError           = Class.new JSONError
128
129  # This exception is raise, if a unparser error occurs.
130  UnparserError         = Class.new JSONError
131
132  # If a circular data structure is encountered while unparsing
133  # this exception is raised.
134  CircularDatastructure = Class.new UnparserError
135
136  class << self
137    # Switches on Unicode support, if _enable_ is _true_. Otherwise switches
138    # Unicode support off.
139    def support_unicode=(enable)
140      @support_unicode = enable
141    end
142
143    # Returns _true_ if JSON supports unicode, otherwise _false_ is returned.
144    #
145    # If loading of the iconv library fails, or it doesn't support utf8/utf16
146    # encoding, this will be set to false, as a fallback.
147    def support_unicode?
148      !!@support_unicode
149    end
150  end
151  JSON.support_unicode = true # default, however it's possible to switch off
152                              # full unicode support, if non-ascii bytes should be
153                              # just passed through.
154
155  begin
156    require 'iconv'
157    # An iconv instance to convert from UTF8 to UTF16 Big Endian.
158    UTF16toUTF8 = Iconv.new('utf-8', 'utf-16be')
159    # An iconv instance to convert from UTF16 Big Endian to UTF8.
160    UTF8toUTF16 = Iconv.new('utf-16be', 'utf-8'); UTF8toUTF16.iconv('no bom')
161  rescue Errno::EINVAL, Iconv::InvalidEncoding
162    # Iconv doesn't support big endian utf-16. Let's try to hack this manually
163    # into the converters.
164    begin
165      old_verbose = $VERBOSE
166      $VERBOSE = nil
167      # An iconv instance to convert from UTF8 to UTF16 Big Endian.
168      UTF16toUTF8 = Iconv.new('utf-8', 'utf-16')
169      # An iconv instance to convert from UTF16 Big Endian to UTF8.
170      UTF8toUTF16 = Iconv.new('utf-16', 'utf-8'); UTF8toUTF16.iconv('no bom')
171      if UTF8toUTF16.iconv("\xe2\x82\xac") == "\xac\x20"
172        swapper = Class.new do
173          def initialize(iconv)
174            @iconv = iconv
175          end
176
177          def iconv(string)
178            result = @iconv.iconv(string)
179            JSON.swap!(result)
180          end
181        end
182        UTF8toUTF16 = swapper.new(UTF8toUTF16)
183      end
184      if UTF16toUTF8.iconv("\xac\x20") == "\xe2\x82\xac"
185        swapper = Class.new do
186          def initialize(iconv)
187            @iconv = iconv
188          end
189
190          def iconv(string)
191            string = JSON.swap!(string.dup)
192            @iconv.iconv(string)
193          end
194        end
195        UTF16toUTF8 = swapper.new(UTF16toUTF8)
196      end
197    rescue Errno::EINVAL, Iconv::InvalidEncoding
198      # Enforce disabling of unicode support, if iconv doesn't support
199      # UTF8/UTF16 at all.
200      JSON.support_unicode = false
201    ensure
202      $VERBOSE = old_verbose
203    end
204  rescue LoadError
205    # Enforce disabling of unicode support, if iconv doesn't exist.
206    JSON.support_unicode = false
207  end
208
209  # Swap consecutive bytes in string in place.
210  def self.swap!(string)
211    0.upto(string.size / 2) do |i|
212      break unless string[2 * i + 1]
213      string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
214    end
215    string
216  end
217
218  # This class implements the JSON parser that is used to parse a JSON string
219  # into a Ruby data structure.
220  class Parser < StringScanner
221    STRING                = /"((?:[^"\\]|\\.)*)"/
222    INTEGER               = /-?(?:0|[1-9]\d*)/
223    FLOAT                 = /-?(?:0|[1-9]\d*)\.(\d+)(?i:e[+-]?\d+)?/
224    OBJECT_OPEN           = /\{/
225    OBJECT_CLOSE          = /\}/
226    ARRAY_OPEN            = /\[/
227    ARRAY_CLOSE           = /\]/
228    PAIR_DELIMITER        = /:/
229    COLLECTION_DELIMITER  = /,/
230    TRUE                  = /true/
231    FALSE                 = /false/
232    NULL                  = /null/
233    IGNORE                = %r(
234      (?:
235        //[^\n\r]*[\n\r]| # line comments
236        /\*               # c-style comments
237          (?:
238            [^*/]|        # normal chars
239            /[^*]|        # slashes that do not start a nested comment
240            \*[^/]|       # asterisks that do not end this comment
241            /(?=\*/)      # single slash before this comment's end
242          )*
243        \*/               # the end of this comment
244        |[ \t\r\n]+       # whitespaces: space, horicontal tab, lf, cr
245      )+
246    )mx
247
248    UNPARSED = Object.new
249
250    # Parses the current JSON string and returns the complete data structure
251    # as a result.
252    def parse
253      reset
254      obj = nil
255      until eos?
256        case
257        when scan(OBJECT_OPEN)
258          obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
259          obj = parse_object
260        when scan(ARRAY_OPEN)
261          obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
262          obj = parse_array
263        when skip(IGNORE)
264          ;
265        else
266          raise ParserError, "source '#{peek(20)}' not in JSON!"
267        end
268      end
269      obj or raise ParserError, "source did not contain any JSON!"
270      obj
271    end
272
273    private
274
275    def parse_string
276      if scan(STRING)
277        return '' if self[1].empty?
278        self[1].gsub(%r(\\(?:[\\bfnrt"/]|u([A-Fa-f\d]{4})))) do
279          case $~[0]
280          when '\\"'  then '"'
281          when '\\\\' then '\\'
282          when '\\/'  then '/'
283          when '\\b'  then "\b"
284          when '\\f'  then "\f"
285          when '\\n'  then "\n"
286          when '\\r'  then "\r"
287          when '\\t'  then "\t"
288          else
289            if JSON.support_unicode? and $KCODE == 'UTF8'
290              JSON.utf16_to_utf8($~[1])
291            else
292              # if utf8 mode is switched off or unicode not supported, try to
293              # transform unicode \u-notation to bytes directly:
294              $~[1].to_i(16).chr
295            end
296          end
297        end
298      else
299        UNPARSED
300      end
301    end
302
303    def parse_value
304      case
305      when scan(FLOAT)
306        Float(self[0].sub(/\.([eE])/, '.0\1'))
307      when scan(INTEGER)
308        Integer(self[0])
309      when scan(TRUE)
310        true
311      when scan(FALSE)
312        false
313      when scan(NULL)
314        nil
315      when (string = parse_string) != UNPARSED
316        string
317      when scan(ARRAY_OPEN)
318        parse_array
319      when scan(OBJECT_OPEN)
320        parse_object
321      else
322        UNPARSED
323      end
324    end
325
326    def parse_array
327      result = []
328      until eos?
329        case
330        when (value = parse_value) != UNPARSED
331          result << value
332          skip(IGNORE)
333          unless scan(COLLECTION_DELIMITER) or match?(ARRAY_CLOSE)
334            raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
335          end
336        when scan(ARRAY_CLOSE)
337          break
338        when skip(IGNORE)
339          ;
340        else
341          raise ParserError, "unexpected token in array at '#{peek(20)}'!"
342        end
343      end
344      result
345    end
346
347    def parse_object
348      result = {}
349      until eos?
350        case
351        when (string = parse_string) != UNPARSED
352          skip(IGNORE)
353          unless scan(PAIR_DELIMITER)
354            raise ParserError, "expected ':' in object at '#{peek(20)}'!"
355          end
356          skip(IGNORE)
357          unless (value = parse_value).equal? UNPARSED
358            result[string] = value
359            skip(IGNORE)
360            unless scan(COLLECTION_DELIMITER) or match?(OBJECT_CLOSE)
361              raise ParserError,
362                "expected ',' or '}' in object at '#{peek(20)}'!"
363            end
364          else
365            raise ParserError, "expected value in object at '#{peek(20)}'!"
366          end
367        when scan(OBJECT_CLOSE)
368          if klassname = result['json_class']
369            klass = klassname.sub(/^:+/, '').split(/::/).inject(Object) do |p,k|
370              p.const_get(k) rescue nil
371            end
372            break unless klass and klass.json_creatable?
373            result = klass.json_create(result)
374          end
375          break
376        when skip(IGNORE)
377          ;
378        else
379          raise ParserError, "unexpected token in object at '#{peek(20)}'!"
380        end
381      end
382      result
383    end
384  end
385
386  # This class is used to create State instances, that are use to hold data
387  # while unparsing a Ruby data structure into a JSON string.
388  class State
389    # Creates a State object from _opts_, which ought to be Hash to create a
390    # new State instance configured by opts, something else to create an
391    # unconfigured instance. If _opts_ is a State object, it is just returned.
392    def self.from_state(opts)
393      case opts
394      when self
395        opts
396      when Hash
397        new(opts)
398      else
399        new
400      end
401    end
402
403    # Instantiates a new State object, configured by _opts_.
404    def initialize(opts = {})
405      @indent     = opts[:indent]     || ''
406      @space      = opts[:space]      || ''
407      @object_nl  = opts[:object_nl]  || ''
408      @array_nl   = opts[:array_nl]   || ''
409      @seen       = {}
410    end
411
412    # This string is used to indent levels in the JSON string.
413    attr_accessor :indent
414
415    # This string is used to include a space between the tokens in a JSON
416    # string.
417    attr_accessor :space
418
419    # This string is put at the end of a line that holds a JSON object (or
420    # Hash).
421    attr_accessor :object_nl
422
423    # This string is put at the end of a line that holds a JSON array.
424    attr_accessor :array_nl
425
426    # Returns _true_, if _object_ was already seen during this Unparsing run.
427    def seen?(object)
428      @seen.key?(object.__id__)
429    end
430
431    # Remember _object_, to find out if it was already encountered (to find out
432    # if a cyclic data structure is unparsed).
433    def remember(object)
434      @seen[object.__id__] = true
435    end
436
437    # Forget _object_ for this Unparsing run.
438    def forget(object)
439      @seen.delete object.__id__
440    end
441  end
442
443  module_function
444
445  # Convert _string_ from UTF8 encoding to UTF16 (big endian) encoding and
446  # return it.
447  def utf8_to_utf16(string)
448    JSON::UTF8toUTF16.iconv(string).unpack('H*')[0]
449  end
450
451  # Convert _string_ from UTF16 (big endian) encoding to UTF8 encoding and
452  # return it.
453  def utf16_to_utf8(string)
454    bytes = '' << string[0, 2].to_i(16) << string[2, 2].to_i(16)
455    JSON::UTF16toUTF8.iconv(bytes)
456  end
457
458  # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
459  # UTF16 big endian characters as \u????, and return it.
460  def utf8_to_json(string)
461    i, n, result = 0, string.size, ''
462    while i < n
463      char = string[i]
464      case
465      when char == ?\b then result << '\b'
466      when char == ?\t then result << '\t'
467      when char == ?\n then result << '\n'
468      when char == ?\f then result << '\f'
469      when char == ?\r then result << '\r'
470      when char == ?"  then result << '\"'
471      when char == ?\\ then result << '\\\\'
472      when char == ?/ then result << '\/'
473      when char.between?(0x0, 0x1f) then result << "\\u%04x" % char
474      when char.between?(0x20, 0x7f) then result << char
475      when !(JSON.support_unicode? && $KCODE == 'UTF8')
476        # if utf8 mode is switched off or unicode not supported, just pass
477        # bytes through:
478        result << char
479      when char & 0xe0 == 0xc0
480        result << '\u' << utf8_to_utf16(string[i, 2])
481        i += 1
482      when char & 0xf0 == 0xe0
483        result << '\u' << utf8_to_utf16(string[i, 3])
484        i += 2
485      when char & 0xf8 == 0xf0
486        result << '\u' << utf8_to_utf16(string[i, 4])
487        i += 3
488      when char & 0xfc == 0xf8
489        result << '\u' << utf8_to_utf16(string[i, 5])
490        i += 4
491      when char & 0xfe == 0xfc
492        result << '\u' << utf8_to_utf16(string[i, 6])
493        i += 5
494      else
495        raise JSON::UnparserError, "Encountered unknown UTF-8 byte: %x!" % char
496      end
497      i += 1
498    end
499    result
500  end
501
502  # Parse the JSON string _source_ into a Ruby data structure and return it.
503  def parse(source)
504    Parser.new(source).parse
505  end
506
507  # Unparse the Ruby data structure _obj_ into a single line JSON string and
508  # return it. _state_ is a JSON::State object, that can be used to configure
509  # the output further.
510  def unparse(obj, state = nil)
511    obj.to_json(JSON::State.from_state(state))
512  end
513
514  alias generate unparse
515
516  # Unparse the Ruby data structure _obj_ into a JSON string and return it.
517  # The returned string is a prettier form of the string returned by #unparse.
518  def pretty_unparse(obj)
519    state = JSON::State.new(
520      :indent     => '  ',
521      :space      => ' ',
522      :object_nl  => "\n",
523      :array_nl   => "\n"
524    )
525    obj.to_json(state)
526  end
527
528  alias pretty_generate pretty_unparse
529end
530
531class Object
532  # Converts this object to a string (calling #to_s), converts
533  # it to a JSON string, and returns the result. This is a fallback, if no
534  # special method #to_json was defined for some object.
535  # _state_ is a JSON::State object, that can also be used
536  # to configure the produced JSON string output further.
537
538  def to_json(*) to_s.to_json end
539end
540
541class Hash
542  # Returns a JSON string containing a JSON object, that is unparsed from
543  # this Hash instance.
544  # _state_ is a JSON::State object, that can also be used to configure the
545  # produced JSON string output further.
546  # _depth_ is used to find out nesting depth, to indent accordingly.
547  def to_json(state = nil, depth = 0)
548    state = JSON::State.from_state(state)
549    json_check_circular(state) { json_transform(state, depth) }
550  end
551
552  private
553
554  def json_check_circular(state)
555    if state
556      state.seen?(self) and raise JSON::CircularDatastructure,
557          "circular data structures not supported!"
558      state.remember self
559    end
560    yield
561  ensure
562    state and state.forget self
563  end
564
565  def json_shift(state, depth)
566    state and not state.object_nl.empty? or return ''
567    state.indent * depth
568  end
569
570  def json_transform(state, depth)
571    delim = ','
572    delim << state.object_nl if state
573    result = '{'
574    result << state.object_nl if state
575    result << map { |key,value|
576      json_shift(state, depth + 1) <<
577        key.to_s.to_json(state, depth + 1) <<
578        ':' << state.space << value.to_json(state, depth + 1)
579    }.join(delim)
580    result << state.object_nl if state
581    result << json_shift(state, depth)
582    result << '}'
583    result
584  end
585end
586
587class Array
588  # Returns a JSON string containing a JSON array, that is unparsed from
589  # this Array instance.
590  # _state_ is a JSON::State object, that can also be used to configure the
591  # produced JSON string output further.
592  # _depth_ is used to find out nesting depth, to indent accordingly.
593  def to_json(state = nil, depth = 0)
594    state = JSON::State.from_state(state)
595    json_check_circular(state) { json_transform(state, depth) }
596  end
597
598  private
599
600  def json_check_circular(state)
601    if state
602      state.seen?(self) and raise JSON::CircularDatastructure,
603        "circular data structures not supported!"
604      state.remember self
605    end
606    yield
607  ensure
608    state and state.forget self
609  end
610
611  def json_shift(state, depth)
612    state and not state.array_nl.empty? or return ''
613    state.indent * depth
614  end
615
616  def json_transform(state, depth)
617    delim = ','
618    delim << state.array_nl if state
619    result = '['
620    result << state.array_nl if state
621    result << map { |value|
622      json_shift(state, depth + 1) << value.to_json(state, depth + 1)
623    }.join(delim)
624    result << state.array_nl if state
625    result << json_shift(state, depth)
626    result << ']'
627    result
628  end
629end
630
631class Integer
632  # Returns a JSON string representation for this Integer number.
633  def to_json(*) to_s end
634end
635
636class Float
637  # Returns a JSON string representation for this Float number.
638  def to_json(*) to_s end
639end
640
641class String
642  # This string should be encoded with UTF-8 (if JSON unicode support is
643  # enabled). A call to this method returns a JSON string
644  # encoded with UTF16 big endian characters as \u????. If
645  # JSON.support_unicode? is false only control characters are encoded this
646  # way, all 8-bit bytes are just passed through.
647  def to_json(*)
648    '"' << JSON::utf8_to_json(self) << '"'
649  end
650
651  # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
652  # key "raw"). The Ruby String can be created by this class method.
653  def self.json_create(o)
654    o['raw'].pack('C*')
655  end
656
657  # This method creates a raw object, that can be nested into other data
658  # structures and will be unparsed as a raw string.
659  def to_json_raw_object
660    {
661      'json_class'  => self.class.name,
662      'raw'         => self.unpack('C*'),
663    }
664  end
665
666  # This method should be used, if you want to convert raw strings to JSON
667  # instead of UTF-8 strings, e. g. binary data (and JSON Unicode support is
668  # enabled).
669  def to_json_raw(*args)
670    to_json_raw_object.to_json(*args)
671  end
672end
673
674class TrueClass
675  # Returns a JSON string for true: 'true'.
676  def to_json(*) to_s end
677end
678
679class FalseClass
680  # Returns a JSON string for false: 'false'.
681  def to_json(*) to_s end
682end
683
684class NilClass
685  # Returns a JSON string for nil: 'null'.
686  def to_json(*) 'null' end
687end
688
689module Kernel
690  # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
691  # one line.
692  def j(*objs)
693    objs.each do |obj|
694      puts JSON::generate(obj)
695    end
696    nil
697  end
698
699  # Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
700  # indentation and over many lines.
701  def jj(*objs)
702    objs.each do |obj|
703      puts JSON::pretty_generate(obj)
704    end
705    nil
706  end
707end
708
709class Class
710  # Returns true, if this class can be used to create an instance
711  # from a serialised JSON string. The class has to implement a class
712  # method _json_create_ that expects a hash as first parameter, which includes
713  # the required data.
714  def json_creatable?
715    respond_to?(:json_create)
716  end
717end
718  # vim: set et sw=2 ts=2:
Note: See TracBrowser for help on using the browser.