~ubuntu-branches/ubuntu/oneiric/mcollective/oneiric-proposed

« back to all changes in this revision

Viewing changes to lib/mcollective/vendor/json/lib/json/pure/generator.rb

  • Committer: Bazaar Package Importer
  • Author(s): Marc Cluet, Marc Cluet, Chuck Short
  • Date: 2011-05-05 07:37:54 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20110505073754-klk1jkz8afi4fomx
Tags: 1.2.0-0ubuntu1
[Marc Cluet]
* Update to 1.2.0
* Build for Oneiric.

[Chuck Short]
* Drop ruby from mcollective, mcollective-middleware, mcollective-client
  since its a dependency of mcollective-common.
* Bump standards to 3.9.2.
* Fix up lintian warnings.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
module JSON
 
2
  MAP = {
 
3
    "\x0" => '\u0000',
 
4
    "\x1" => '\u0001',
 
5
    "\x2" => '\u0002',
 
6
    "\x3" => '\u0003',
 
7
    "\x4" => '\u0004',
 
8
    "\x5" => '\u0005',
 
9
    "\x6" => '\u0006',
 
10
    "\x7" => '\u0007',
 
11
    "\b"  =>  '\b',
 
12
    "\t"  =>  '\t',
 
13
    "\n"  =>  '\n',
 
14
    "\xb" => '\u000b',
 
15
    "\f"  =>  '\f',
 
16
    "\r"  =>  '\r',
 
17
    "\xe" => '\u000e',
 
18
    "\xf" => '\u000f',
 
19
    "\x10" => '\u0010',
 
20
    "\x11" => '\u0011',
 
21
    "\x12" => '\u0012',
 
22
    "\x13" => '\u0013',
 
23
    "\x14" => '\u0014',
 
24
    "\x15" => '\u0015',
 
25
    "\x16" => '\u0016',
 
26
    "\x17" => '\u0017',
 
27
    "\x18" => '\u0018',
 
28
    "\x19" => '\u0019',
 
29
    "\x1a" => '\u001a',
 
30
    "\x1b" => '\u001b',
 
31
    "\x1c" => '\u001c',
 
32
    "\x1d" => '\u001d',
 
33
    "\x1e" => '\u001e',
 
34
    "\x1f" => '\u001f',
 
35
    '"'   =>  '\"',
 
36
    '\\'  =>  '\\\\',
 
37
  } # :nodoc:
 
38
 
 
39
  # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
 
40
  # UTF16 big endian characters as \u????, and return it.
 
41
  if defined?(::Encoding)
 
42
    def utf8_to_json(string) # :nodoc:
 
43
      string = string.dup
 
44
      string << '' # XXX workaround: avoid buffer sharing
 
45
      string.force_encoding(::Encoding::ASCII_8BIT)
 
46
      string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
 
47
      string.force_encoding(::Encoding::UTF_8)
 
48
      string
 
49
    end
 
50
 
 
51
    def utf8_to_json_ascii(string) # :nodoc:
 
52
      string = string.dup
 
53
      string << '' # XXX workaround: avoid buffer sharing
 
54
      string.force_encoding(::Encoding::ASCII_8BIT)
 
55
      string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
 
56
      string.gsub!(/(
 
57
                      (?:
 
58
                        [\xc2-\xdf][\x80-\xbf]    |
 
59
                        [\xe0-\xef][\x80-\xbf]{2} |
 
60
                        [\xf0-\xf4][\x80-\xbf]{3}
 
61
                      )+ |
 
62
                      [\x80-\xc1\xf5-\xff]       # invalid
 
63
                    )/nx) { |c|
 
64
                      c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
 
65
                      s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
 
66
                      s.gsub!(/.{4}/n, '\\\\u\&')
 
67
                    }
 
68
      string.force_encoding(::Encoding::UTF_8)
 
69
      string
 
70
    rescue => e
 
71
      raise GeneratorError, "Caught #{e.class}: #{e}"
 
72
    end
 
73
  else
 
74
    def utf8_to_json(string) # :nodoc:
 
75
      string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
 
76
    end
 
77
 
 
78
    def utf8_to_json_ascii(string) # :nodoc:
 
79
      string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
 
80
      string.gsub!(/(
 
81
                      (?:
 
82
                        [\xc2-\xdf][\x80-\xbf]    |
 
83
                        [\xe0-\xef][\x80-\xbf]{2} |
 
84
                        [\xf0-\xf4][\x80-\xbf]{3}
 
85
                      )+ |
 
86
                      [\x80-\xc1\xf5-\xff]       # invalid
 
87
                    )/nx) { |c|
 
88
        c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
 
89
        s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
 
90
        s.gsub!(/.{4}/n, '\\\\u\&')
 
91
      }
 
92
      string
 
93
    rescue => e
 
94
      raise GeneratorError, "Caught #{e.class}: #{e}"
 
95
    end
 
96
  end
 
97
  module_function :utf8_to_json, :utf8_to_json_ascii
 
98
 
 
99
  module Pure
 
100
    module Generator
 
101
      # This class is used to create State instances, that are use to hold data
 
102
      # while generating a JSON text from a a Ruby data structure.
 
103
      class State
 
104
        # Creates a State object from _opts_, which ought to be Hash to create
 
105
        # a new State instance configured by _opts_, something else to create
 
106
        # an unconfigured instance. If _opts_ is a State object, it is just
 
107
        # returned.
 
108
        def self.from_state(opts)
 
109
          case
 
110
          when self === opts
 
111
            opts
 
112
          when opts.respond_to?(:to_hash)
 
113
            new(opts.to_hash)
 
114
          when opts.respond_to?(:to_h)
 
115
            new(opts.to_h)
 
116
          else
 
117
            SAFE_STATE_PROTOTYPE.dup
 
118
          end
 
119
        end
 
120
 
 
121
        # Instantiates a new State object, configured by _opts_.
 
122
        #
 
123
        # _opts_ can have the following keys:
 
124
        #
 
125
        # * *indent*: a string used to indent levels (default: ''),
 
126
        # * *space*: a string that is put after, a : or , delimiter (default: ''),
 
127
        # * *space_before*: a string that is put before a : pair delimiter (default: ''),
 
128
        # * *object_nl*: a string that is put at the end of a JSON object (default: ''), 
 
129
        # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
 
130
        # * *check_circular*: is deprecated now, use the :max_nesting option instead,
 
131
        # * *max_nesting*: sets the maximum level of data structure nesting in
 
132
        #   the generated JSON, max_nesting = 0 if no maximum should be checked.
 
133
        # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
 
134
        #   generated, otherwise an exception is thrown, if these values are
 
135
        #   encountered. This options defaults to false.
 
136
        def initialize(opts = {})
 
137
          @indent         = ''
 
138
          @space          = ''
 
139
          @space_before   = ''
 
140
          @object_nl      = ''
 
141
          @array_nl       = ''
 
142
          @allow_nan      = false
 
143
          @ascii_only     = false
 
144
          configure opts
 
145
        end
 
146
 
 
147
        # This string is used to indent levels in the JSON text.
 
148
        attr_accessor :indent
 
149
 
 
150
        # This string is used to insert a space between the tokens in a JSON
 
151
        # string.
 
152
        attr_accessor :space
 
153
 
 
154
        # This string is used to insert a space before the ':' in JSON objects.
 
155
        attr_accessor :space_before
 
156
 
 
157
        # This string is put at the end of a line that holds a JSON object (or
 
158
        # Hash).
 
159
        attr_accessor :object_nl
 
160
 
 
161
        # This string is put at the end of a line that holds a JSON array.
 
162
        attr_accessor :array_nl
 
163
 
 
164
        # This integer returns the maximum level of data structure nesting in
 
165
        # the generated JSON, max_nesting = 0 if no maximum is checked.
 
166
        attr_accessor :max_nesting
 
167
 
 
168
        # This integer returns the current depth data structure nesting in the
 
169
        # generated JSON.
 
170
        attr_accessor :depth
 
171
 
 
172
        def check_max_nesting # :nodoc:
 
173
          return if @max_nesting.zero?
 
174
          current_nesting = depth + 1
 
175
          current_nesting > @max_nesting and
 
176
            raise NestingError, "nesting of #{current_nesting} is too deep"
 
177
        end
 
178
 
 
179
        # Returns true, if circular data structures are checked,
 
180
        # otherwise returns false.
 
181
        def check_circular?
 
182
          !@max_nesting.zero?
 
183
        end
 
184
 
 
185
        # Returns true if NaN, Infinity, and -Infinity should be considered as
 
186
        # valid JSON and output.
 
187
        def allow_nan?
 
188
          @allow_nan
 
189
        end
 
190
 
 
191
        def ascii_only?
 
192
          @ascii_only
 
193
        end
 
194
 
 
195
        # Configure this State instance with the Hash _opts_, and return
 
196
        # itself.
 
197
        def configure(opts)
 
198
          @indent         = opts[:indent] if opts.key?(:indent)
 
199
          @space          = opts[:space] if opts.key?(:space)
 
200
          @space_before   = opts[:space_before] if opts.key?(:space_before)
 
201
          @object_nl      = opts[:object_nl] if opts.key?(:object_nl)
 
202
          @array_nl       = opts[:array_nl] if opts.key?(:array_nl)
 
203
          @allow_nan      = !!opts[:allow_nan] if opts.key?(:allow_nan)
 
204
          @ascii_only     = opts[:ascii_only] if opts.key?(:ascii_only)
 
205
          @depth          = opts[:depth] || 0
 
206
          if !opts.key?(:max_nesting) # defaults to 19
 
207
            @max_nesting = 19
 
208
          elsif opts[:max_nesting]
 
209
            @max_nesting = opts[:max_nesting]
 
210
          else
 
211
            @max_nesting = 0
 
212
          end
 
213
          self
 
214
        end
 
215
 
 
216
        # Returns the configuration instance variables as a hash, that can be
 
217
        # passed to the configure method.
 
218
        def to_h
 
219
          result = {}
 
220
          for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only depth]
 
221
            result[iv.intern] = instance_variable_get("@#{iv}")
 
222
          end
 
223
          result
 
224
        end
 
225
 
 
226
        # Generates a valid JSON document from object +obj+ and returns the
 
227
        # result. If no valid JSON document can be created this method raises a
 
228
        # GeneratorError exception.
 
229
        def generate(obj)
 
230
          result = obj.to_json(self)
 
231
          if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m
 
232
            raise GeneratorError, "only generation of JSON objects or arrays allowed"
 
233
          end
 
234
          result
 
235
        end
 
236
 
 
237
        # Return the value returned by method +name+.
 
238
        def [](name)
 
239
          __send__ name
 
240
        end
 
241
      end
 
242
 
 
243
      module GeneratorMethods
 
244
        module Object
 
245
          # Converts this object to a string (calling #to_s), converts
 
246
          # it to a JSON string, and returns the result. This is a fallback, if no
 
247
          # special method #to_json was defined for some object.
 
248
          def to_json(*) to_s.to_json end
 
249
        end
 
250
 
 
251
        module Hash
 
252
          # Returns a JSON string containing a JSON object, that is unparsed from
 
253
          # this Hash instance.
 
254
          # _state_ is a JSON::State object, that can also be used to configure the
 
255
          # produced JSON string output further.
 
256
          # _depth_ is used to find out nesting depth, to indent accordingly.
 
257
          def to_json(state = nil, *)
 
258
            state = State.from_state(state)
 
259
            state.check_max_nesting
 
260
            json_transform(state)
 
261
          end
 
262
 
 
263
          private
 
264
 
 
265
          def json_shift(state)
 
266
            state.object_nl.empty? or return ''
 
267
            state.indent * state.depth
 
268
          end
 
269
 
 
270
          def json_transform(state)
 
271
            delim = ','
 
272
            delim << state.object_nl
 
273
            result = '{'
 
274
            result << state.object_nl
 
275
            depth = state.depth += 1
 
276
            first = true
 
277
            indent = !state.object_nl.empty?
 
278
            each { |key,value|
 
279
              result << delim unless first
 
280
              result << state.indent * depth if indent
 
281
              result << key.to_s.to_json(state)
 
282
              result << state.space_before
 
283
              result << ':'
 
284
              result << state.space
 
285
              result << value.to_json(state)
 
286
              first = false
 
287
            }
 
288
            depth = state.depth -= 1
 
289
            result << state.object_nl
 
290
            result << state.indent * depth if indent if indent
 
291
            result << '}'
 
292
            result
 
293
          end
 
294
        end
 
295
 
 
296
        module Array
 
297
          # Returns a JSON string containing a JSON array, that is unparsed from
 
298
          # this Array instance.
 
299
          # _state_ is a JSON::State object, that can also be used to configure the
 
300
          # produced JSON string output further.
 
301
          def to_json(state = nil, *)
 
302
            state = State.from_state(state)
 
303
            state.check_max_nesting
 
304
            json_transform(state)
 
305
          end
 
306
 
 
307
          private
 
308
 
 
309
          def json_transform(state)
 
310
            delim = ','
 
311
            delim << state.array_nl
 
312
            result = '['
 
313
            result << state.array_nl
 
314
            depth = state.depth += 1
 
315
            first = true
 
316
            indent = !state.array_nl.empty?
 
317
            each { |value|
 
318
              result << delim unless first
 
319
              result << state.indent * depth if indent
 
320
              result << value.to_json(state)
 
321
              first = false
 
322
            }
 
323
            depth = state.depth -= 1
 
324
            result << state.array_nl
 
325
            result << state.indent * depth if indent
 
326
            result << ']'
 
327
          end
 
328
        end
 
329
 
 
330
        module Integer
 
331
          # Returns a JSON string representation for this Integer number.
 
332
          def to_json(*) to_s end
 
333
        end
 
334
 
 
335
        module Float
 
336
          # Returns a JSON string representation for this Float number.
 
337
          def to_json(state = nil, *)
 
338
            state = State.from_state(state)
 
339
            case
 
340
            when infinite?
 
341
              if state.allow_nan?
 
342
                to_s
 
343
              else
 
344
                raise GeneratorError, "#{self} not allowed in JSON"
 
345
              end
 
346
            when nan?
 
347
              if state.allow_nan?
 
348
                to_s
 
349
              else
 
350
                raise GeneratorError, "#{self} not allowed in JSON"
 
351
              end
 
352
            else
 
353
              to_s
 
354
            end
 
355
          end
 
356
        end
 
357
 
 
358
        module String
 
359
          if defined?(::Encoding)
 
360
            # This string should be encoded with UTF-8 A call to this method
 
361
            # returns a JSON string encoded with UTF16 big endian characters as
 
362
            # \u????.
 
363
            def to_json(state = nil, *args)
 
364
              state = State.from_state(state)
 
365
              if encoding == ::Encoding::UTF_8
 
366
                string = self
 
367
              else
 
368
                string = encode(::Encoding::UTF_8)
 
369
              end
 
370
              if state.ascii_only?
 
371
                '"' << JSON.utf8_to_json_ascii(string) << '"'
 
372
              else
 
373
                '"' << JSON.utf8_to_json(string) << '"'
 
374
              end
 
375
            end
 
376
          else
 
377
            # This string should be encoded with UTF-8 A call to this method
 
378
            # returns a JSON string encoded with UTF16 big endian characters as
 
379
            # \u????.
 
380
            def to_json(state = nil, *args)
 
381
              state = State.from_state(state)
 
382
              if state.ascii_only?
 
383
                '"' << JSON.utf8_to_json_ascii(self) << '"'
 
384
              else
 
385
                '"' << JSON.utf8_to_json(self) << '"'
 
386
              end
 
387
            end
 
388
          end
 
389
 
 
390
          # Module that holds the extinding methods if, the String module is
 
391
          # included.
 
392
          module Extend
 
393
            # Raw Strings are JSON Objects (the raw bytes are stored in an
 
394
            # array for the key "raw"). The Ruby String can be created by this
 
395
            # module method.
 
396
            def json_create(o)
 
397
              o['raw'].pack('C*')
 
398
            end
 
399
          end
 
400
 
 
401
          # Extends _modul_ with the String::Extend module.
 
402
          def self.included(modul)
 
403
            modul.extend Extend
 
404
          end
 
405
 
 
406
          # This method creates a raw object hash, that can be nested into
 
407
          # other data structures and will be unparsed as a raw string. This
 
408
          # method should be used, if you want to convert raw strings to JSON
 
409
          # instead of UTF-8 strings, e. g. binary data.
 
410
          def to_json_raw_object
 
411
            {
 
412
              JSON.create_id  => self.class.name,
 
413
              'raw'           => self.unpack('C*'),
 
414
            }
 
415
          end
 
416
 
 
417
          # This method creates a JSON text from the result of
 
418
          # a call to to_json_raw_object of this String.
 
419
          def to_json_raw(*args)
 
420
            to_json_raw_object.to_json(*args)
 
421
          end
 
422
        end
 
423
 
 
424
        module TrueClass
 
425
          # Returns a JSON string for true: 'true'.
 
426
          def to_json(*) 'true' end
 
427
        end
 
428
 
 
429
        module FalseClass
 
430
          # Returns a JSON string for false: 'false'.
 
431
          def to_json(*) 'false' end
 
432
        end
 
433
 
 
434
        module NilClass
 
435
          # Returns a JSON string for nil: 'null'.
 
436
          def to_json(*) 'null' end
 
437
        end
 
438
      end
 
439
    end
 
440
  end
 
441
end