diff options
56 files changed, 1673 insertions, 4005 deletions
@@ -5,3 +5,4 @@ pkg .idea java/Json.iml Gemfile.lock +.rvmrc diff --git a/.travis.yml b/.travis.yml index 2ebf7fc..cb74e2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,11 @@ bundler_args: --binstubs rvm: - 1.8.7 - 1.9.2 + - 1.9.3 - rbx - rbx-2.0 - ree - jruby - ruby-head - - 1.8.6 script: "bundle exec rake" @@ -1,6 +1,16 @@ -2011-07-04 (1.5.4) +2011-09-18 (1.6.1) + * Using -target 1.5 to force Java bits to compile with 1.5. +2011-09-12 (1.6.0) + * Extract utilities (prettifier and GUI-editor) in its own gem json-utils. + * Split json/add/core into different files for classes to be serialised. +2011-08-31 (1.5.4) * Fix memory leak when used from multiple JRuby. (Patch by jfirebaugh@github). + * Apply patch by Eric Wong <nocode@yhbt.net> that fixes garbage collection problem + reported in https://github.com/flori/json/issues/46. + * Add :quirks_mode option to parser and generator. + * Add support for Rational and Complex number additions via json/add/complex + and json/add/rational requires. 2011-06-20 (1.5.3) * Alias State#configure method as State#merge to increase duck type synonymy with Hash. * Add as_json methods in json/add/core, so rails can create its json objects @@ -4,7 +4,12 @@ rescue LoadError end require 'rbconfig' -include Config +begin + include RbConfig +rescue NameError + include Config +end + require 'rake/clean' CLOBBER.include Dir['benchmarks/data/*.{dat,log}'], 'doc', 'Gemfile.lock' @@ -91,10 +96,6 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.add_development_dependency 'bullshit' s.add_development_dependency 'sdoc' s.add_development_dependency 'rake', '~>0.9.2' - s.add_dependency 'spruz', '~>0.2.8' - - s.bindir = "bin" - s.executables = [ "edit_json.rb", "prettify_json.rb" ] s.extra_rdoc_files << 'README.rdoc' s.rdoc_options << @@ -136,9 +137,6 @@ if defined?(Gem) and defined?(Gem::PackageTask) s.add_development_dependency 'bullshit' s.add_development_dependency 'sdoc' - s.bindir = "bin" - s.executables = [ "edit_json.rb", "prettify_json.rb" ] - s.extra_rdoc_files << 'README.rdoc' s.rdoc_options << '--title' << 'JSON implemention for Ruby' << '--main' << 'README.rdoc' @@ -228,13 +226,13 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby' rm_rf JAVA_PARSER_SRC end - JRUBY_JAR = File.join(Config::CONFIG["libdir"], "jruby.jar") + JRUBY_JAR = File.join(CONFIG["libdir"], "jruby.jar") if File.exist?(JRUBY_JAR) JAVA_SOURCES.each do |src| classpath = (Dir['java/lib/*.jar'] << 'java/src' << JRUBY_JAR) * ':' obj = src.sub(/\.java\Z/, '.class') file obj => src do - sh 'javac', '-classpath', classpath, '-source', '1.5', src + sh 'javac', '-classpath', classpath, '-source', '1.5', '-target', '1.5', src end JAVA_CLASSES << obj end @@ -298,7 +296,9 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby' task :create_jar => [ :create_parser_jar, :create_generator_jar ] desc "Build all gems and archives for a new release of the jruby extension." - task :release => [ :clean, :version, :jruby_gem ] + task :build => [ :clean, :version, :jruby_gem ] + + task :release => :build else desc "Compiling extension" task :compile => [ EXT_PARSER_DL, EXT_GENERATOR_DL ] @@ -400,7 +400,9 @@ else task :ragel_dot => [ :ragel_dot_png, :ragel_dot_ps ] desc "Build all gems and archives for a new release of json and json_pure." - task :release => [ :clean, :gemspec, :package ] + task :build => [ :clean, :gemspec, :package ] + + task :release => :build end desc "Compile in the the source directory" @@ -1 +1 @@ -1.5.4 +1.6.1 diff --git a/bin/edit_json.rb b/bin/edit_json.rb deleted file mode 100755 index 04a8189..0000000 --- a/bin/edit_json.rb +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env ruby -require 'json/editor' - -filename, encoding = ARGV -JSON::Editor.start(encoding) do |window| - if filename - window.file_open(filename) - end -end diff --git a/bin/prettify_json.rb b/bin/prettify_json.rb deleted file mode 100755 index 3c53183..0000000 --- a/bin/prettify_json.rb +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env ruby - -require 'json' -require 'fileutils' -include FileUtils -require 'spruz/go' -include Spruz::GO - -opts = go 'slhi:', args = ARGV.dup -if opts['h'] || opts['l'] && opts['s'] - puts <<EOT -Usage: #{File.basename($0)} [OPTION] [FILE] - -If FILE is skipped, this scripts waits for input from STDIN. Otherwise -FILE is opened, read, and used as input for the prettifier. - -OPTION can be - -s to output the shortest possible JSON (precludes -l) - -l to output a longer, better formatted JSON (precludes -s) - -i EXT prettifies FILE in place, saving a backup to FILE.EXT - -h this help -EOT - exit 0 -end - -json_opts = { :max_nesting => false, :create_additions => false } - -document = - if filename = args.first or filename == '-' - File.read(filename) - else - STDIN.read - end - -json = JSON.parse document, json_opts - -output = if opts['s'] - JSON.fast_generate json, json_opts -else # default is -l - JSON.pretty_generate json, json_opts -end - -if opts['i'] && filename - cp filename, "#{filename}.#{opts['i']}" - File.open(filename, 'w') { |f| f.puts output } -else - puts output -end diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index e7d71e9..44dc3fa 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -13,8 +13,8 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject, static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before, i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only, - i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send, - i_respond_to_p, i_match, i_keys, i_depth, i_dup; + i_quirks_mode, i_pack, i_unpack, i_create_id, i_extend, i_key_p, + i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth, i_dup; /* * Copyright 2001-2004 Unicode, Inc. @@ -349,6 +349,16 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len) } } +static void fbuffer_append_str(FBuffer *fb, VALUE str) +{ + const char *newstr = StringValuePtr(str); + unsigned long len = RSTRING_LEN(str); + + RB_GC_GUARD(str); + + fbuffer_append(fb, newstr, len); +} + static void fbuffer_append_char(FBuffer *fb, char newchr) { fbuffer_inc_capa(fb, 1); @@ -688,6 +698,8 @@ static VALUE cState_configure(VALUE self, VALUE opts) state->allow_nan = RTEST(tmp); tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only)); state->ascii_only = RTEST(tmp); + tmp = rb_hash_aref(opts, ID2SYM(i_quirks_mode)); + state->quirks_mode = RTEST(tmp); return self; } @@ -708,6 +720,7 @@ static VALUE cState_to_h(VALUE self) rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len)); rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse); + rb_hash_aset(result, ID2SYM(i_quirks_mode), state->quirks_mode ? Qtrue : Qfalse); rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting)); rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth)); return result; @@ -852,7 +865,7 @@ static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_S static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) { VALUE tmp = rb_funcall(obj, i_to_s, 0); - fbuffer_append(buffer, RSTRING_PAIR(tmp)); + fbuffer_append_str(buffer, tmp); } static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) @@ -869,7 +882,7 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp)); } } - fbuffer_append(buffer, RSTRING_PAIR(tmp)); + fbuffer_append_str(buffer, tmp); } static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj) @@ -897,7 +910,7 @@ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *s } else if (rb_respond_to(obj, i_to_json)) { tmp = rb_funcall(obj, i_to_json, 1, Vstate); Check_Type(tmp, T_STRING); - fbuffer_append(buffer, RSTRING_PAIR(tmp)); + fbuffer_append_str(buffer, tmp); } else { tmp = rb_funcall(obj, i_to_s, 0); Check_Type(tmp, T_STRING); @@ -961,11 +974,14 @@ static VALUE cState_generate(VALUE self, VALUE obj) { VALUE result = cState_partial_generate(self, obj); VALUE re, args[2]; - args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z"); - args[1] = CRegexp_MULTILINE; - re = rb_class_new_instance(2, args, rb_cRegexp); - if (NIL_P(rb_funcall(re, i_match, 1, result))) { - rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed"); + GET_STATE(self); + if (!state->quirks_mode) { + args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z"); + args[1] = CRegexp_MULTILINE; + re = rb_class_new_instance(2, args, rb_cRegexp); + if (NIL_P(rb_funcall(re, i_match, 1, result))) { + rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed"); + } } return result; } @@ -985,6 +1001,8 @@ static VALUE cState_generate(VALUE self, VALUE obj) * * *allow_nan*: true if NaN, Infinity, and -Infinity should be * generated, otherwise an exception is thrown, if these values are * encountered. This options defaults to false. + * * *quirks_mode*: Enables quirks_mode for parser, that is for example + * generating single JSON values instead of documents is possible. */ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self) { @@ -1288,6 +1306,29 @@ static VALUE cState_ascii_only_p(VALUE self) } /* + * call-seq: quirks_mode? + * + * Returns true, if quirks mode is enabled. Otherwise returns false. + */ +static VALUE cState_quirks_mode_p(VALUE self) +{ + GET_STATE(self); + return state->quirks_mode ? Qtrue : Qfalse; +} + +/* + * call-seq: quirks_mode=(enable) + * + * If set to true, enables the quirks_mode mode. + */ +static VALUE cState_quirks_mode_set(VALUE self, VALUE enable) +{ + GET_STATE(self); + state->quirks_mode = RTEST(enable); + return Qnil; +} + +/* * call-seq: depth * * This integer returns the current depth of data structure nesting. @@ -1345,6 +1386,9 @@ void Init_generator() rb_define_method(cState, "check_circular?", cState_check_circular_p, 0); rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0); rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0); + rb_define_method(cState, "quirks_mode?", cState_quirks_mode_p, 0); + rb_define_method(cState, "quirks_mode", cState_quirks_mode_p, 0); + rb_define_method(cState, "quirks_mode=", cState_quirks_mode_set, 1); rb_define_method(cState, "depth", cState_depth, 0); rb_define_method(cState, "depth=", cState_depth_set, 1); rb_define_method(cState, "configure", cState_configure, 1); @@ -1392,6 +1436,7 @@ void Init_generator() i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_ascii_only = rb_intern("ascii_only"); + i_quirks_mode = rb_intern("quirks_mode"); i_depth = rb_intern("depth"); i_pack = rb_intern("pack"); i_unpack = rb_intern("unpack"); diff --git a/ext/json/ext/generator/generator.h b/ext/json/ext/generator/generator.h index ee496fe..f882ea0 100644 --- a/ext/json/ext/generator/generator.h +++ b/ext/json/ext/generator/generator.h @@ -45,7 +45,10 @@ #define RSTRING_LEN(string) RSTRING(string)->len #endif -#define RSTRING_PAIR(string) RSTRING_PTR(string), RSTRING_LEN(string) +/* We don't need to guard objects for rbx, so let's do nothing at all. */ +#ifndef RB_GC_GUARD +#define RB_GC_GUARD(object) +#endif /* fbuffer implementation */ @@ -123,6 +126,7 @@ typedef struct JSON_Generator_StateStruct { long max_nesting; char allow_nan; char ascii_only; + char quirks_mode; long depth; } JSON_Generator_State; diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 5cee27d..b7d0b68 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -78,15 +78,16 @@ static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, - i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, - i_array_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_leftshift; + i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_quirks_mode, + i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match, + i_match_string, i_aset, i_leftshift; -#line 108 "parser.rl" +#line 109 "parser.rl" -#line 90 "parser.c" +#line 91 "parser.c" static const int JSON_object_start = 1; static const int JSON_object_first_final = 27; static const int JSON_object_error = 0; @@ -94,7 +95,7 @@ static const int JSON_object_error = 0; static const int JSON_object_en_main = 1; -#line 148 "parser.rl" +#line 150 "parser.rl" static const char *JSON_parse_object(JSON_Parser *json, const char *p, const char *pe, VALUE *result) @@ -110,14 +111,14 @@ static const char *JSON_parse_object(JSON_Parser *json, const char *p, const cha *result = NIL_P(object_class) ? rb_hash_new() : rb_class_new_instance(0, 0, object_class); -#line 114 "parser.c" +#line 115 "parser.c" { cs = JSON_object_start; } -#line 163 "parser.rl" +#line 165 "parser.rl" -#line 121 "parser.c" +#line 122 "parser.c" { if ( p == pe ) goto _test_eof; @@ -145,7 +146,7 @@ case 2: goto st2; goto st0; tr2: -#line 131 "parser.rl" +#line 132 "parser.rl" { const char *np; json->parsing_name = 1; @@ -158,7 +159,7 @@ st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 162 "parser.c" +#line 163 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -225,7 +226,7 @@ case 8: goto st8; goto st0; tr11: -#line 116 "parser.rl" +#line 117 "parser.rl" { VALUE v = Qnil; const char *np = JSON_parse_value(json, p, pe, &v); @@ -245,7 +246,7 @@ st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 249 "parser.c" +#line 250 "parser.c" switch( (*p) ) { case 13: goto st9; case 32: goto st9; @@ -334,14 +335,14 @@ case 18: goto st9; goto st18; tr4: -#line 139 "parser.rl" +#line 140 "parser.rl" { p--; {p++; cs = 27; goto _out;} } goto st27; st27: if ( ++p == pe ) goto _test_eof27; case 27: -#line 345 "parser.c" +#line 346 "parser.c" goto st0; st19: if ( ++p == pe ) @@ -439,7 +440,7 @@ case 26: _out: {} } -#line 164 "parser.rl" +#line 166 "parser.rl" if (cs >= JSON_object_first_final) { if (json->create_additions) { @@ -458,7 +459,8 @@ case 26: } -#line 462 "parser.c" + +#line 464 "parser.c" static const int JSON_value_start = 1; static const int JSON_value_first_final = 21; static const int JSON_value_error = 0; @@ -466,7 +468,7 @@ static const int JSON_value_error = 0; static const int JSON_value_en_main = 1; -#line 262 "parser.rl" +#line 265 "parser.rl" static const char *JSON_parse_value(JSON_Parser *json, const char *p, const char *pe, VALUE *result) @@ -474,14 +476,14 @@ static const char *JSON_parse_value(JSON_Parser *json, const char *p, const char int cs = EVIL; -#line 478 "parser.c" +#line 480 "parser.c" { cs = JSON_value_start; } -#line 269 "parser.rl" +#line 272 "parser.rl" -#line 485 "parser.c" +#line 487 "parser.c" { if ( p == pe ) goto _test_eof; @@ -506,17 +508,17 @@ st0: cs = 0; goto _out; tr0: -#line 210 "parser.rl" +#line 213 "parser.rl" { const char *np = JSON_parse_string(json, p, pe, result); if (np == NULL) { p--; {p++; cs = 21; goto _out;} } else {p = (( np))-1;} } goto st21; tr2: -#line 215 "parser.rl" +#line 218 "parser.rl" { - const char *np; - if(pe > p + 9 && !strncmp(MinusInfinity, p, 9)) { + char *np; + if(pe > p + 9 - json->quirks_mode && !strncmp(MinusInfinity, p, 9)) { if (json->allow_nan) { *result = CMinusInfinity; {p = (( p + 10))-1;} @@ -533,7 +535,7 @@ tr2: } goto st21; tr5: -#line 233 "parser.rl" +#line 236 "parser.rl" { const char *np; json->current_nesting++; @@ -543,7 +545,7 @@ tr5: } goto st21; tr9: -#line 241 "parser.rl" +#line 244 "parser.rl" { const char *np; json->current_nesting++; @@ -553,7 +555,7 @@ tr9: } goto st21; tr16: -#line 203 "parser.rl" +#line 206 "parser.rl" { if (json->allow_nan) { *result = CInfinity; @@ -563,7 +565,7 @@ tr16: } goto st21; tr18: -#line 196 "parser.rl" +#line 199 "parser.rl" { if (json->allow_nan) { *result = CNaN; @@ -573,19 +575,19 @@ tr18: } goto st21; tr22: -#line 190 "parser.rl" +#line 193 "parser.rl" { *result = Qfalse; } goto st21; tr25: -#line 187 "parser.rl" +#line 190 "parser.rl" { *result = Qnil; } goto st21; tr28: -#line 193 "parser.rl" +#line 196 "parser.rl" { *result = Qtrue; } @@ -594,9 +596,9 @@ st21: if ( ++p == pe ) goto _test_eof21; case 21: -#line 249 "parser.rl" +#line 252 "parser.rl" { p--; {p++; cs = 21; goto _out;} } -#line 600 "parser.c" +#line 602 "parser.c" goto st0; st2: if ( ++p == pe ) @@ -757,7 +759,7 @@ case 20: _out: {} } -#line 270 "parser.rl" +#line 273 "parser.rl" if (cs >= JSON_value_first_final) { return p; @@ -767,15 +769,15 @@ case 20: } -#line 771 "parser.c" +#line 773 "parser.c" static const int JSON_integer_start = 1; -static const int JSON_integer_first_final = 5; +static const int JSON_integer_first_final = 3; static const int JSON_integer_error = 0; static const int JSON_integer_en_main = 1; -#line 286 "parser.rl" +#line 289 "parser.rl" static const char *JSON_parse_integer(JSON_Parser *json, const char *p, const char *pe, VALUE *result) @@ -783,15 +785,15 @@ static const char *JSON_parse_integer(JSON_Parser *json, const char *p, const ch int cs = EVIL; -#line 787 "parser.c" +#line 789 "parser.c" { cs = JSON_integer_start; } -#line 293 "parser.rl" +#line 296 "parser.rl" json->memo = p; -#line 795 "parser.c" +#line 797 "parser.c" { if ( p == pe ) goto _test_eof; @@ -803,7 +805,7 @@ case 1: case 48: goto st3; } if ( 49 <= (*p) && (*p) <= 57 ) - goto st4; + goto st5; goto st0; st0: cs = 0; @@ -815,7 +817,7 @@ case 2: if ( (*p) == 48 ) goto st3; if ( 49 <= (*p) && (*p) <= 57 ) - goto st4; + goto st5; goto st0; st3: if ( ++p == pe ) @@ -825,33 +827,33 @@ case 3: goto st0; goto tr4; tr4: -#line 283 "parser.rl" - { p--; {p++; cs = 5; goto _out;} } - goto st5; -st5: - if ( ++p == pe ) - goto _test_eof5; -case 5: -#line 836 "parser.c" - goto st0; +#line 286 "parser.rl" + { p--; {p++; cs = 4; goto _out;} } + goto st4; st4: if ( ++p == pe ) goto _test_eof4; case 4: +#line 838 "parser.c" + goto st0; +st5: + if ( ++p == pe ) + goto _test_eof5; +case 5: if ( 48 <= (*p) && (*p) <= 57 ) - goto st4; + goto st5; goto tr4; } _test_eof2: cs = 2; goto _test_eof; _test_eof3: cs = 3; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; _test_eof: {} _out: {} } -#line 295 "parser.rl" +#line 298 "parser.rl" if (cs >= JSON_integer_first_final) { long len = p - json->memo; @@ -863,15 +865,15 @@ case 4: } -#line 867 "parser.c" +#line 869 "parser.c" static const int JSON_float_start = 1; -static const int JSON_float_first_final = 10; +static const int JSON_float_first_final = 8; static const int JSON_float_error = 0; static const int JSON_float_en_main = 1; -#line 317 "parser.rl" +#line 320 "parser.rl" static const char *JSON_parse_float(JSON_Parser *json, const char *p, const char *pe, VALUE *result) @@ -879,15 +881,15 @@ static const char *JSON_parse_float(JSON_Parser *json, const char *p, const char int cs = EVIL; -#line 883 "parser.c" +#line 885 "parser.c" { cs = JSON_float_start; } -#line 324 "parser.rl" +#line 327 "parser.rl" json->memo = p; -#line 891 "parser.c" +#line 893 "parser.c" { if ( p == pe ) goto _test_eof; @@ -899,7 +901,7 @@ case 1: case 48: goto st3; } if ( 49 <= (*p) && (*p) <= 57 ) - goto st9; + goto st7; goto st0; st0: cs = 0; @@ -911,7 +913,7 @@ case 2: if ( (*p) == 48 ) goto st3; if ( 49 <= (*p) && (*p) <= 57 ) - goto st9; + goto st7; goto st0; st3: if ( ++p == pe ) @@ -919,8 +921,8 @@ st3: case 3: switch( (*p) ) { case 46: goto st4; - case 69: goto st6; - case 101: goto st6; + case 69: goto st5; + case 101: goto st5; } goto st0; st4: @@ -928,92 +930,92 @@ st4: goto _test_eof4; case 4: if ( 48 <= (*p) && (*p) <= 57 ) - goto st5; + goto st8; goto st0; -st5: +st8: if ( ++p == pe ) - goto _test_eof5; -case 5: + goto _test_eof8; +case 8: switch( (*p) ) { - case 69: goto st6; - case 101: goto st6; + case 69: goto st5; + case 101: goto st5; } if ( (*p) > 46 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto st5; + goto st8; } else if ( (*p) >= 45 ) goto st0; - goto tr7; -tr7: -#line 311 "parser.rl" - { p--; {p++; cs = 10; goto _out;} } - goto st10; -st10: + goto tr9; +tr9: +#line 314 "parser.rl" + { p--; {p++; cs = 9; goto _out;} } + goto st9; +st9: if ( ++p == pe ) - goto _test_eof10; -case 10: -#line 956 "parser.c" + goto _test_eof9; +case 9: +#line 958 "parser.c" goto st0; -st6: +st5: if ( ++p == pe ) - goto _test_eof6; -case 6: + goto _test_eof5; +case 5: switch( (*p) ) { - case 43: goto st7; - case 45: goto st7; + case 43: goto st6; + case 45: goto st6; } if ( 48 <= (*p) && (*p) <= 57 ) - goto st8; + goto st10; goto st0; -st7: +st6: if ( ++p == pe ) - goto _test_eof7; -case 7: + goto _test_eof6; +case 6: if ( 48 <= (*p) && (*p) <= 57 ) - goto st8; + goto st10; goto st0; -st8: +st10: if ( ++p == pe ) - goto _test_eof8; -case 8: + goto _test_eof10; +case 10: switch( (*p) ) { case 69: goto st0; case 101: goto st0; } if ( (*p) > 46 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto st8; + goto st10; } else if ( (*p) >= 45 ) goto st0; - goto tr7; -st9: + goto tr9; +st7: if ( ++p == pe ) - goto _test_eof9; -case 9: + goto _test_eof7; +case 7: switch( (*p) ) { case 46: goto st4; - case 69: goto st6; - case 101: goto st6; + case 69: goto st5; + case 101: goto st5; } if ( 48 <= (*p) && (*p) <= 57 ) - goto st9; + goto st7; goto st0; } _test_eof2: cs = 2; goto _test_eof; _test_eof3: cs = 3; goto _test_eof; _test_eof4: cs = 4; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; _test_eof5: cs = 5; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; _test_eof6: cs = 6; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; _test_eof: {} _out: {} } -#line 326 "parser.rl" +#line 329 "parser.rl" if (cs >= JSON_float_first_final) { long len = p - json->memo; @@ -1026,7 +1028,7 @@ case 9: -#line 1030 "parser.c" +#line 1032 "parser.c" static const int JSON_array_start = 1; static const int JSON_array_first_final = 17; static const int JSON_array_error = 0; @@ -1034,7 +1036,7 @@ static const int JSON_array_error = 0; static const int JSON_array_en_main = 1; -#line 366 "parser.rl" +#line 369 "parser.rl" static const char *JSON_parse_array(JSON_Parser *json, const char *p, const char *pe, VALUE *result) @@ -1048,14 +1050,14 @@ static const char *JSON_parse_array(JSON_Parser *json, const char *p, const char *result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class); -#line 1052 "parser.c" +#line 1054 "parser.c" { cs = JSON_array_start; } -#line 379 "parser.rl" +#line 382 "parser.rl" -#line 1059 "parser.c" +#line 1061 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1094,7 +1096,7 @@ case 2: goto st2; goto st0; tr2: -#line 343 "parser.rl" +#line 346 "parser.rl" { VALUE v = Qnil; const char *np = JSON_parse_value(json, p, pe, &v); @@ -1114,7 +1116,7 @@ st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 1118 "parser.c" +#line 1120 "parser.c" switch( (*p) ) { case 13: goto st3; case 32: goto st3; @@ -1214,14 +1216,14 @@ case 12: goto st3; goto st12; tr4: -#line 358 "parser.rl" +#line 361 "parser.rl" { p--; {p++; cs = 17; goto _out;} } goto st17; st17: if ( ++p == pe ) goto _test_eof17; case 17: -#line 1225 "parser.c" +#line 1227 "parser.c" goto st0; st13: if ( ++p == pe ) @@ -1277,7 +1279,7 @@ case 16: _out: {} } -#line 380 "parser.rl" +#line 383 "parser.rl" if(cs >= JSON_array_first_final) { return p + 1; @@ -1358,7 +1360,7 @@ static VALUE json_string_unescape(VALUE result, const char *string, const char * } -#line 1362 "parser.c" +#line 1364 "parser.c" static const int JSON_string_start = 1; static const int JSON_string_first_final = 8; static const int JSON_string_error = 0; @@ -1366,7 +1368,7 @@ static const int JSON_string_error = 0; static const int JSON_string_en_main = 1; -#line 479 "parser.rl" +#line 482 "parser.rl" static int @@ -1386,15 +1388,15 @@ static const char *JSON_parse_string(JSON_Parser *json, const char *p, const cha int cs = EVIL; *result = rb_str_buf_new(0); -#line 1390 "parser.c" +#line 1394 "parser.c" { cs = JSON_string_start; } -#line 498 "parser.rl" +#line 503 "parser.rl" json->memo = p; -#line 1398 "parser.c" +#line 1402 "parser.c" { if ( p == pe ) goto _test_eof; @@ -1419,7 +1421,7 @@ case 2: goto st0; goto st2; tr2: -#line 465 "parser.rl" +#line 468 "parser.rl" { *result = json_string_unescape(*result, json->memo + 1, p); if (NIL_P(*result)) { @@ -1430,14 +1432,14 @@ tr2: {p = (( p + 1))-1;} } } -#line 476 "parser.rl" +#line 479 "parser.rl" { p--; {p++; cs = 8; goto _out;} } goto st8; st8: if ( ++p == pe ) goto _test_eof8; case 8: -#line 1441 "parser.c" +#line 1445 "parser.c" goto st0; st3: if ( ++p == pe ) @@ -1513,7 +1515,7 @@ case 7: _out: {} } -#line 500 "parser.rl" +#line 505 "parser.rl" if (json->create_additions) { VALUE match_string = json->match_string; @@ -1539,19 +1541,6 @@ case 7: } } - - -#line 1545 "parser.c" -static const int JSON_start = 1; -static const int JSON_first_final = 10; -static const int JSON_error = 0; - -static const int JSON_en_main = 1; - - -#line 551 "parser.rl" - - /* * Document-class: JSON::Ext::Parser * @@ -1637,13 +1626,14 @@ static inline void parser_iv_set(JSON_Parser *json, const char* iv_name, VALUE v * defaults to true. * * *object_class*: Defaults to Hash * * *array_class*: Defaults to Array + * * *quirks_mode*: Enables quirks_mode for parser, that is for example + * parsing single JSON values instead of documents is possible. + * */ static int init_count = 0; static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) { - const char *ptr; - long len; VALUE source, opts; GET_PARSER_INIT; @@ -1655,9 +1645,6 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } #endif rb_scan_args(argc, argv, "11", &source, &opts); - source = convert_encoding(StringValue(source)); - ptr = RSTRING_PTR(source); - len = RSTRING_LEN(source); if (!NIL_P(opts)) { opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); if (NIL_P(opts)) { @@ -1687,6 +1674,13 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->symbolize_names = 0; } + tmp = ID2SYM(i_quirks_mode); + if (option_given_p(opts, tmp)) { + VALUE quirks_mode = rb_hash_aref(opts, tmp); + json->quirks_mode = RTEST(quirks_mode) ? 1 : 0; + } else { + json->quirks_mode = 0; + } tmp = ID2SYM(i_create_additions); if (option_given_p(opts, tmp)) { json->create_additions = RTEST(rb_hash_aref(opts, tmp)); @@ -1724,21 +1718,30 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->create_id = rb_funcall(mJSON, i_create_id, 0); parser_iv_set(json, "@create_id", json->create_id); } + if (!json->quirks_mode) { + source = convert_encoding(StringValue(source)); + } json->current_nesting = 0; - json->len = len; - json->source = ptr; + json->len = RSTRING_LEN(source); + json->source = RSTRING_PTR(source);; json->Vsource = source; parser_iv_set(json, "@vsource", json->Vsource); return self; } -/* - * call-seq: parse() - * - * Parses the current JSON text _source_ and returns the complete data - * structure as a result. - */ -static VALUE cParser_parse(VALUE self) + +#line 1719 "parser.c" +static const int JSON_start = 1; +static const int JSON_first_final = 10; +static const int JSON_error = 0; + +static const int JSON_en_main = 1; + + +#line 726 "parser.rl" + + +static VALUE cParser_parse_strict(VALUE self) { const char *p, *pe; int cs = EVIL; @@ -1751,7 +1754,7 @@ static VALUE cParser_parse(VALUE self) cs = JSON_start; } -#line 745 "parser.rl" +#line 736 "parser.rl" p = json->source; pe = p + json->len; @@ -1811,7 +1814,7 @@ case 5: goto st1; goto st5; tr3: -#line 540 "parser.rl" +#line 715 "parser.rl" { const char *np; json->current_nesting = 1; @@ -1820,7 +1823,7 @@ tr3: } goto st10; tr4: -#line 533 "parser.rl" +#line 708 "parser.rl" { const char *np; json->current_nesting = 1; @@ -1889,7 +1892,7 @@ case 9: _out: {} } -#line 748 "parser.rl" +#line 739 "parser.rl" if (cs >= JSON_first_final && p == pe) { return result; @@ -1899,6 +1902,197 @@ case 9: } } + + +#line 1893 "parser.c" +static const int JSON_quirks_mode_start = 1; +static const int JSON_quirks_mode_first_final = 10; +static const int JSON_quirks_mode_error = 0; + +static const int JSON_quirks_mode_en_main = 1; + + +#line 764 "parser.rl" + + +static VALUE cParser_parse_quirks_mode(VALUE self) +{ + char *p, *pe; + int cs = EVIL; + VALUE result = Qnil; + GET_PARSER; + + +#line 1912 "parser.c" + { + cs = JSON_quirks_mode_start; + } + +#line 774 "parser.rl" + p = json->source; + pe = p + json->len; + +#line 1921 "parser.c" + { + if ( p == pe ) + goto _test_eof; + switch ( cs ) + { +st1: + if ( ++p == pe ) + goto _test_eof1; +case 1: + switch( (*p) ) { + case 13: goto st1; + case 32: goto st1; + case 34: goto tr2; + case 45: goto tr2; + case 47: goto st6; + case 73: goto tr2; + case 78: goto tr2; + case 91: goto tr2; + case 102: goto tr2; + case 110: goto tr2; + case 116: goto tr2; + case 123: goto tr2; + } + if ( (*p) > 10 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2; + } else if ( (*p) >= 9 ) + goto st1; + goto st0; +st0: +cs = 0; + goto _out; +tr2: +#line 756 "parser.rl" + { + char *np = JSON_parse_value(json, p, pe, &result); + if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;} + } + goto st10; +st10: + if ( ++p == pe ) + goto _test_eof10; +case 10: +#line 1965 "parser.c" + switch( (*p) ) { + case 13: goto st10; + case 32: goto st10; + case 47: goto st2; + } + if ( 9 <= (*p) && (*p) <= 10 ) + goto st10; + goto st0; +st2: + if ( ++p == pe ) + goto _test_eof2; +case 2: + switch( (*p) ) { + case 42: goto st3; + case 47: goto st5; + } + goto st0; +st3: + if ( ++p == pe ) + goto _test_eof3; +case 3: + if ( (*p) == 42 ) + goto st4; + goto st3; +st4: + if ( ++p == pe ) + goto _test_eof4; +case 4: + switch( (*p) ) { + case 42: goto st4; + case 47: goto st10; + } + goto st3; +st5: + if ( ++p == pe ) + goto _test_eof5; +case 5: + if ( (*p) == 10 ) + goto st10; + goto st5; +st6: + if ( ++p == pe ) + goto _test_eof6; +case 6: + switch( (*p) ) { + case 42: goto st7; + case 47: goto st9; + } + goto st0; +st7: + if ( ++p == pe ) + goto _test_eof7; +case 7: + if ( (*p) == 42 ) + goto st8; + goto st7; +st8: + if ( ++p == pe ) + goto _test_eof8; +case 8: + switch( (*p) ) { + case 42: goto st8; + case 47: goto st1; + } + goto st7; +st9: + if ( ++p == pe ) + goto _test_eof9; +case 9: + if ( (*p) == 10 ) + goto st1; + goto st9; + } + _test_eof1: cs = 1; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + + _test_eof: {} + _out: {} + } + +#line 777 "parser.rl" + + if (cs >= JSON_quirks_mode_first_final && p == pe) { + return result; + } else { + rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); + return Qnil; + } +} + +/* + * call-seq: parse() + * + * Parses the current JSON text _source_ and returns the complete data + * structure as a result. + */ +static VALUE cParser_parse(VALUE self) +{ + GET_PARSER; + + if (json->quirks_mode) { + return cParser_parse_quirks_mode(self); + } else { + return cParser_parse_strict(self); + } +} + + static JSON_Parser *JSON_allocate() { JSON_Parser *json = ALLOC(JSON_Parser); @@ -1940,6 +2134,18 @@ static VALUE cParser_source(VALUE self) return rb_str_dup(json->Vsource); } +/* + * call-seq: quirks_mode?() + * + * Returns a true, if this parser is in quirks_mode, false otherwise. + */ +static VALUE cParser_quirks_mode_p(VALUE self) +{ + GET_PARSER; + return json->quirks_mode ? Qtrue : Qfalse; +} + + void Init_parser() { rb_require("json/common"); @@ -1952,6 +2158,7 @@ void Init_parser() rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); + rb_define_method(cParser, "quirks_mode?", cParser_quirks_mode_p, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); @@ -1965,6 +2172,7 @@ void Init_parser() i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_symbolize_names = rb_intern("symbolize_names"); + i_quirks_mode = rb_intern("quirks_mode"); i_object_class = rb_intern("object_class"); i_array_class = rb_intern("array_class"); i_match = rb_intern("match"); diff --git a/ext/json/ext/parser/parser.h b/ext/json/ext/parser/parser.h index 5571787..cf3e558 100644 --- a/ext/json/ext/parser/parser.h +++ b/ext/json/ext/parser/parser.h @@ -45,6 +45,7 @@ typedef struct JSON_ParserStruct { int allow_nan; int parsing_name; int symbolize_names; + int quirks_mode; VALUE object_class; VALUE array_class; int create_additions; diff --git a/ext/json/ext/parser/parser.rl b/ext/json/ext/parser/parser.rl index feb0093..5de6118 100644 --- a/ext/json/ext/parser/parser.rl +++ b/ext/json/ext/parser/parser.rl @@ -76,8 +76,9 @@ static VALUE mJSON, mExt, cParser, eParserError, eNestingError; static VALUE CNaN, CInfinity, CMinusInfinity; static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, - i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_object_class, - i_array_class, i_key_p, i_deep_const_get, i_match, i_match_string, i_aset, i_leftshift; + i_chr, i_max_nesting, i_allow_nan, i_symbolize_names, i_quirks_mode, + i_object_class, i_array_class, i_key_p, i_deep_const_get, i_match, + i_match_string, i_aset, i_leftshift; %%{ machine JSON_common; @@ -138,13 +139,14 @@ static ID i_json_creatable_p, i_json_create, i_create_id, i_create_additions, action exit { fhold; fbreak; } - a_pair = ignore* begin_name >parse_name - ignore* name_separator ignore* - begin_value >parse_value; + pair = ignore* begin_name >parse_name ignore* name_separator ignore* begin_value >parse_value; + next_pair = ignore* value_separator pair; - main := begin_object - (a_pair (ignore* value_separator a_pair)*)? - ignore* end_object @exit; + main := ( + begin_object + (pair (next_pair)*)? ignore* + end_object + ) @exit; }%% static const char *JSON_parse_object(JSON_Parser *json, const char *p, const char *pe, VALUE *result) @@ -178,6 +180,7 @@ static const char *JSON_parse_object(JSON_Parser *json, const char *p, const cha } } + %%{ machine JSON_value; include JSON_common; @@ -213,8 +216,8 @@ static const char *JSON_parse_object(JSON_Parser *json, const char *p, const cha } action parse_number { - const char *np; - if(pe > fpc + 9 && !strncmp(MinusInfinity, fpc, 9)) { + char *np; + if(pe > fpc + 9 - json->quirks_mode && !strncmp(MinusInfinity, fpc, 9)) { if (json->allow_nan) { *result = CMinusInfinity; fexec p + 10; @@ -282,7 +285,7 @@ static const char *JSON_parse_value(JSON_Parser *json, const char *p, const char action exit { fhold; fbreak; } - main := '-'? ('0' | [1-9][0-9]*) (^[0-9] @exit); + main := '-'? ('0' | [1-9][0-9]*) (^[0-9]? @exit); }%% static const char *JSON_parse_integer(JSON_Parser *json, const char *p, const char *pe, VALUE *result) @@ -313,7 +316,7 @@ static const char *JSON_parse_integer(JSON_Parser *json, const char *p, const ch main := '-'? ( (('0' | [1-9][0-9]*) '.' [0-9]+ ([Ee] [+\-]?[0-9]+)?) | (('0' | [1-9][0-9]*) ([Ee] [+\-]?[0-9]+)) - ) (^[0-9Ee.\-] @exit ); + ) (^[0-9Ee.\-]? @exit ); }%% static const char *JSON_parse_float(JSON_Parser *json, const char *p, const char *pe, VALUE *result) @@ -522,34 +525,6 @@ static const char *JSON_parse_string(JSON_Parser *json, const char *p, const cha } } - -%%{ - machine JSON; - - write data; - - include JSON_common; - - action parse_object { - const char *np; - json->current_nesting = 1; - np = JSON_parse_object(json, fpc, pe, &result); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - action parse_array { - const char *np; - json->current_nesting = 1; - np = JSON_parse_array(json, fpc, pe, &result); - if (np == NULL) { fhold; fbreak; } else fexec np; - } - - main := ignore* ( - begin_object >parse_object | - begin_array >parse_array - ) ignore*; -}%% - /* * Document-class: JSON::Ext::Parser * @@ -640,8 +615,6 @@ static inline void parser_iv_set(JSON_Parser *json, const char* iv_name, VALUE v static int init_count = 0; static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) { - const char *ptr; - long len; VALUE source, opts; GET_PARSER_INIT; @@ -653,9 +626,6 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } #endif rb_scan_args(argc, argv, "11", &source, &opts); - source = convert_encoding(StringValue(source)); - ptr = RSTRING_PTR(source); - len = RSTRING_LEN(source); if (!NIL_P(opts)) { opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash"); if (NIL_P(opts)) { @@ -685,6 +655,13 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) } else { json->symbolize_names = 0; } + tmp = ID2SYM(i_quirks_mode); + if (option_given_p(opts, tmp)) { + VALUE quirks_mode = rb_hash_aref(opts, tmp); + json->quirks_mode = RTEST(quirks_mode) ? 1 : 0; + } else { + json->quirks_mode = 0; + } tmp = ID2SYM(i_create_additions); if (option_given_p(opts, tmp)) { json->create_additions = RTEST(rb_hash_aref(opts, tmp)); @@ -722,21 +699,45 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self) json->create_id = rb_funcall(mJSON, i_create_id, 0); parser_iv_set(json, "@create_id", json->create_id); } + if (!json->quirks_mode) { + source = convert_encoding(StringValue(source)); + } json->current_nesting = 0; - json->len = len; - json->source = ptr; + json->len = RSTRING_LEN(source); + json->source = RSTRING_PTR(source);; json->Vsource = source; parser_iv_set(json, "@vsource", json->Vsource); return self; } -/* - * call-seq: parse() - * - * Parses the current JSON text _source_ and returns the complete data - * structure as a result. - */ -static VALUE cParser_parse(VALUE self) +%%{ + machine JSON; + + write data; + + include JSON_common; + + action parse_object { + char *np; + json->current_nesting = 1; + np = JSON_parse_object(json, fpc, pe, &result); + if (np == NULL) { fhold; fbreak; } else fexec np; + } + + action parse_array { + char *np; + json->current_nesting = 1; + np = JSON_parse_array(json, fpc, pe, &result); + if (np == NULL) { fhold; fbreak; } else fexec np; + } + + main := ignore* ( + begin_object >parse_object | + begin_array >parse_array + ) ignore*; +}%% + +static VALUE cParser_parse_strict(VALUE self) { const char *p, *pe; int cs = EVIL; @@ -756,6 +757,62 @@ static VALUE cParser_parse(VALUE self) } } + +%%{ + machine JSON_quirks_mode; + + write data; + + include JSON_common; + + action parse_value { + char *np = JSON_parse_value(json, fpc, pe, &result); + if (np == NULL) { fhold; fbreak; } else fexec np; + } + + main := ignore* ( + begin_value >parse_value + ) ignore*; +}%% + +static VALUE cParser_parse_quirks_mode(VALUE self) +{ + char *p, *pe; + int cs = EVIL; + VALUE result = Qnil; + GET_PARSER; + + %% write init; + p = json->source; + pe = p + json->len; + %% write exec; + + if (cs >= JSON_quirks_mode_first_final && p == pe) { + return result; + } else { + rb_raise(eParserError, "%u: unexpected token at '%s'", __LINE__, p); + return Qnil; + } +} + +/* + * call-seq: parse() + * + * Parses the current JSON text _source_ and returns the complete data + * structure as a result. + */ +static VALUE cParser_parse(VALUE self) +{ + GET_PARSER; + + if (json->quirks_mode) { + return cParser_parse_quirks_mode(self); + } else { + return cParser_parse_strict(self); + } +} + + static JSON_Parser *JSON_allocate() { JSON_Parser *json = ALLOC(JSON_Parser); @@ -797,6 +854,18 @@ static VALUE cParser_source(VALUE self) return rb_str_dup(json->Vsource); } +/* + * call-seq: quirks_mode?() + * + * Returns a true, if this parser is in quirks_mode, false otherwise. + */ +static VALUE cParser_quirks_mode_p(VALUE self) +{ + GET_PARSER; + return json->quirks_mode ? Qtrue : Qfalse; +} + + void Init_parser() { rb_require("json/common"); @@ -809,6 +878,7 @@ void Init_parser() rb_define_method(cParser, "initialize", cParser_initialize, -1); rb_define_method(cParser, "parse", cParser_parse, 0); rb_define_method(cParser, "source", cParser_source, 0); + rb_define_method(cParser, "quirks_mode?", cParser_quirks_mode_p, 0); CNaN = rb_const_get(mJSON, rb_intern("NaN")); CInfinity = rb_const_get(mJSON, rb_intern("Infinity")); @@ -822,6 +892,7 @@ void Init_parser() i_max_nesting = rb_intern("max_nesting"); i_allow_nan = rb_intern("allow_nan"); i_symbolize_names = rb_intern("symbolize_names"); + i_quirks_mode = rb_intern("quirks_mode"); i_object_class = rb_intern("object_class"); i_array_class = rb_intern("array_class"); i_match = rb_intern("match"); @@ -6,21 +6,14 @@ include FileUtils::Verbose include Config -bindir = CONFIG["bindir"] -cd 'bin' do - filename = 'edit_json.rb' - #install(filename, bindir) -end sitelibdir = CONFIG["sitelibdir"] cd 'lib' do install('json.rb', sitelibdir) mkdir_p File.join(sitelibdir, 'json') - for file in Dir['json/**/*.{rb,xpm}'] + for file in Dir['json/**/*}'] d = File.join(sitelibdir, file) mkdir_p File.dirname(d) install(file, d) end - install(File.join('json', 'editor.rb'), File.join(sitelibdir,'json')) - install(File.join('json', 'json.xpm'), File.join(sitelibdir,'json')) end warn " *** Installed PURE ruby library." diff --git a/java/src/json/ext/Generator.java b/java/src/json/ext/Generator.java index fbc394f..78dc078 100644 --- a/java/src/json/ext/Generator.java +++ b/java/src/json/ext/Generator.java @@ -1,6 +1,6 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * + * * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files * for details. */ @@ -85,11 +85,11 @@ public final class Generator { /** * A class that concentrates all the information that is shared by * generators working on a single session. - * + * * <p>A session is defined as the process of serializing a single root * object; any handler directly called by container handlers (arrays and * hashes/objects) shares this object with its caller. - * + * * <p>Note that anything called indirectly (via {@link GENERIC_HANDLER}) * won't be part of the session. */ diff --git a/java/src/json/ext/GeneratorMethods.java b/java/src/json/ext/GeneratorMethods.java index 356f2d0..637b579 100644 --- a/java/src/json/ext/GeneratorMethods.java +++ b/java/src/json/ext/GeneratorMethods.java @@ -1,6 +1,6 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * + * * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files * for details. */ @@ -25,7 +25,7 @@ import org.jruby.util.ByteList; /** * A class that populates the * <code>Json::Ext::Generator::GeneratorMethods</code> module. - * + * * @author mernen */ class GeneratorMethods { diff --git a/java/src/json/ext/GeneratorService.java b/java/src/json/ext/GeneratorService.java index 2f3b07e..ed33639 100644 --- a/java/src/json/ext/GeneratorService.java +++ b/java/src/json/ext/GeneratorService.java @@ -1,6 +1,6 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * + * * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files * for details. */ diff --git a/java/src/json/ext/GeneratorState.java b/java/src/json/ext/GeneratorState.java index f04eda2..78524a1 100644 --- a/java/src/json/ext/GeneratorState.java +++ b/java/src/json/ext/GeneratorState.java @@ -1,6 +1,6 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * + * * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files * for details. */ @@ -24,10 +24,10 @@ import org.jruby.util.ByteList; /** * The <code>JSON::Ext::Generator::State</code> class. - * + * * <p>This class is used to create State instances, that are use to hold data * while generating a JSON text from a a Ruby data structure. - * + * * @author mernen */ public class GeneratorState extends RubyObject { @@ -72,10 +72,17 @@ public class GeneratorState extends RubyObject { private boolean allowNaN = DEFAULT_ALLOW_NAN; static final boolean DEFAULT_ALLOW_NAN = false; /** - * XXX + * If set to <code>true</code> all JSON documents generated do not contain + * any other characters than ASCII characters. */ private boolean asciiOnly = DEFAULT_ASCII_ONLY; static final boolean DEFAULT_ASCII_ONLY = false; + /** + * If set to <code>true</code> all JSON values generated might not be + * RFC-conform JSON documents. + */ + private boolean quirksMode = DEFAULT_QUIRKS_MODE; + static final boolean DEFAULT_QUIRKS_MODE = false; /** * The current depth (inside a #to_json call) @@ -94,7 +101,7 @@ public class GeneratorState extends RubyObject { /** * <code>State.from_state(opts)</code> - * + * * <p>Creates a State object from <code>opts</code>, which ought to be * {@link RubyHash Hash} to create a new <code>State</code> instance * configured by <codes>opts</code>, something else to create an @@ -136,11 +143,11 @@ public class GeneratorState extends RubyObject { /** * <code>State#initialize(opts = {})</code> - * + * * Instantiates a new <code>State</code> object, configured by <code>opts</code>. - * + * * <code>opts</code> can have the following keys: - * + * * <dl> * <dt><code>:indent</code> * <dd>a {@link RubyString String} used to indent levels (default: <code>""</code>) @@ -151,7 +158,7 @@ public class GeneratorState extends RubyObject { * <dd>a String that is put before a <code>":"</code> pair delimiter * (default: <code>""</code>) * <dt><code>:object_nl</code> - * <dd>a String that is put at the end of a JSON object (default: <code>""</code>) + * <dd>a String that is put at the end of a JSON object (default: <code>""</code>) * <dt><code>:array_nl</code> * <dd>a String that is put at the end of a JSON array (default: <code>""</code>) * <dt><code>:allow_nan</code> @@ -181,17 +188,20 @@ public class GeneratorState extends RubyObject { this.maxNesting = orig.maxNesting; this.allowNaN = orig.allowNaN; this.asciiOnly = orig.asciiOnly; + this.quirksMode = orig.quirksMode; this.depth = orig.depth; return this; } /** - * XXX + * Generates a valid JSON document from object <code>obj</code> and returns + * the result. If no valid JSON document can be created this method raises + * a GeneratorError exception. */ @JRubyMethod public IRubyObject generate(ThreadContext context, IRubyObject obj) { RubyString result = Generator.generateJson(context, obj, this); - if (!objectOrArrayLiteral(result)) { + if (!quirksMode && !objectOrArrayLiteral(result)) { throw Utils.newException(context, Utils.M_GENERATOR_ERROR, "only generation of JSON objects or arrays allowed"); } @@ -364,6 +374,22 @@ public class GeneratorState extends RubyObject { return context.getRuntime().newBoolean(asciiOnly); } + @JRubyMethod(name="quirks_mode") + public RubyBoolean quirks_mode_get(ThreadContext context) { + return context.getRuntime().newBoolean(quirksMode); + } + + @JRubyMethod(name="quirks_mode=") + public IRubyObject quirks_mode_set(IRubyObject quirks_mode) { + quirksMode = quirks_mode.isTrue(); + return quirks_mode.getRuntime().newBoolean(quirksMode); + } + + @JRubyMethod(name="quirks_mode?") + public RubyBoolean quirks_mode_p(ThreadContext context) { + return context.getRuntime().newBoolean(quirksMode); + } + public int getDepth() { return depth; } @@ -390,7 +416,7 @@ public class GeneratorState extends RubyObject { /** * <code>State#configure(opts)</code> - * + * * <p>Configures this State instance with the {@link RubyHash Hash} * <code>opts</code>, and returns itself. * @param vOpts The options hash @@ -418,6 +444,7 @@ public class GeneratorState extends RubyObject { maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); allowNaN = opts.getBool("allow_nan", DEFAULT_ALLOW_NAN); asciiOnly = opts.getBool("ascii_only", DEFAULT_ASCII_ONLY); + quirksMode = opts.getBool("quirks_mode", DEFAULT_QUIRKS_MODE); depth = opts.getInt("depth", 0); @@ -426,7 +453,7 @@ public class GeneratorState extends RubyObject { /** * <code>State#to_h()</code> - * + * * <p>Returns the configuration instance variables as a hash, that can be * passed to the configure method. * @return @@ -443,6 +470,7 @@ public class GeneratorState extends RubyObject { result.op_aset(context, runtime.newSymbol("array_nl"), array_nl_get(context)); result.op_aset(context, runtime.newSymbol("allow_nan"), allow_nan_p(context)); result.op_aset(context, runtime.newSymbol("ascii_only"), ascii_only_p(context)); + result.op_aset(context, runtime.newSymbol("quirks_mode"), quirks_mode_p(context)); result.op_aset(context, runtime.newSymbol("max_nesting"), max_nesting_get(context)); result.op_aset(context, runtime.newSymbol("depth"), depth_get(context)); return result; diff --git a/java/src/json/ext/OptionsReader.java b/java/src/json/ext/OptionsReader.java index c9c9c94..a0b76b1 100644 --- a/java/src/json/ext/OptionsReader.java +++ b/java/src/json/ext/OptionsReader.java @@ -1,6 +1,6 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * + * * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files * for details. */ diff --git a/java/src/json/ext/Parser.java b/java/src/json/ext/Parser.java index cea42d4..0b59c8f 100644 --- a/java/src/json/ext/Parser.java +++ b/java/src/json/ext/Parser.java @@ -2,7 +2,7 @@ // line 1 "Parser.rl" /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * + * * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files * for details. */ @@ -31,16 +31,16 @@ import org.jruby.util.ByteList; /** * The <code>JSON::Ext::Parser</code> class. - * + * * <p>This is the JSON parser implemented as a Java class. To use it as the * standard parser, set * <pre>JSON.parser = JSON::Ext::Parser</pre> * This is performed for you when you <code>include "json/ext"</code>. - * + * * <p>This class does not perform the actual parsing, just acts as an interface * to Ruby code. When the {@link #parse()} method is invoked, a * Parser.ParserSession object is instantiated, which handles the process. - * + * * @author mernen */ public class Parser extends RubyObject { @@ -51,6 +51,7 @@ public class Parser extends RubyObject { private int maxNesting; private boolean allowNaN; private boolean symbolizeNames; + private boolean quirksMode; private RubyClass objectClass; private RubyClass arrayClass; private RubyHash match_string; @@ -71,7 +72,7 @@ public class Parser extends RubyObject { /** * Multiple-value return for internal parser methods. - * + * * <p>All the <code>parse<var>Stuff</var></code> methods return instances of * <code>ParserResult</code> when successful, or <code>null</code> when * there's a problem with the input data. @@ -100,18 +101,18 @@ public class Parser extends RubyObject { /** * <code>Parser.new(source, opts = {})</code> - * + * * <p>Creates a new <code>JSON::Ext::Parser</code> instance for the string * <code>source</code>. * It will be configured by the <code>opts</code> Hash. * <code>opts</code> can have the following keys: - * + * * <dl> * <dt><code>:max_nesting</code> * <dd>The maximum depth of nesting allowed in the parsed data * structures. Disable depth checking with <code>:max_nesting => false|nil|0</code>, * it defaults to 19. - * + * * <dt><code>:allow_nan</code> * <dd>If set to <code>true</code>, allow <code>NaN</code>, * <code>Infinity</code> and <code>-Infinity</code> in defiance of RFC 4627 @@ -120,17 +121,25 @@ public class Parser extends RubyObject { * <dt><code>:symbolize_names</code> * <dd>If set to <code>true</code>, returns symbols for the names (keys) in * a JSON object. Otherwise strings are returned, which is also the default. + * + * <dt><code>:quirks_mode?</code> + * <dd>If set to <code>true</code>, if the parse is in quirks_mode, false + * otherwise. * * <dt><code>:create_additions</code> * <dd>If set to <code>false</code>, the Parser doesn't create additions * even if a matchin class and <code>create_id</code> was found. This option * defaults to <code>true</code>. - * + * * <dt><code>:object_class</code> * <dd>Defaults to Hash. - * + * * <dt><code>:array_class</code> * <dd>Defaults to Array. + * + * <dt><code>:quirks_mode</code> + * <dd>Enables quirks_mode for parser, that is for example parsing single + * JSON values instead of documents is possible. * </dl> */ @JRubyMethod(name = "new", required = 1, optional = 1, meta = true) @@ -148,19 +157,21 @@ public class Parser extends RubyObject { if (this.vSource != null) { throw runtime.newTypeError("already initialized instance"); } - RubyString source = convertEncoding(context, args[0].convertToString()); OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null); this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); this.allowNaN = opts.getBool("allow_nan", false); this.symbolizeNames = opts.getBool("symbolize_names", false); + this.quirksMode = opts.getBool("quirks_mode", false); this.createId = opts.getString("create_id", getCreateId(context)); this.createAdditions = opts.getBool("create_additions", true); this.objectClass = opts.getClass("object_class", runtime.getHash()); this.arrayClass = opts.getClass("array_class", runtime.getArray()); this.match_string = opts.getHash("match_string"); - this.vSource = source; + this.vSource = args[0].convertToString(); + if (!quirksMode) this.vSource = convertEncoding(context, vSource); + return this; } @@ -229,7 +240,7 @@ public class Parser extends RubyObject { /** * <code>Parser#parse()</code> - * + * * <p>Parses the current JSON text <code>source</code> and returns the * complete data structure as a result. */ @@ -240,7 +251,7 @@ public class Parser extends RubyObject { /** * <code>Parser#source()</code> - * + * * <p>Returns a copy of the current <code>source</code> string, that was * used to construct this Parser. */ @@ -249,6 +260,17 @@ public class Parser extends RubyObject { return checkAndGetSource().dup(); } + /** + * <code>Parser#quirks_mode?()</code> + * + * <p>If set to <code>true</code>, if the parse is in quirks_mode, false + * otherwise. + */ + @JRubyMethod(name = "quirks_mode?") + public IRubyObject quirks_mode_p(ThreadContext context) { + return context.getRuntime().newBoolean(quirksMode); + } + public RubyString checkAndGetSource() { if (vSource != null) { return vSource; @@ -268,7 +290,7 @@ public class Parser extends RubyObject { /** * A string parsing session. - * + * * <p>Once a ParserSession is instantiated, the source string should not * change until the parsing is complete. The ParserSession object assumes * the source {@link RubyString} is still associated to its original @@ -309,11 +331,11 @@ public class Parser extends RubyObject { } -// line 335 "Parser.rl" +// line 357 "Parser.rl" -// line 317 "Parser.java" +// line 339 "Parser.java" private static byte[] init__JSON_value_actions_0() { return new byte [] { @@ -427,7 +449,7 @@ static final int JSON_value_error = 0; static final int JSON_value_en_main = 1; -// line 441 "Parser.rl" +// line 463 "Parser.rl" ParserResult parseValue(int p, int pe) { @@ -435,14 +457,14 @@ static final int JSON_value_en_main = 1; IRubyObject result = null; -// line 439 "Parser.java" +// line 461 "Parser.java" { cs = JSON_value_start; } -// line 448 "Parser.rl" +// line 470 "Parser.rl" -// line 446 "Parser.java" +// line 468 "Parser.java" { int _klen; int _trans = 0; @@ -468,13 +490,13 @@ case 1: while ( _nacts-- > 0 ) { switch ( _JSON_value_actions[_acts++] ) { case 9: -// line 426 "Parser.rl" +// line 448 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 478 "Parser.java" +// line 500 "Parser.java" } } @@ -537,25 +559,25 @@ case 1: switch ( _JSON_value_actions[_acts++] ) { case 0: -// line 343 "Parser.rl" +// line 365 "Parser.rl" { result = getRuntime().getNil(); } break; case 1: -// line 346 "Parser.rl" +// line 368 "Parser.rl" { result = getRuntime().getFalse(); } break; case 2: -// line 349 "Parser.rl" +// line 371 "Parser.rl" { result = getRuntime().getTrue(); } break; case 3: -// line 352 "Parser.rl" +// line 374 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_NAN); @@ -565,7 +587,7 @@ case 1: } break; case 4: -// line 359 "Parser.rl" +// line 381 "Parser.rl" { if (parser.allowNaN) { result = getConstant(CONST_INFINITY); @@ -575,9 +597,9 @@ case 1: } break; case 5: -// line 366 "Parser.rl" +// line 388 "Parser.rl" { - if (pe > p + 9 && + if (pe > p + 9 - (parser.quirksMode ? 1 : 0) && absSubSequence(p, p + 9).toString().equals(JSON_MINUS_INFINITY)) { if (parser.allowNaN) { @@ -604,7 +626,7 @@ case 1: } break; case 6: -// line 392 "Parser.rl" +// line 414 "Parser.rl" { ParserResult res = parseString(p, pe); if (res == null) { @@ -617,7 +639,7 @@ case 1: } break; case 7: -// line 402 "Parser.rl" +// line 424 "Parser.rl" { currentNesting++; ParserResult res = parseArray(p, pe); @@ -632,7 +654,7 @@ case 1: } break; case 8: -// line 414 "Parser.rl" +// line 436 "Parser.rl" { currentNesting++; ParserResult res = parseObject(p, pe); @@ -646,7 +668,7 @@ case 1: } } break; -// line 650 "Parser.java" +// line 672 "Parser.java" } } } @@ -666,7 +688,7 @@ case 5: break; } } -// line 449 "Parser.rl" +// line 471 "Parser.rl" if (cs >= JSON_value_first_final && result != null) { return new ParserResult(result, p); @@ -676,7 +698,7 @@ case 5: } -// line 680 "Parser.java" +// line 702 "Parser.java" private static byte[] init__JSON_integer_actions_0() { return new byte [] { @@ -690,7 +712,7 @@ private static final byte _JSON_integer_actions[] = init__JSON_integer_actions_0 private static byte[] init__JSON_integer_key_offsets_0() { return new byte [] { - 0, 0, 4, 7, 9, 11 + 0, 0, 4, 7, 9, 9 }; } @@ -720,7 +742,7 @@ private static final byte _JSON_integer_single_lengths[] = init__JSON_integer_si private static byte[] init__JSON_integer_range_lengths_0() { return new byte [] { - 0, 1, 1, 1, 1, 0 + 0, 1, 1, 1, 0, 1 }; } @@ -730,7 +752,7 @@ private static final byte _JSON_integer_range_lengths[] = init__JSON_integer_ran private static byte[] init__JSON_integer_index_offsets_0() { return new byte [] { - 0, 0, 4, 7, 9, 11 + 0, 0, 4, 7, 9, 10 }; } @@ -740,7 +762,7 @@ private static final byte _JSON_integer_index_offsets[] = init__JSON_integer_ind private static byte[] init__JSON_integer_indicies_0() { return new byte [] { - 0, 2, 3, 1, 2, 3, 1, 1, 4, 3, 4, 1, + 0, 2, 3, 1, 2, 3, 1, 1, 4, 1, 3, 4, 0 }; } @@ -751,7 +773,7 @@ private static final byte _JSON_integer_indicies[] = init__JSON_integer_indicies private static byte[] init__JSON_integer_trans_targs_0() { return new byte [] { - 2, 0, 3, 4, 5 + 2, 0, 3, 5, 4 }; } @@ -769,28 +791,28 @@ private static final byte _JSON_integer_trans_actions[] = init__JSON_integer_tra static final int JSON_integer_start = 1; -static final int JSON_integer_first_final = 5; +static final int JSON_integer_first_final = 3; static final int JSON_integer_error = 0; static final int JSON_integer_en_main = 1; -// line 468 "Parser.rl" +// line 490 "Parser.rl" ParserResult parseInteger(int p, int pe) { int cs = EVIL; -// line 786 "Parser.java" +// line 808 "Parser.java" { cs = JSON_integer_start; } -// line 474 "Parser.rl" +// line 496 "Parser.rl" int memo = p; -// line 794 "Parser.java" +// line 816 "Parser.java" { int _klen; int _trans = 0; @@ -871,13 +893,13 @@ case 1: switch ( _JSON_integer_actions[_acts++] ) { case 0: -// line 462 "Parser.rl" +// line 484 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 881 "Parser.java" +// line 903 "Parser.java" } } } @@ -897,7 +919,7 @@ case 5: break; } } -// line 476 "Parser.rl" +// line 498 "Parser.rl" if (cs < JSON_integer_first_final) { return null; @@ -912,7 +934,7 @@ case 5: } -// line 916 "Parser.java" +// line 938 "Parser.java" private static byte[] init__JSON_float_actions_0() { return new byte [] { @@ -926,7 +948,7 @@ private static final byte _JSON_float_actions[] = init__JSON_float_actions_0(); private static byte[] init__JSON_float_key_offsets_0() { return new byte [] { - 0, 0, 4, 7, 10, 12, 18, 22, 24, 30, 35 + 0, 0, 4, 7, 10, 12, 16, 18, 23, 29, 29 }; } @@ -937,8 +959,8 @@ private static char[] init__JSON_float_trans_keys_0() { return new char [] { 45, 48, 49, 57, 48, 49, 57, 46, 69, 101, 48, 57, - 69, 101, 45, 46, 48, 57, 43, 45, 48, 57, 48, 57, - 69, 101, 45, 46, 48, 57, 46, 69, 101, 48, 57, 0 + 43, 45, 48, 57, 48, 57, 46, 69, 101, 48, 57, 69, + 101, 45, 46, 48, 57, 69, 101, 45, 46, 48, 57, 0 }; } @@ -948,7 +970,7 @@ private static final char _JSON_float_trans_keys[] = init__JSON_float_trans_keys private static byte[] init__JSON_float_single_lengths_0() { return new byte [] { - 0, 2, 1, 3, 0, 2, 2, 0, 2, 3, 0 + 0, 2, 1, 3, 0, 2, 0, 3, 2, 0, 2 }; } @@ -958,7 +980,7 @@ private static final byte _JSON_float_single_lengths[] = init__JSON_float_single private static byte[] init__JSON_float_range_lengths_0() { return new byte [] { - 0, 1, 1, 0, 1, 2, 1, 1, 2, 1, 0 + 0, 1, 1, 0, 1, 1, 1, 1, 2, 0, 2 }; } @@ -968,7 +990,7 @@ private static final byte _JSON_float_range_lengths[] = init__JSON_float_range_l private static byte[] init__JSON_float_index_offsets_0() { return new byte [] { - 0, 0, 4, 7, 11, 13, 18, 22, 24, 29, 34 + 0, 0, 4, 7, 11, 13, 17, 19, 24, 29, 30 }; } @@ -979,8 +1001,8 @@ private static byte[] init__JSON_float_indicies_0() { return new byte [] { 0, 2, 3, 1, 2, 3, 1, 4, 5, 5, 1, 6, - 1, 5, 5, 1, 6, 7, 8, 8, 9, 1, 9, 1, - 1, 1, 1, 9, 7, 4, 5, 5, 3, 1, 1, 0 + 1, 7, 7, 8, 1, 8, 1, 4, 5, 5, 3, 1, + 5, 5, 1, 6, 9, 1, 1, 1, 1, 8, 9, 0 }; } @@ -990,7 +1012,7 @@ private static final byte _JSON_float_indicies[] = init__JSON_float_indicies_0() private static byte[] init__JSON_float_trans_targs_0() { return new byte [] { - 2, 0, 3, 9, 4, 6, 5, 10, 7, 8 + 2, 0, 3, 7, 4, 5, 8, 6, 10, 9 }; } @@ -1000,7 +1022,7 @@ private static final byte _JSON_float_trans_targs[] = init__JSON_float_trans_tar private static byte[] init__JSON_float_trans_actions_0() { return new byte [] { - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; } @@ -1008,28 +1030,28 @@ private static final byte _JSON_float_trans_actions[] = init__JSON_float_trans_a static final int JSON_float_start = 1; -static final int JSON_float_first_final = 10; +static final int JSON_float_first_final = 8; static final int JSON_float_error = 0; static final int JSON_float_en_main = 1; -// line 504 "Parser.rl" +// line 526 "Parser.rl" ParserResult parseFloat(int p, int pe) { int cs = EVIL; -// line 1025 "Parser.java" +// line 1047 "Parser.java" { cs = JSON_float_start; } -// line 510 "Parser.rl" +// line 532 "Parser.rl" int memo = p; -// line 1033 "Parser.java" +// line 1055 "Parser.java" { int _klen; int _trans = 0; @@ -1110,13 +1132,13 @@ case 1: switch ( _JSON_float_actions[_acts++] ) { case 0: -// line 495 "Parser.rl" +// line 517 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1120 "Parser.java" +// line 1142 "Parser.java" } } } @@ -1136,7 +1158,7 @@ case 5: break; } } -// line 512 "Parser.rl" +// line 534 "Parser.rl" if (cs < JSON_float_first_final) { return null; @@ -1151,7 +1173,7 @@ case 5: } -// line 1155 "Parser.java" +// line 1177 "Parser.java" private static byte[] init__JSON_string_actions_0() { return new byte [] { @@ -1253,7 +1275,7 @@ static final int JSON_string_error = 0; static final int JSON_string_en_main = 1; -// line 556 "Parser.rl" +// line 578 "Parser.rl" ParserResult parseString(int p, int pe) { @@ -1261,15 +1283,15 @@ static final int JSON_string_en_main = 1; IRubyObject result = null; -// line 1265 "Parser.java" +// line 1287 "Parser.java" { cs = JSON_string_start; } -// line 563 "Parser.rl" +// line 585 "Parser.rl" int memo = p; -// line 1273 "Parser.java" +// line 1295 "Parser.java" { int _klen; int _trans = 0; @@ -1350,7 +1372,7 @@ case 1: switch ( _JSON_string_actions[_acts++] ) { case 0: -// line 531 "Parser.rl" +// line 553 "Parser.rl" { int offset = byteList.begin(); ByteList decoded = decoder.decode(byteList, memo + 1 - offset, @@ -1365,13 +1387,13 @@ case 1: } break; case 1: -// line 544 "Parser.rl" +// line 566 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1375 "Parser.java" +// line 1397 "Parser.java" } } } @@ -1391,7 +1413,7 @@ case 5: break; } } -// line 565 "Parser.rl" +// line 587 "Parser.rl" if (parser.createAdditions) { RubyHash match_string = parser.match_string; @@ -1408,7 +1430,7 @@ case 5: } }); } catch (JumpException e) { } - if (memoArray[1] != null) { + if (memoArray[1] != null) { RubyClass klass = (RubyClass) memoArray[1]; if (klass.respondsTo("json_creatable?") && klass.callMethod(context, "json_creatable?").isTrue()) { @@ -1426,7 +1448,7 @@ case 5: } -// line 1430 "Parser.java" +// line 1452 "Parser.java" private static byte[] init__JSON_array_actions_0() { return new byte [] { @@ -1539,7 +1561,7 @@ static final int JSON_array_error = 0; static final int JSON_array_en_main = 1; -// line 635 "Parser.rl" +// line 657 "Parser.rl" ParserResult parseArray(int p, int pe) { @@ -1557,14 +1579,14 @@ static final int JSON_array_en_main = 1; IRubyObject.NULL_ARRAY, Block.NULL_BLOCK); -// line 1561 "Parser.java" +// line 1583 "Parser.java" { cs = JSON_array_start; } -// line 652 "Parser.rl" +// line 674 "Parser.rl" -// line 1568 "Parser.java" +// line 1590 "Parser.java" { int _klen; int _trans = 0; @@ -1645,7 +1667,7 @@ case 1: switch ( _JSON_array_actions[_acts++] ) { case 0: -// line 604 "Parser.rl" +// line 626 "Parser.rl" { ParserResult res = parseValue(p, pe); if (res == null) { @@ -1662,13 +1684,13 @@ case 1: } break; case 1: -// line 619 "Parser.rl" +// line 641 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1672 "Parser.java" +// line 1694 "Parser.java" } } } @@ -1688,7 +1710,7 @@ case 5: break; } } -// line 653 "Parser.rl" +// line 675 "Parser.rl" if (cs >= JSON_array_first_final) { return new ParserResult(result, p + 1); @@ -1698,7 +1720,7 @@ case 5: } -// line 1702 "Parser.java" +// line 1724 "Parser.java" private static byte[] init__JSON_object_actions_0() { return new byte [] { @@ -1821,7 +1843,7 @@ static final int JSON_object_error = 0; static final int JSON_object_en_main = 1; -// line 713 "Parser.rl" +// line 734 "Parser.rl" ParserResult parseObject(int p, int pe) { @@ -1840,14 +1862,14 @@ static final int JSON_object_en_main = 1; IRubyObject.NULL_ARRAY, Block.NULL_BLOCK); -// line 1844 "Parser.java" +// line 1866 "Parser.java" { cs = JSON_object_start; } -// line 731 "Parser.rl" +// line 752 "Parser.rl" -// line 1851 "Parser.java" +// line 1873 "Parser.java" { int _klen; int _trans = 0; @@ -1928,7 +1950,7 @@ case 1: switch ( _JSON_object_actions[_acts++] ) { case 0: -// line 667 "Parser.rl" +// line 689 "Parser.rl" { ParserResult res = parseValue(p, pe); if (res == null) { @@ -1945,7 +1967,7 @@ case 1: } break; case 1: -// line 682 "Parser.rl" +// line 704 "Parser.rl" { ParserResult res = parseString(p, pe); if (res == null) { @@ -1965,13 +1987,13 @@ case 1: } break; case 2: -// line 700 "Parser.rl" +// line 722 "Parser.rl" { p--; { p += 1; _goto_targ = 5; if (true) continue _goto;} } break; -// line 1975 "Parser.java" +// line 1997 "Parser.java" } } } @@ -1991,7 +2013,7 @@ case 5: break; } } -// line 732 "Parser.rl" +// line 753 "Parser.rl" if (cs < JSON_object_first_final) { return null; @@ -2017,7 +2039,7 @@ case 5: } -// line 2021 "Parser.java" +// line 2043 "Parser.java" private static byte[] init__JSON_actions_0() { return new byte [] { @@ -2121,25 +2143,25 @@ static final int JSON_error = 0; static final int JSON_en_main = 1; -// line 790 "Parser.rl" +// line 811 "Parser.rl" - public IRubyObject parse() { + public IRubyObject parseStrict() { int cs = EVIL; int p, pe; IRubyObject result = null; -// line 2134 "Parser.java" +// line 2156 "Parser.java" { cs = JSON_start; } -// line 798 "Parser.rl" +// line 819 "Parser.rl" p = byteList.begin(); pe = p + byteList.length(); -// line 2143 "Parser.java" +// line 2165 "Parser.java" { int _klen; int _trans = 0; @@ -2220,7 +2242,7 @@ case 1: switch ( _JSON_actions[_acts++] ) { case 0: -// line 762 "Parser.rl" +// line 783 "Parser.rl" { currentNesting = 1; ParserResult res = parseObject(p, pe); @@ -2234,7 +2256,7 @@ case 1: } break; case 1: -// line 774 "Parser.rl" +// line 795 "Parser.rl" { currentNesting = 1; ParserResult res = parseArray(p, pe); @@ -2247,7 +2269,7 @@ case 1: } } break; -// line 2251 "Parser.java" +// line 2273 "Parser.java" } } } @@ -2267,7 +2289,7 @@ case 5: break; } } -// line 801 "Parser.rl" +// line 822 "Parser.rl" if (cs >= JSON_first_final && p == pe) { return result; @@ -2276,6 +2298,259 @@ case 5: } } + +// line 2303 "Parser.java" +private static byte[] init__JSON_quirks_mode_actions_0() +{ + return new byte [] { + 0, 1, 0 + }; +} + +private static final byte _JSON_quirks_mode_actions[] = init__JSON_quirks_mode_actions_0(); + + +private static byte[] init__JSON_quirks_mode_key_offsets_0() +{ + return new byte [] { + 0, 0, 16, 18, 19, 21, 22, 24, 25, 27, 28 + }; +} + +private static final byte _JSON_quirks_mode_key_offsets[] = init__JSON_quirks_mode_key_offsets_0(); + + +private static char[] init__JSON_quirks_mode_trans_keys_0() +{ + return new char [] { + 13, 32, 34, 45, 47, 73, 78, 91, 102, 110, 116, 123, + 9, 10, 48, 57, 42, 47, 42, 42, 47, 10, 42, 47, + 42, 42, 47, 10, 13, 32, 47, 9, 10, 0 + }; +} + +private static final char _JSON_quirks_mode_trans_keys[] = init__JSON_quirks_mode_trans_keys_0(); + + +private static byte[] init__JSON_quirks_mode_single_lengths_0() +{ + return new byte [] { + 0, 12, 2, 1, 2, 1, 2, 1, 2, 1, 3 + }; +} + +private static final byte _JSON_quirks_mode_single_lengths[] = init__JSON_quirks_mode_single_lengths_0(); + + +private static byte[] init__JSON_quirks_mode_range_lengths_0() +{ + return new byte [] { + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1 + }; +} + +private static final byte _JSON_quirks_mode_range_lengths[] = init__JSON_quirks_mode_range_lengths_0(); + + +private static byte[] init__JSON_quirks_mode_index_offsets_0() +{ + return new byte [] { + 0, 0, 15, 18, 20, 23, 25, 28, 30, 33, 35 + }; +} + +private static final byte _JSON_quirks_mode_index_offsets[] = init__JSON_quirks_mode_index_offsets_0(); + + +private static byte[] init__JSON_quirks_mode_indicies_0() +{ + return new byte [] { + 0, 0, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 1, 4, 5, 1, 6, 4, 6, 7, 4, 7, + 5, 8, 9, 1, 10, 8, 10, 0, 8, 0, 9, 7, + 7, 11, 7, 1, 0 + }; +} + +private static final byte _JSON_quirks_mode_indicies[] = init__JSON_quirks_mode_indicies_0(); + + +private static byte[] init__JSON_quirks_mode_trans_targs_0() +{ + return new byte [] { + 1, 0, 10, 6, 3, 5, 4, 10, 7, 9, 8, 2 + }; +} + +private static final byte _JSON_quirks_mode_trans_targs[] = init__JSON_quirks_mode_trans_targs_0(); + + +private static byte[] init__JSON_quirks_mode_trans_actions_0() +{ + return new byte [] { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; +} + +private static final byte _JSON_quirks_mode_trans_actions[] = init__JSON_quirks_mode_trans_actions_0(); + + +static final int JSON_quirks_mode_start = 1; +static final int JSON_quirks_mode_first_final = 10; +static final int JSON_quirks_mode_error = 0; + +static final int JSON_quirks_mode_en_main = 1; + + +// line 850 "Parser.rl" + + + public IRubyObject parseQuirksMode() { + int cs = EVIL; + int p, pe; + IRubyObject result = null; + + +// line 2415 "Parser.java" + { + cs = JSON_quirks_mode_start; + } + +// line 858 "Parser.rl" + p = byteList.begin(); + pe = p + byteList.length(); + +// line 2424 "Parser.java" + { + int _klen; + int _trans = 0; + int _acts; + int _nacts; + int _keys; + int _goto_targ = 0; + + _goto: while (true) { + switch ( _goto_targ ) { + case 0: + if ( p == pe ) { + _goto_targ = 4; + continue _goto; + } + if ( cs == 0 ) { + _goto_targ = 5; + continue _goto; + } +case 1: + _match: do { + _keys = _JSON_quirks_mode_key_offsets[cs]; + _trans = _JSON_quirks_mode_index_offsets[cs]; + _klen = _JSON_quirks_mode_single_lengths[cs]; + if ( _klen > 0 ) { + int _lower = _keys; + int _mid; + int _upper = _keys + _klen - 1; + while (true) { + if ( _upper < _lower ) + break; + + _mid = _lower + ((_upper-_lower) >> 1); + if ( data[p] < _JSON_quirks_mode_trans_keys[_mid] ) + _upper = _mid - 1; + else if ( data[p] > _JSON_quirks_mode_trans_keys[_mid] ) + _lower = _mid + 1; + else { + _trans += (_mid - _keys); + break _match; + } + } + _keys += _klen; + _trans += _klen; + } + + _klen = _JSON_quirks_mode_range_lengths[cs]; + if ( _klen > 0 ) { + int _lower = _keys; + int _mid; + int _upper = _keys + (_klen<<1) - 2; + while (true) { + if ( _upper < _lower ) + break; + + _mid = _lower + (((_upper-_lower) >> 1) & ~1); + if ( data[p] < _JSON_quirks_mode_trans_keys[_mid] ) + _upper = _mid - 2; + else if ( data[p] > _JSON_quirks_mode_trans_keys[_mid+1] ) + _lower = _mid + 2; + else { + _trans += ((_mid - _keys)>>1); + break _match; + } + } + _trans += _klen; + } + } while (false); + + _trans = _JSON_quirks_mode_indicies[_trans]; + cs = _JSON_quirks_mode_trans_targs[_trans]; + + if ( _JSON_quirks_mode_trans_actions[_trans] != 0 ) { + _acts = _JSON_quirks_mode_trans_actions[_trans]; + _nacts = (int) _JSON_quirks_mode_actions[_acts++]; + while ( _nacts-- > 0 ) + { + switch ( _JSON_quirks_mode_actions[_acts++] ) + { + case 0: +// line 836 "Parser.rl" + { + ParserResult res = parseValue(p, pe); + if (res == null) { + p--; + { p += 1; _goto_targ = 5; if (true) continue _goto;} + } else { + result = res.result; + {p = (( res.p))-1;} + } + } + break; +// line 2517 "Parser.java" + } + } + } + +case 2: + if ( cs == 0 ) { + _goto_targ = 5; + continue _goto; + } + if ( ++p != pe ) { + _goto_targ = 1; + continue _goto; + } +case 4: +case 5: + } + break; } + } + +// line 861 "Parser.rl" + + if (cs >= JSON_quirks_mode_first_final && p == pe) { + return result; + } else { + throw unexpectedToken(p, pe); + } + } + + public IRubyObject parse() { + if (parser.quirksMode) { + return parseQuirksMode(); + } else { + return parseStrict(); + } + + } + /** * Returns a subsequence of the source ByteList, based on source * array byte offsets (i.e., the ByteList's own begin offset is not diff --git a/java/src/json/ext/Parser.rl b/java/src/json/ext/Parser.rl index 779d3f3..e8cd874 100644 --- a/java/src/json/ext/Parser.rl +++ b/java/src/json/ext/Parser.rl @@ -1,6 +1,6 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * + * * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files * for details. */ @@ -29,16 +29,16 @@ import org.jruby.util.ByteList; /** * The <code>JSON::Ext::Parser</code> class. - * + * * <p>This is the JSON parser implemented as a Java class. To use it as the * standard parser, set * <pre>JSON.parser = JSON::Ext::Parser</pre> * This is performed for you when you <code>include "json/ext"</code>. - * + * * <p>This class does not perform the actual parsing, just acts as an interface * to Ruby code. When the {@link #parse()} method is invoked, a * Parser.ParserSession object is instantiated, which handles the process. - * + * * @author mernen */ public class Parser extends RubyObject { @@ -49,6 +49,7 @@ public class Parser extends RubyObject { private int maxNesting; private boolean allowNaN; private boolean symbolizeNames; + private boolean quirksMode; private RubyClass objectClass; private RubyClass arrayClass; private RubyHash match_string; @@ -69,7 +70,7 @@ public class Parser extends RubyObject { /** * Multiple-value return for internal parser methods. - * + * * <p>All the <code>parse<var>Stuff</var></code> methods return instances of * <code>ParserResult</code> when successful, or <code>null</code> when * there's a problem with the input data. @@ -98,18 +99,18 @@ public class Parser extends RubyObject { /** * <code>Parser.new(source, opts = {})</code> - * + * * <p>Creates a new <code>JSON::Ext::Parser</code> instance for the string * <code>source</code>. * It will be configured by the <code>opts</code> Hash. * <code>opts</code> can have the following keys: - * + * * <dl> * <dt><code>:max_nesting</code> * <dd>The maximum depth of nesting allowed in the parsed data * structures. Disable depth checking with <code>:max_nesting => false|nil|0</code>, * it defaults to 19. - * + * * <dt><code>:allow_nan</code> * <dd>If set to <code>true</code>, allow <code>NaN</code>, * <code>Infinity</code> and <code>-Infinity</code> in defiance of RFC 4627 @@ -118,17 +119,25 @@ public class Parser extends RubyObject { * <dt><code>:symbolize_names</code> * <dd>If set to <code>true</code>, returns symbols for the names (keys) in * a JSON object. Otherwise strings are returned, which is also the default. + * + * <dt><code>:quirks_mode?</code> + * <dd>If set to <code>true</code>, if the parse is in quirks_mode, false + * otherwise. * * <dt><code>:create_additions</code> * <dd>If set to <code>false</code>, the Parser doesn't create additions * even if a matchin class and <code>create_id</code> was found. This option * defaults to <code>true</code>. - * + * * <dt><code>:object_class</code> * <dd>Defaults to Hash. - * + * * <dt><code>:array_class</code> * <dd>Defaults to Array. + * + * <dt><code>:quirks_mode</code> + * <dd>Enables quirks_mode for parser, that is for example parsing single + * JSON values instead of documents is possible. * </dl> */ @JRubyMethod(name = "new", required = 1, optional = 1, meta = true) @@ -146,19 +155,21 @@ public class Parser extends RubyObject { if (this.vSource != null) { throw runtime.newTypeError("already initialized instance"); } - RubyString source = convertEncoding(context, args[0].convertToString()); OptionsReader opts = new OptionsReader(context, args.length > 1 ? args[1] : null); this.maxNesting = opts.getInt("max_nesting", DEFAULT_MAX_NESTING); this.allowNaN = opts.getBool("allow_nan", false); this.symbolizeNames = opts.getBool("symbolize_names", false); + this.quirksMode = opts.getBool("quirks_mode", false); this.createId = opts.getString("create_id", getCreateId(context)); this.createAdditions = opts.getBool("create_additions", true); this.objectClass = opts.getClass("object_class", runtime.getHash()); this.arrayClass = opts.getClass("array_class", runtime.getArray()); this.match_string = opts.getHash("match_string"); - this.vSource = source; + this.vSource = args[0].convertToString(); + if (!quirksMode) this.vSource = convertEncoding(context, vSource); + return this; } @@ -227,7 +238,7 @@ public class Parser extends RubyObject { /** * <code>Parser#parse()</code> - * + * * <p>Parses the current JSON text <code>source</code> and returns the * complete data structure as a result. */ @@ -238,7 +249,7 @@ public class Parser extends RubyObject { /** * <code>Parser#source()</code> - * + * * <p>Returns a copy of the current <code>source</code> string, that was * used to construct this Parser. */ @@ -247,6 +258,17 @@ public class Parser extends RubyObject { return checkAndGetSource().dup(); } + /** + * <code>Parser#quirks_mode?()</code> + * + * <p>If set to <code>true</code>, if the parse is in quirks_mode, false + * otherwise. + */ + @JRubyMethod(name = "quirks_mode?") + public IRubyObject quirks_mode_p(ThreadContext context) { + return context.getRuntime().newBoolean(quirksMode); + } + public RubyString checkAndGetSource() { if (vSource != null) { return vSource; @@ -266,7 +288,7 @@ public class Parser extends RubyObject { /** * A string parsing session. - * + * * <p>Once a ParserSession is instantiated, the source string should not * change until the parsing is complete. The ParserSession object assumes * the source {@link RubyString} is still associated to its original @@ -364,7 +386,7 @@ public class Parser extends RubyObject { } } action parse_number { - if (pe > fpc + 9 && + if (pe > fpc + 9 - (parser.quirksMode ? 1 : 0) && absSubSequence(fpc, fpc + 9).toString().equals(JSON_MINUS_INFINITY)) { if (parser.allowNaN) { @@ -464,7 +486,7 @@ public class Parser extends RubyObject { fbreak; } - main := '-'? ( '0' | [1-9][0-9]* ) ( ^[0-9] @exit ); + main := '-'? ( '0' | [1-9][0-9]* ) ( ^[0-9]? @exit ); }%% ParserResult parseInteger(int p, int pe) { @@ -500,7 +522,7 @@ public class Parser extends RubyObject { main := '-'? ( ( ( '0' | [1-9][0-9]* ) '.' [0-9]+ ( [Ee] [+\-]?[0-9]+ )? ) | ( ( '0' | [1-9][0-9]* ) ( [Ee] [+\-]? [0-9]+ ) ) ) - ( ^[0-9Ee.\-] @exit ); + ( ^[0-9Ee.\-]? @exit ); }%% ParserResult parseFloat(int p, int pe) { @@ -578,7 +600,7 @@ public class Parser extends RubyObject { } }); } catch (JumpException e) { } - if (memoArray[1] != null) { + if (memoArray[1] != null) { RubyClass klass = (RubyClass) memoArray[1]; if (klass.respondsTo("json_creatable?") && klass.callMethod(context, "json_creatable?").isTrue()) { @@ -701,15 +723,14 @@ public class Parser extends RubyObject { fhold; fbreak; } + + pair = ignore* begin_name >parse_name ignore* name_separator + ignore* begin_value >parse_value; + next_pair = ignore* value_separator pair; - a_pair = ignore* - begin_name >parse_name - ignore* name_separator ignore* - begin_value >parse_value; - - main := begin_object - (a_pair (ignore* value_separator a_pair)*)? - ignore* end_object @exit; + main := ( + begin_object (pair (next_pair)*)? ignore* end_object + ) @exit; }%% ParserResult parseObject(int p, int pe) { @@ -789,7 +810,7 @@ public class Parser extends RubyObject { ignore*; }%% - public IRubyObject parse() { + public IRubyObject parseStrict() { int cs = EVIL; int p, pe; IRubyObject result = null; @@ -806,6 +827,54 @@ public class Parser extends RubyObject { } } + %%{ + machine JSON_quirks_mode; + include JSON_common; + + write data; + + action parse_value { + ParserResult res = parseValue(fpc, pe); + if (res == null) { + fhold; + fbreak; + } else { + result = res.result; + fexec res.p; + } + } + + main := ignore* + ( begin_value >parse_value) + ignore*; + }%% + + public IRubyObject parseQuirksMode() { + int cs = EVIL; + int p, pe; + IRubyObject result = null; + + %% write init; + p = byteList.begin(); + pe = p + byteList.length(); + %% write exec; + + if (cs >= JSON_quirks_mode_first_final && p == pe) { + return result; + } else { + throw unexpectedToken(p, pe); + } + } + + public IRubyObject parse() { + if (parser.quirksMode) { + return parseQuirksMode(); + } else { + return parseStrict(); + } + + } + /** * Returns a subsequence of the source ByteList, based on source * array byte offsets (i.e., the ByteList's own begin offset is not diff --git a/java/src/json/ext/ParserService.java b/java/src/json/ext/ParserService.java index f2ecae1..dde8834 100644 --- a/java/src/json/ext/ParserService.java +++ b/java/src/json/ext/ParserService.java @@ -1,6 +1,6 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * + * * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files * for details. */ diff --git a/java/src/json/ext/Utils.java b/java/src/json/ext/Utils.java index f52ac8d..44d6a55 100644 --- a/java/src/json/ext/Utils.java +++ b/java/src/json/ext/Utils.java @@ -1,6 +1,6 @@ /* * This code is copyrighted work by Daniel Luz <dev at mernen dot com>. - * + * * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files * for details. */ diff --git a/json.gemspec b/json.gemspec index 3bd955a..65bead0 100644 --- a/json.gemspec +++ b/json.gemspec @@ -2,27 +2,26 @@ Gem::Specification.new do |s| s.name = %q{json} - s.version = "1.5.4" + s.version = "1.6.1" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = [%q{Florian Frank}] - s.date = %q{2011-07-08} + s.date = %q{2011-09-18} s.description = %q{This is a JSON implementation as a Ruby extension in C.} s.email = %q{flori@ping.de} - s.executables = [%q{edit_json.rb}, %q{prettify_json.rb}] s.extensions = [%q{ext/json/ext/parser/extconf.rb}, %q{ext/json/ext/generator/extconf.rb}] s.extra_rdoc_files = [%q{README.rdoc}] - s.files = [%q{tests}, %q{tests/test_json_string_matching.rb}, %q{tests/test_json_fixtures.rb}, %q{tests/setup_variant.rb}, %q{tests/fixtures}, %q{tests/fixtures/fail6.json}, %q{tests/fixtures/fail9.json}, %q{tests/fixtures/fail10.json}, %q{tests/fixtures/fail24.json}, %q{tests/fixtures/fail28.json}, %q{tests/fixtures/fail13.json}, %q{tests/fixtures/fail4.json}, %q{tests/fixtures/pass3.json}, %q{tests/fixtures/fail11.json}, %q{tests/fixtures/fail14.json}, %q{tests/fixtures/fail3.json}, %q{tests/fixtures/fail12.json}, %q{tests/fixtures/pass16.json}, %q{tests/fixtures/pass15.json}, %q{tests/fixtures/fail20.json}, %q{tests/fixtures/fail8.json}, %q{tests/fixtures/pass2.json}, %q{tests/fixtures/fail5.json}, %q{tests/fixtures/fail1.json}, %q{tests/fixtures/fail25.json}, %q{tests/fixtures/pass17.json}, %q{tests/fixtures/fail7.json}, %q{tests/fixtures/pass26.json}, %q{tests/fixtures/fail21.json}, %q{tests/fixtures/pass1.json}, %q{tests/fixtures/fail23.json}, %q{tests/fixtures/fail18.json}, %q{tests/fixtures/fail2.json}, %q{tests/fixtures/fail22.json}, %q{tests/fixtures/fail27.json}, %q{tests/fixtures/fail19.json}, %q{tests/test_json_unicode.rb}, %q{tests/test_json_addition.rb}, %q{tests/test_json_generate.rb}, %q{tests/test_json_encoding.rb}, %q{tests/test_json.rb}, %q{COPYING}, %q{TODO}, %q{Rakefile}, %q{benchmarks}, %q{benchmarks/data-p4-3GHz-ruby18}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat}, %q{benchmarks/parser2_benchmark.rb}, %q{benchmarks/parser_benchmark.rb}, %q{benchmarks/generator2_benchmark.rb}, %q{benchmarks/generator_benchmark.rb}, %q{benchmarks/ohai.ruby}, %q{benchmarks/data}, %q{benchmarks/ohai.json}, %q{lib}, %q{lib/json}, %q{lib/json/json.xpm}, %q{lib/json/TrueClass.xpm}, %q{lib/json/version.rb}, %q{lib/json/Array.xpm}, %q{lib/json/add}, %q{lib/json/add/core.rb}, %q{lib/json/add/rails.rb}, %q{lib/json/common.rb}, %q{lib/json/pure}, %q{lib/json/pure/generator.rb}, %q{lib/json/pure/parser.rb}, %q{lib/json/ext.rb}, %q{lib/json/pure.rb}, %q{lib/json/Key.xpm}, %q{lib/json/FalseClass.xpm}, %q{lib/json/editor.rb}, %q{lib/json/Numeric.xpm}, %q{lib/json/ext}, %q{lib/json/NilClass.xpm}, %q{lib/json/String.xpm}, %q{lib/json/Hash.xpm}, %q{lib/json.rb}, %q{Gemfile}, %q{README.rdoc}, %q{json_pure.gemspec}, %q{GPL}, %q{CHANGES}, %q{bin}, %q{bin/prettify_json.rb}, %q{bin/edit_json.rb}, %q{COPYING-json-jruby}, %q{ext}, %q{ext/json}, %q{ext/json/ext}, %q{ext/json/ext/parser}, %q{ext/json/ext/parser/parser.h}, %q{ext/json/ext/parser/extconf.rb}, %q{ext/json/ext/parser/parser.rl}, %q{ext/json/ext/parser/parser.c}, %q{ext/json/ext/generator}, %q{ext/json/ext/generator/generator.c}, %q{ext/json/ext/generator/extconf.rb}, %q{ext/json/ext/generator/generator.h}, %q{VERSION}, %q{data}, %q{data/prototype.js}, %q{data/index.html}, %q{data/example.json}, %q{json.gemspec}, %q{java}, %q{java/src}, %q{java/src/json}, %q{java/src/json/ext}, %q{java/src/json/ext/Parser.java}, %q{java/src/json/ext/RuntimeInfo.java}, %q{java/src/json/ext/GeneratorState.java}, %q{java/src/json/ext/OptionsReader.java}, %q{java/src/json/ext/ParserService.java}, %q{java/src/json/ext/Parser.rl}, %q{java/src/json/ext/StringEncoder.java}, %q{java/src/json/ext/GeneratorService.java}, %q{java/src/json/ext/Utils.java}, %q{java/src/json/ext/StringDecoder.java}, %q{java/src/json/ext/Generator.java}, %q{java/src/json/ext/ByteListTranscoder.java}, %q{java/src/json/ext/GeneratorMethods.java}, %q{java/lib}, %q{java/lib/bytelist-1.0.6.jar}, %q{java/lib/jcodings.jar}, %q{diagrams}, %q{README-json-jruby.markdown}, %q{install.rb}, %q{json-java.gemspec}, %q{tools}, %q{tools/fuzz.rb}, %q{tools/server.rb}, %q{./tests/test_json_string_matching.rb}, %q{./tests/test_json_fixtures.rb}, %q{./tests/test_json_unicode.rb}, %q{./tests/test_json_addition.rb}, %q{./tests/test_json_generate.rb}, %q{./tests/test_json_encoding.rb}, %q{./tests/test_json.rb}] + s.files = [%q{tests}, %q{tests/test_json_string_matching.rb}, %q{tests/test_json_fixtures.rb}, %q{tests/setup_variant.rb}, %q{tests/fixtures}, %q{tests/fixtures/fail6.json}, %q{tests/fixtures/fail9.json}, %q{tests/fixtures/fail10.json}, %q{tests/fixtures/fail24.json}, %q{tests/fixtures/fail28.json}, %q{tests/fixtures/fail13.json}, %q{tests/fixtures/fail4.json}, %q{tests/fixtures/pass3.json}, %q{tests/fixtures/fail11.json}, %q{tests/fixtures/fail14.json}, %q{tests/fixtures/fail3.json}, %q{tests/fixtures/fail12.json}, %q{tests/fixtures/pass16.json}, %q{tests/fixtures/pass15.json}, %q{tests/fixtures/fail20.json}, %q{tests/fixtures/fail8.json}, %q{tests/fixtures/pass2.json}, %q{tests/fixtures/fail5.json}, %q{tests/fixtures/fail1.json}, %q{tests/fixtures/fail25.json}, %q{tests/fixtures/pass17.json}, %q{tests/fixtures/fail7.json}, %q{tests/fixtures/pass26.json}, %q{tests/fixtures/fail21.json}, %q{tests/fixtures/pass1.json}, %q{tests/fixtures/fail23.json}, %q{tests/fixtures/fail18.json}, %q{tests/fixtures/fail2.json}, %q{tests/fixtures/fail22.json}, %q{tests/fixtures/fail27.json}, %q{tests/fixtures/fail19.json}, %q{tests/test_json_unicode.rb}, %q{tests/test_json_addition.rb}, %q{tests/test_json_generate.rb}, %q{tests/test_json_encoding.rb}, %q{tests/test_json.rb}, %q{COPYING}, %q{TODO}, %q{Rakefile}, %q{benchmarks}, %q{benchmarks/data-p4-3GHz-ruby18}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat}, %q{benchmarks/parser2_benchmark.rb}, %q{benchmarks/parser_benchmark.rb}, %q{benchmarks/generator2_benchmark.rb}, %q{benchmarks/generator_benchmark.rb}, %q{benchmarks/ohai.ruby}, %q{benchmarks/data}, %q{benchmarks/ohai.json}, %q{lib}, %q{lib/json}, %q{lib/json/version.rb}, %q{lib/json/add}, %q{lib/json/add/symbol.rb}, %q{lib/json/add/struct.rb}, %q{lib/json/add/complex.rb}, %q{lib/json/add/rational.rb}, %q{lib/json/add/exception.rb}, %q{lib/json/add/time.rb}, %q{lib/json/add/date_time.rb}, %q{lib/json/add/core.rb}, %q{lib/json/add/range.rb}, %q{lib/json/add/date.rb}, %q{lib/json/add/regexp.rb}, %q{lib/json/common.rb}, %q{lib/json/pure}, %q{lib/json/pure/generator.rb}, %q{lib/json/pure/parser.rb}, %q{lib/json/ext.rb}, %q{lib/json/pure.rb}, %q{lib/json/ext}, %q{lib/json.rb}, %q{Gemfile}, %q{README.rdoc}, %q{json_pure.gemspec}, %q{GPL}, %q{CHANGES}, %q{COPYING-json-jruby}, %q{ext}, %q{ext/json}, %q{ext/json/ext}, %q{ext/json/ext/parser}, %q{ext/json/ext/parser/parser.h}, %q{ext/json/ext/parser/extconf.rb}, %q{ext/json/ext/parser/parser.rl}, %q{ext/json/ext/parser/parser.c}, %q{ext/json/ext/generator}, %q{ext/json/ext/generator/generator.c}, %q{ext/json/ext/generator/extconf.rb}, %q{ext/json/ext/generator/generator.h}, %q{VERSION}, %q{data}, %q{data/prototype.js}, %q{data/index.html}, %q{data/example.json}, %q{json.gemspec}, %q{java}, %q{java/src}, %q{java/src/json}, %q{java/src/json/ext}, %q{java/src/json/ext/Parser.java}, %q{java/src/json/ext/RuntimeInfo.java}, %q{java/src/json/ext/GeneratorState.java}, %q{java/src/json/ext/OptionsReader.java}, %q{java/src/json/ext/ParserService.java}, %q{java/src/json/ext/Parser.rl}, %q{java/src/json/ext/StringEncoder.java}, %q{java/src/json/ext/GeneratorService.java}, %q{java/src/json/ext/Utils.java}, %q{java/src/json/ext/StringDecoder.java}, %q{java/src/json/ext/Generator.java}, %q{java/src/json/ext/ByteListTranscoder.java}, %q{java/src/json/ext/GeneratorMethods.java}, %q{java/lib}, %q{java/lib/bytelist-1.0.6.jar}, %q{java/lib/jcodings.jar}, %q{diagrams}, %q{README-json-jruby.markdown}, %q{install.rb}, %q{json-java.gemspec}, %q{tools}, %q{tools/fuzz.rb}, %q{tools/server.rb}, %q{./tests/test_json_string_matching.rb}, %q{./tests/test_json_fixtures.rb}, %q{./tests/test_json_unicode.rb}, %q{./tests/test_json_addition.rb}, %q{./tests/test_json_generate.rb}, %q{./tests/test_json_encoding.rb}, %q{./tests/test_json.rb}] s.homepage = %q{http://flori.github.com/json} s.rdoc_options = [%q{--title}, %q{JSON implemention for Ruby}, %q{--main}, %q{README.rdoc}] s.require_paths = [%q{ext/json/ext}, %q{ext}, %q{lib}] s.rubyforge_project = %q{json} - s.rubygems_version = %q{1.8.5} + s.rubygems_version = %q{1.8.6} s.summary = %q{JSON Implementation for Ruby} s.test_files = [%q{./tests/test_json_string_matching.rb}, %q{./tests/test_json_fixtures.rb}, %q{./tests/test_json_unicode.rb}, %q{./tests/test_json_addition.rb}, %q{./tests/test_json_generate.rb}, %q{./tests/test_json_encoding.rb}, %q{./tests/test_json.rb}] if s.respond_to? :specification_version then - s.specification_version = 4 + s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q<permutation>, [">= 0"]) diff --git a/json_pure.gemspec b/json_pure.gemspec index a3be22a..5b8cb04 100644 --- a/json_pure.gemspec +++ b/json_pure.gemspec @@ -2,45 +2,41 @@ Gem::Specification.new do |s| s.name = %q{json_pure} - s.version = "1.5.4" + s.version = "1.6.1" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = [%q{Florian Frank}] - s.date = %q{2011-07-08} + s.date = %q{2011-09-18} s.description = %q{This is a JSON implementation in pure Ruby.} s.email = %q{flori@ping.de} - s.executables = [%q{edit_json.rb}, %q{prettify_json.rb}] s.extra_rdoc_files = [%q{README.rdoc}] - s.files = [%q{tests}, %q{tests/test_json_string_matching.rb}, %q{tests/test_json_fixtures.rb}, %q{tests/setup_variant.rb}, %q{tests/fixtures}, %q{tests/fixtures/fail6.json}, %q{tests/fixtures/fail9.json}, %q{tests/fixtures/fail10.json}, %q{tests/fixtures/fail24.json}, %q{tests/fixtures/fail28.json}, %q{tests/fixtures/fail13.json}, %q{tests/fixtures/fail4.json}, %q{tests/fixtures/pass3.json}, %q{tests/fixtures/fail11.json}, %q{tests/fixtures/fail14.json}, %q{tests/fixtures/fail3.json}, %q{tests/fixtures/fail12.json}, %q{tests/fixtures/pass16.json}, %q{tests/fixtures/pass15.json}, %q{tests/fixtures/fail20.json}, %q{tests/fixtures/fail8.json}, %q{tests/fixtures/pass2.json}, %q{tests/fixtures/fail5.json}, %q{tests/fixtures/fail1.json}, %q{tests/fixtures/fail25.json}, %q{tests/fixtures/pass17.json}, %q{tests/fixtures/fail7.json}, %q{tests/fixtures/pass26.json}, %q{tests/fixtures/fail21.json}, %q{tests/fixtures/pass1.json}, %q{tests/fixtures/fail23.json}, %q{tests/fixtures/fail18.json}, %q{tests/fixtures/fail2.json}, %q{tests/fixtures/fail22.json}, %q{tests/fixtures/fail27.json}, %q{tests/fixtures/fail19.json}, %q{tests/test_json_unicode.rb}, %q{tests/test_json_addition.rb}, %q{tests/test_json_generate.rb}, %q{tests/test_json_encoding.rb}, %q{tests/test_json.rb}, %q{COPYING}, %q{TODO}, %q{Rakefile}, %q{benchmarks}, %q{benchmarks/data-p4-3GHz-ruby18}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat}, %q{benchmarks/parser2_benchmark.rb}, %q{benchmarks/parser_benchmark.rb}, %q{benchmarks/generator2_benchmark.rb}, %q{benchmarks/generator_benchmark.rb}, %q{benchmarks/ohai.ruby}, %q{benchmarks/data}, %q{benchmarks/ohai.json}, %q{lib}, %q{lib/json}, %q{lib/json/json.xpm}, %q{lib/json/TrueClass.xpm}, %q{lib/json/version.rb}, %q{lib/json/Array.xpm}, %q{lib/json/add}, %q{lib/json/add/core.rb}, %q{lib/json/add/rails.rb}, %q{lib/json/common.rb}, %q{lib/json/pure}, %q{lib/json/pure/generator.rb}, %q{lib/json/pure/parser.rb}, %q{lib/json/ext.rb}, %q{lib/json/pure.rb}, %q{lib/json/Key.xpm}, %q{lib/json/FalseClass.xpm}, %q{lib/json/editor.rb}, %q{lib/json/Numeric.xpm}, %q{lib/json/ext}, %q{lib/json/NilClass.xpm}, %q{lib/json/String.xpm}, %q{lib/json/Hash.xpm}, %q{lib/json.rb}, %q{Gemfile}, %q{README.rdoc}, %q{json_pure.gemspec}, %q{GPL}, %q{CHANGES}, %q{bin}, %q{bin/prettify_json.rb}, %q{bin/edit_json.rb}, %q{COPYING-json-jruby}, %q{ext}, %q{ext/json}, %q{ext/json/ext}, %q{ext/json/ext/parser}, %q{ext/json/ext/parser/parser.h}, %q{ext/json/ext/parser/extconf.rb}, %q{ext/json/ext/parser/parser.rl}, %q{ext/json/ext/parser/mkmf.log}, %q{ext/json/ext/parser/parser.c}, %q{ext/json/ext/generator}, %q{ext/json/ext/generator/generator.c}, %q{ext/json/ext/generator/extconf.rb}, %q{ext/json/ext/generator/generator.h}, %q{VERSION}, %q{data}, %q{data/prototype.js}, %q{data/index.html}, %q{data/example.json}, %q{json.gemspec}, %q{java}, %q{java/src}, %q{java/src/json}, %q{java/src/json/ext}, %q{java/src/json/ext/Parser.java}, %q{java/src/json/ext/RuntimeInfo.java}, %q{java/src/json/ext/GeneratorState.java}, %q{java/src/json/ext/OptionsReader.java}, %q{java/src/json/ext/ParserService.java}, %q{java/src/json/ext/Parser.rl}, %q{java/src/json/ext/StringEncoder.java}, %q{java/src/json/ext/GeneratorService.java}, %q{java/src/json/ext/Utils.java}, %q{java/src/json/ext/StringDecoder.java}, %q{java/src/json/ext/Generator.java}, %q{java/src/json/ext/ByteListTranscoder.java}, %q{java/src/json/ext/GeneratorMethods.java}, %q{java/lib}, %q{java/lib/bytelist-1.0.6.jar}, %q{java/lib/jcodings.jar}, %q{diagrams}, %q{README-json-jruby.markdown}, %q{install.rb}, %q{json-java.gemspec}, %q{tools}, %q{tools/fuzz.rb}, %q{tools/server.rb}, %q{./tests/test_json_string_matching.rb}, %q{./tests/test_json_fixtures.rb}, %q{./tests/test_json_unicode.rb}, %q{./tests/test_json_addition.rb}, %q{./tests/test_json_generate.rb}, %q{./tests/test_json_encoding.rb}, %q{./tests/test_json.rb}] + s.files = [%q{tests}, %q{tests/test_json_string_matching.rb}, %q{tests/test_json_fixtures.rb}, %q{tests/setup_variant.rb}, %q{tests/fixtures}, %q{tests/fixtures/fail6.json}, %q{tests/fixtures/fail9.json}, %q{tests/fixtures/fail10.json}, %q{tests/fixtures/fail24.json}, %q{tests/fixtures/fail28.json}, %q{tests/fixtures/fail13.json}, %q{tests/fixtures/fail4.json}, %q{tests/fixtures/pass3.json}, %q{tests/fixtures/fail11.json}, %q{tests/fixtures/fail14.json}, %q{tests/fixtures/fail3.json}, %q{tests/fixtures/fail12.json}, %q{tests/fixtures/pass16.json}, %q{tests/fixtures/pass15.json}, %q{tests/fixtures/fail20.json}, %q{tests/fixtures/fail8.json}, %q{tests/fixtures/pass2.json}, %q{tests/fixtures/fail5.json}, %q{tests/fixtures/fail1.json}, %q{tests/fixtures/fail25.json}, %q{tests/fixtures/pass17.json}, %q{tests/fixtures/fail7.json}, %q{tests/fixtures/pass26.json}, %q{tests/fixtures/fail21.json}, %q{tests/fixtures/pass1.json}, %q{tests/fixtures/fail23.json}, %q{tests/fixtures/fail18.json}, %q{tests/fixtures/fail2.json}, %q{tests/fixtures/fail22.json}, %q{tests/fixtures/fail27.json}, %q{tests/fixtures/fail19.json}, %q{tests/test_json_unicode.rb}, %q{tests/test_json_addition.rb}, %q{tests/test_json_generate.rb}, %q{tests/test_json_encoding.rb}, %q{tests/test_json.rb}, %q{COPYING}, %q{TODO}, %q{Rakefile}, %q{benchmarks}, %q{benchmarks/data-p4-3GHz-ruby18}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat}, %q{benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat}, %q{benchmarks/parser2_benchmark.rb}, %q{benchmarks/parser_benchmark.rb}, %q{benchmarks/generator2_benchmark.rb}, %q{benchmarks/generator_benchmark.rb}, %q{benchmarks/ohai.ruby}, %q{benchmarks/data}, %q{benchmarks/ohai.json}, %q{lib}, %q{lib/json}, %q{lib/json/version.rb}, %q{lib/json/add}, %q{lib/json/add/symbol.rb}, %q{lib/json/add/struct.rb}, %q{lib/json/add/complex.rb}, %q{lib/json/add/rational.rb}, %q{lib/json/add/exception.rb}, %q{lib/json/add/time.rb}, %q{lib/json/add/date_time.rb}, %q{lib/json/add/core.rb}, %q{lib/json/add/range.rb}, %q{lib/json/add/date.rb}, %q{lib/json/add/regexp.rb}, %q{lib/json/common.rb}, %q{lib/json/pure}, %q{lib/json/pure/generator.rb}, %q{lib/json/pure/parser.rb}, %q{lib/json/ext.rb}, %q{lib/json/pure.rb}, %q{lib/json/ext}, %q{lib/json.rb}, %q{Gemfile}, %q{README.rdoc}, %q{json_pure.gemspec}, %q{GPL}, %q{CHANGES}, %q{COPYING-json-jruby}, %q{ext}, %q{ext/json}, %q{ext/json/ext}, %q{ext/json/ext/parser}, %q{ext/json/ext/parser/parser.h}, %q{ext/json/ext/parser/extconf.rb}, %q{ext/json/ext/parser/parser.rl}, %q{ext/json/ext/parser/parser.c}, %q{ext/json/ext/generator}, %q{ext/json/ext/generator/generator.c}, %q{ext/json/ext/generator/extconf.rb}, %q{ext/json/ext/generator/generator.h}, %q{VERSION}, %q{data}, %q{data/prototype.js}, %q{data/index.html}, %q{data/example.json}, %q{json.gemspec}, %q{java}, %q{java/src}, %q{java/src/json}, %q{java/src/json/ext}, %q{java/src/json/ext/Parser.java}, %q{java/src/json/ext/RuntimeInfo.java}, %q{java/src/json/ext/GeneratorState.java}, %q{java/src/json/ext/OptionsReader.java}, %q{java/src/json/ext/ParserService.java}, %q{java/src/json/ext/Parser.rl}, %q{java/src/json/ext/StringEncoder.java}, %q{java/src/json/ext/GeneratorService.java}, %q{java/src/json/ext/Utils.java}, %q{java/src/json/ext/StringDecoder.java}, %q{java/src/json/ext/Generator.java}, %q{java/src/json/ext/ByteListTranscoder.java}, %q{java/src/json/ext/GeneratorMethods.java}, %q{java/lib}, %q{java/lib/bytelist-1.0.6.jar}, %q{java/lib/jcodings.jar}, %q{diagrams}, %q{README-json-jruby.markdown}, %q{install.rb}, %q{json-java.gemspec}, %q{tools}, %q{tools/fuzz.rb}, %q{tools/server.rb}, %q{./tests/test_json_string_matching.rb}, %q{./tests/test_json_fixtures.rb}, %q{./tests/test_json_unicode.rb}, %q{./tests/test_json_addition.rb}, %q{./tests/test_json_generate.rb}, %q{./tests/test_json_encoding.rb}, %q{./tests/test_json.rb}] s.homepage = %q{http://flori.github.com/json} s.rdoc_options = [%q{--title}, %q{JSON implemention for ruby}, %q{--main}, %q{README.rdoc}] s.require_paths = [%q{lib}] s.rubyforge_project = %q{json} - s.rubygems_version = %q{1.8.5} + s.rubygems_version = %q{1.8.6} s.summary = %q{JSON Implementation for Ruby} s.test_files = [%q{./tests/test_json_string_matching.rb}, %q{./tests/test_json_fixtures.rb}, %q{./tests/test_json_unicode.rb}, %q{./tests/test_json_addition.rb}, %q{./tests/test_json_generate.rb}, %q{./tests/test_json_encoding.rb}, %q{./tests/test_json.rb}] if s.respond_to? :specification_version then - s.specification_version = 4 + s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q<permutation>, [">= 0"]) s.add_development_dependency(%q<bullshit>, [">= 0"]) s.add_development_dependency(%q<sdoc>, [">= 0"]) s.add_development_dependency(%q<rake>, ["~> 0.9.2"]) - s.add_runtime_dependency(%q<spruz>, ["~> 0.2.8"]) else s.add_dependency(%q<permutation>, [">= 0"]) s.add_dependency(%q<bullshit>, [">= 0"]) s.add_dependency(%q<sdoc>, [">= 0"]) s.add_dependency(%q<rake>, ["~> 0.9.2"]) - s.add_dependency(%q<spruz>, ["~> 0.2.8"]) end else s.add_dependency(%q<permutation>, [">= 0"]) s.add_dependency(%q<bullshit>, [">= 0"]) s.add_dependency(%q<sdoc>, [">= 0"]) s.add_dependency(%q<rake>, ["~> 0.9.2"]) - s.add_dependency(%q<spruz>, ["~> 0.2.8"]) end end diff --git a/lib/json.rb b/lib/json.rb index d7bc1a2..00fe4ca 100644 --- a/lib/json.rb +++ b/lib/json.rb @@ -7,13 +7,13 @@ # # Built on two universally available structures: # 1. A collection of name/value pairs. Often referred to as an _object_, hash table, record, struct, keyed list, or associative array. -# 2. An orderd list of values. More commonly named as an _array_, vector, sequence, or list. +# 2. An ordered list of values. More commonly called an _array_, vector, sequence or list. # # To read more about JSON visit: http://json.org # # == Parsing JSON # -# To parse a JSON string received by another application, or generated within +# To parse a JSON string received by another application or generated within # your existing application: # # require 'json' @@ -42,8 +42,8 @@ # puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}" # # <tt>JSON.generate</tt> only allows objects or arrays to be converted -# to JSON syntax. While <tt>to_json</tt> accepts many Ruby classes -# even though it only acts a method for serialization: +# to JSON syntax. <tt>to_json</tt>, however, accepts many Ruby classes +# even though it acts only as a method for serialization: # # require 'json' # diff --git a/lib/json/Array.xpm b/lib/json/Array.xpm deleted file mode 100644 index 27c4801..0000000 --- a/lib/json/Array.xpm +++ /dev/null @@ -1,21 +0,0 @@ -/* XPM */ -static char * Array_xpm[] = { -"16 16 2 1", -" c None", -". c #000000", -" ", -" ", -" ", -" .......... ", -" . . ", -" . . ", -" . . ", -" . . ", -" . . ", -" . . ", -" . . ", -" . . ", -" .......... ", -" ", -" ", -" "}; diff --git a/lib/json/FalseClass.xpm b/lib/json/FalseClass.xpm deleted file mode 100644 index 25ce608..0000000 --- a/lib/json/FalseClass.xpm +++ /dev/null @@ -1,21 +0,0 @@ -/* XPM */ -static char * False_xpm[] = { -"16 16 2 1", -" c None", -". c #FF0000", -" ", -" ", -" ", -" ...... ", -" . ", -" . ", -" . ", -" ...... ", -" . ", -" . ", -" . ", -" . ", -" . ", -" ", -" ", -" "}; diff --git a/lib/json/Hash.xpm b/lib/json/Hash.xpm deleted file mode 100644 index cd8f6f7..0000000 --- a/lib/json/Hash.xpm +++ /dev/null @@ -1,21 +0,0 @@ -/* XPM */ -static char * Hash_xpm[] = { -"16 16 2 1", -" c None", -". c #000000", -" ", -" ", -" ", -" . . ", -" . . ", -" . . ", -" ......... ", -" . . ", -" . . ", -" ......... ", -" . . ", -" . . ", -" . . ", -" ", -" ", -" "}; diff --git a/lib/json/Key.xpm b/lib/json/Key.xpm deleted file mode 100644 index 9fd7281..0000000 --- a/lib/json/Key.xpm +++ /dev/null @@ -1,73 +0,0 @@ -/* XPM */ -static char * Key_xpm[] = { -"16 16 54 1", -" c None", -". c #110007", -"+ c #0E0900", -"@ c #000013", -"# c #070600", -"$ c #F6F006", -"% c #ECE711", -"& c #E5EE00", -"* c #16021E", -"= c #120900", -"- c #EDF12B", -"; c #000033", -"> c #0F0000", -", c #FFFE03", -"' c #E6E500", -") c #16021B", -"! c #F7F502", -"~ c #000E00", -"{ c #130000", -"] c #FFF000", -"^ c #FFE711", -"/ c #140005", -"( c #190025", -"_ c #E9DD27", -": c #E7DC04", -"< c #FFEC09", -"[ c #FFE707", -"} c #FFDE10", -"| c #150021", -"1 c #160700", -"2 c #FAF60E", -"3 c #EFE301", -"4 c #FEF300", -"5 c #E7E000", -"6 c #FFFF08", -"7 c #0E0206", -"8 c #040000", -"9 c #03052E", -"0 c #041212", -"a c #070300", -"b c #F2E713", -"c c #F9DE13", -"d c #36091E", -"e c #00001C", -"f c #1F0010", -"g c #FFF500", -"h c #DEDE00", -"i c #050A00", -"j c #FAF14A", -"k c #F5F200", -"l c #040404", -"m c #1A0D00", -"n c #EDE43D", -"o c #ECE007", -" ", -" ", -" .+@ ", -" #$%&* ", -" =-;>,') ", -" >!~{]^/ ", -" (_:<[}| ", -" 1234567 ", -" 890abcd ", -" efghi ", -" >jkl ", -" mnol ", -" >kl ", -" ll ", -" ", -" "}; diff --git a/lib/json/NilClass.xpm b/lib/json/NilClass.xpm deleted file mode 100644 index 3509f06..0000000 --- a/lib/json/NilClass.xpm +++ /dev/null @@ -1,21 +0,0 @@ -/* XPM */ -static char * False_xpm[] = { -"16 16 2 1", -" c None", -". c #000000", -" ", -" ", -" ", -" ... ", -" . . ", -" . . ", -" . . ", -" . . ", -" . . ", -" . . ", -" . . ", -" . . ", -" ... ", -" ", -" ", -" "}; diff --git a/lib/json/Numeric.xpm b/lib/json/Numeric.xpm deleted file mode 100644 index e071e2e..0000000 --- a/lib/json/Numeric.xpm +++ /dev/null @@ -1,28 +0,0 @@ -/* XPM */ -static char * Numeric_xpm[] = { -"16 16 9 1", -" c None", -". c #FF0000", -"+ c #0000FF", -"@ c #0023DB", -"# c #00EA14", -"$ c #00FF00", -"% c #004FAF", -"& c #0028D6", -"* c #00F20C", -" ", -" ", -" ", -" ... +++@#$$$$ ", -" .+ %& $$ ", -" . + $ ", -" . + $$ ", -" . ++$$$$ ", -" . + $$ ", -" . + $ ", -" . + $ ", -" . + $ $$ ", -" .....++++*$$ ", -" ", -" ", -" "}; diff --git a/lib/json/String.xpm b/lib/json/String.xpm deleted file mode 100644 index f79a89c..0000000 --- a/lib/json/String.xpm +++ /dev/null @@ -1,96 +0,0 @@ -/* XPM */ -static char * String_xpm[] = { -"16 16 77 1", -" c None", -". c #000000", -"+ c #040404", -"@ c #080806", -"# c #090606", -"$ c #EEEAE1", -"% c #E7E3DA", -"& c #E0DBD1", -"* c #D4B46F", -"= c #0C0906", -"- c #E3C072", -"; c #E4C072", -"> c #060505", -", c #0B0A08", -"' c #D5B264", -") c #D3AF5A", -"! c #080602", -"~ c #E1B863", -"{ c #DDB151", -"] c #DBAE4A", -"^ c #DDB152", -"/ c #DDB252", -"( c #070705", -"_ c #0C0A07", -": c #D3A33B", -"< c #020201", -"[ c #DAAA41", -"} c #040302", -"| c #E4D9BF", -"1 c #0B0907", -"2 c #030201", -"3 c #020200", -"4 c #C99115", -"5 c #080704", -"6 c #DBC8A2", -"7 c #E7D7B4", -"8 c #E0CD9E", -"9 c #080601", -"0 c #040400", -"a c #010100", -"b c #0B0B08", -"c c #DCBF83", -"d c #DCBC75", -"e c #DEB559", -"f c #040301", -"g c #BC8815", -"h c #120E07", -"i c #060402", -"j c #0A0804", -"k c #D4A747", -"l c #D6A12F", -"m c #0E0C05", -"n c #C8C1B0", -"o c #1D1B15", -"p c #D7AD51", -"q c #070502", -"r c #080804", -"s c #BC953B", -"t c #C4BDAD", -"u c #0B0807", -"v c #DBAC47", -"w c #1B150A", -"x c #B78A2C", -"y c #D8A83C", -"z c #D4A338", -"A c #0F0B03", -"B c #181105", -"C c #C59325", -"D c #C18E1F", -"E c #060600", -"F c #CC992D", -"G c #B98B25", -"H c #B3831F", -"I c #C08C1C", -"J c #060500", -"K c #0E0C03", -"L c #0D0A00", -" ", -" .+@# ", -" .$%&*= ", -" .-;>,')! ", -" .~. .{]. ", -" .^/. (_:< ", -" .[.}|$12 ", -" 345678}90 ", -" a2bcdefgh ", -" ijkl.mno ", -" <pq. rstu ", -" .]v. wx= ", -" .yzABCDE ", -" .FGHIJ ", -" 0KL0 ", -" "}; diff --git a/lib/json/TrueClass.xpm b/lib/json/TrueClass.xpm deleted file mode 100644 index 143eef4..0000000 --- a/lib/json/TrueClass.xpm +++ /dev/null @@ -1,21 +0,0 @@ -/* XPM */ -static char * TrueClass_xpm[] = { -"16 16 2 1", -" c None", -". c #0BF311", -" ", -" ", -" ", -" ......... ", -" . ", -" . ", -" . ", -" . ", -" . ", -" . ", -" . ", -" . ", -" . ", -" ", -" ", -" "}; diff --git a/lib/json/add/complex.rb b/lib/json/add/complex.rb new file mode 100644 index 0000000..d7ebebf --- /dev/null +++ b/lib/json/add/complex.rb @@ -0,0 +1,22 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +defined?(::Complex) or require 'complex' + +class Complex + def self.json_create(object) + Complex(object['r'], object['i']) + end + + def as_json(*) + { + JSON.create_id => self.class.name, + 'r' => real, + 'i' => imag, + } + end + + def to_json(*) + as_json.to_json + end +end diff --git a/lib/json/add/core.rb b/lib/json/add/core.rb index e9850af..77d9dc0 100644 --- a/lib/json/add/core.rb +++ b/lib/json/add/core.rb @@ -1,243 +1,11 @@ -# This file contains implementations of ruby core's custom objects for +# This file requires the implementations of ruby core's custom objects for # serialisation/deserialisation. -unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED - require 'json' -end -require 'date' - -# Symbol serialization/deserialization -class Symbol - # Returns a hash, that will be turned into a JSON object and represent this - # object. - def as_json(*) - { - JSON.create_id => self.class.name, - 's' => to_s, - } - end - - # Stores class name (Symbol) with String representation of Symbol as a JSON string. - def to_json(*a) - as_json.to_json(*a) - end - - # Deserializes JSON string by converting the <tt>string</tt> value stored in the object to a Symbol - def self.json_create(o) - o['s'].to_sym - end -end - -# Time serialization/deserialization -class Time - - # Deserializes JSON string by converting time since epoch to Time - def self.json_create(object) - if usec = object.delete('u') # used to be tv_usec -> tv_nsec - object['n'] = usec * 1000 - end - if respond_to?(:tv_nsec) - at(*object.values_at('s', 'n')) - else - at(object['s'], object['n'] / 1000) - end - end - - # Returns a hash, that will be turned into a JSON object and represent this - # object. - def as_json(*) - { - JSON.create_id => self.class.name, - 's' => tv_sec, - 'n' => respond_to?(:tv_nsec) ? tv_nsec : tv_usec * 1000 - } - end - - # Stores class name (Time) with number of seconds since epoch and number of - # microseconds for Time as JSON string - def to_json(*args) - as_json.to_json(*args) - end -end - -# Date serialization/deserialization -class Date - - # Deserializes JSON string by converting Julian year <tt>y</tt>, month - # <tt>m</tt>, day <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> to Date. - def self.json_create(object) - civil(*object.values_at('y', 'm', 'd', 'sg')) - end - - alias start sg unless method_defined?(:start) - - # Returns a hash, that will be turned into a JSON object and represent this - # object. - def as_json(*) - { - JSON.create_id => self.class.name, - 'y' => year, - 'm' => month, - 'd' => day, - 'sg' => start, - } - end - - # Stores class name (Date) with Julian year <tt>y</tt>, month <tt>m</tt>, day - # <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string - def to_json(*args) - as_json.to_json(*args) - end -end - -# DateTime serialization/deserialization -class DateTime - - # Deserializes JSON string by converting year <tt>y</tt>, month <tt>m</tt>, - # day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>, - # offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> to DateTime. - def self.json_create(object) - args = object.values_at('y', 'm', 'd', 'H', 'M', 'S') - of_a, of_b = object['of'].split('/') - if of_b and of_b != '0' - args << Rational(of_a.to_i, of_b.to_i) - else - args << of_a - end - args << object['sg'] - civil(*args) - end - - alias start sg unless method_defined?(:start) - - # Returns a hash, that will be turned into a JSON object and represent this - # object. - def as_json(*) - { - JSON.create_id => self.class.name, - 'y' => year, - 'm' => month, - 'd' => day, - 'H' => hour, - 'M' => min, - 'S' => sec, - 'of' => offset.to_s, - 'sg' => start, - } - end - - # Stores class name (DateTime) with Julian year <tt>y</tt>, month <tt>m</tt>, - # day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>, - # offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string - def to_json(*args) - as_json.to_json(*args) - end -end - -# Range serialization/deserialization -class Range - - # Deserializes JSON string by constructing new Range object with arguments - # <tt>a</tt> serialized by <tt>to_json</tt>. - def self.json_create(object) - new(*object['a']) - end - - # Returns a hash, that will be turned into a JSON object and represent this - # object. - def as_json(*) - { - JSON.create_id => self.class.name, - 'a' => [ first, last, exclude_end? ] - } - end - - # Stores class name (Range) with JSON array of arguments <tt>a</tt> which - # include <tt>first</tt> (integer), <tt>last</tt> (integer), and - # <tt>exclude_end?</tt> (boolean) as JSON string. - def to_json(*args) - as_json.to_json(*args) - end -end - -# Struct serialization/deserialization -class Struct - - # Deserializes JSON string by constructing new Struct object with values - # <tt>v</tt> serialized by <tt>to_json</tt>. - def self.json_create(object) - new(*object['v']) - end - - # Returns a hash, that will be turned into a JSON object and represent this - # object. - def as_json(*) - klass = self.class.name - klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!" - { - JSON.create_id => klass, - 'v' => values, - } - end - - # Stores class name (Struct) with Struct values <tt>v</tt> as a JSON string. - # Only named structs are supported. - def to_json(*args) - as_json.to_json(*args) - end -end - -# Exception serialization/deserialization -class Exception - - # Deserializes JSON string by constructing new Exception object with message - # <tt>m</tt> and backtrace <tt>b</tt> serialized with <tt>to_json</tt> - def self.json_create(object) - result = new(object['m']) - result.set_backtrace object['b'] - result - end - - # Returns a hash, that will be turned into a JSON object and represent this - # object. - def as_json(*) - { - JSON.create_id => self.class.name, - 'm' => message, - 'b' => backtrace, - } - end - - # Stores class name (Exception) with message <tt>m</tt> and backtrace array - # <tt>b</tt> as JSON string - def to_json(*args) - as_json.to_json(*args) - end -end - -# Regexp serialization/deserialization -class Regexp - - # Deserializes JSON string by constructing new Regexp object with source - # <tt>s</tt> (Regexp or String) and options <tt>o</tt> serialized by - # <tt>to_json</tt> - def self.json_create(object) - new(object['s'], object['o']) - end - - # Returns a hash, that will be turned into a JSON object and represent this - # object. - def as_json(*) - { - JSON.create_id => self.class.name, - 'o' => options, - 's' => source, - } - end - - # Stores class name (Regexp) with options <tt>o</tt> and source <tt>s</tt> - # (Regexp or String) as JSON string - def to_json(*) - as_json.to_json - end -end +require 'json/add/date' +require 'json/add/date_time' +require 'json/add/exception' +require 'json/add/range' +require 'json/add/regexp' +require 'json/add/struct' +require 'json/add/symbol' +require 'json/add/time' diff --git a/lib/json/add/date.rb b/lib/json/add/date.rb new file mode 100644 index 0000000..4288237 --- /dev/null +++ b/lib/json/add/date.rb @@ -0,0 +1,34 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +require 'date' + +# Date serialization/deserialization +class Date + + # Deserializes JSON string by converting Julian year <tt>y</tt>, month + # <tt>m</tt>, day <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> to Date. + def self.json_create(object) + civil(*object.values_at('y', 'm', 'd', 'sg')) + end + + alias start sg unless method_defined?(:start) + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'y' => year, + 'm' => month, + 'd' => day, + 'sg' => start, + } + end + + # Stores class name (Date) with Julian year <tt>y</tt>, month <tt>m</tt>, day + # <tt>d</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/lib/json/add/date_time.rb b/lib/json/add/date_time.rb new file mode 100644 index 0000000..5ea42ea --- /dev/null +++ b/lib/json/add/date_time.rb @@ -0,0 +1,50 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +require 'date' + +# DateTime serialization/deserialization +class DateTime + + # Deserializes JSON string by converting year <tt>y</tt>, month <tt>m</tt>, + # day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>, + # offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> to DateTime. + def self.json_create(object) + args = object.values_at('y', 'm', 'd', 'H', 'M', 'S') + of_a, of_b = object['of'].split('/') + if of_b and of_b != '0' + args << Rational(of_a.to_i, of_b.to_i) + else + args << of_a + end + args << object['sg'] + civil(*args) + end + + alias start sg unless method_defined?(:start) + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'y' => year, + 'm' => month, + 'd' => day, + 'H' => hour, + 'M' => min, + 'S' => sec, + 'of' => offset.to_s, + 'sg' => start, + } + end + + # Stores class name (DateTime) with Julian year <tt>y</tt>, month <tt>m</tt>, + # day <tt>d</tt>, hour <tt>H</tt>, minute <tt>M</tt>, second <tt>S</tt>, + # offset <tt>of</tt> and Day of Calendar Reform <tt>sg</tt> as JSON string + def to_json(*args) + as_json.to_json(*args) + end +end + + diff --git a/lib/json/add/exception.rb b/lib/json/add/exception.rb new file mode 100644 index 0000000..e6ad257 --- /dev/null +++ b/lib/json/add/exception.rb @@ -0,0 +1,31 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Exception serialization/deserialization +class Exception + + # Deserializes JSON string by constructing new Exception object with message + # <tt>m</tt> and backtrace <tt>b</tt> serialized with <tt>to_json</tt> + def self.json_create(object) + result = new(object['m']) + result.set_backtrace object['b'] + result + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'm' => message, + 'b' => backtrace, + } + end + + # Stores class name (Exception) with message <tt>m</tt> and backtrace array + # <tt>b</tt> as JSON string + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/lib/json/add/rails.rb b/lib/json/add/rails.rb deleted file mode 100644 index af95e52..0000000 --- a/lib/json/add/rails.rb +++ /dev/null @@ -1,8 +0,0 @@ -# This file used to implementations of rails custom objects for -# serialisation/deserialisation and is obsoleted now. - -unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED - require 'json' -end - -$DEBUG and warn "required json/add/rails which is obsolete now!" diff --git a/lib/json/add/range.rb b/lib/json/add/range.rb new file mode 100644 index 0000000..e61e553 --- /dev/null +++ b/lib/json/add/range.rb @@ -0,0 +1,29 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Range serialization/deserialization +class Range + + # Deserializes JSON string by constructing new Range object with arguments + # <tt>a</tt> serialized by <tt>to_json</tt>. + def self.json_create(object) + new(*object['a']) + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'a' => [ first, last, exclude_end? ] + } + end + + # Stores class name (Range) with JSON array of arguments <tt>a</tt> which + # include <tt>first</tt> (integer), <tt>last</tt> (integer), and + # <tt>exclude_end?</tt> (boolean) as JSON string. + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/lib/json/add/rational.rb b/lib/json/add/rational.rb new file mode 100644 index 0000000..867cd92 --- /dev/null +++ b/lib/json/add/rational.rb @@ -0,0 +1,22 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +defined?(::Rational) or require 'rational' + +class Rational + def self.json_create(object) + Rational(object['n'], object['d']) + end + + def as_json(*) + { + JSON.create_id => self.class.name, + 'n' => numerator, + 'd' => denominator, + } + end + + def to_json(*) + as_json.to_json + end +end diff --git a/lib/json/add/regexp.rb b/lib/json/add/regexp.rb new file mode 100644 index 0000000..2fcbb6f --- /dev/null +++ b/lib/json/add/regexp.rb @@ -0,0 +1,30 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Regexp serialization/deserialization +class Regexp + + # Deserializes JSON string by constructing new Regexp object with source + # <tt>s</tt> (Regexp or String) and options <tt>o</tt> serialized by + # <tt>to_json</tt> + def self.json_create(object) + new(object['s'], object['o']) + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'o' => options, + 's' => source, + } + end + + # Stores class name (Regexp) with options <tt>o</tt> and source <tt>s</tt> + # (Regexp or String) as JSON string + def to_json(*) + as_json.to_json + end +end diff --git a/lib/json/add/struct.rb b/lib/json/add/struct.rb new file mode 100644 index 0000000..6847cde --- /dev/null +++ b/lib/json/add/struct.rb @@ -0,0 +1,30 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Struct serialization/deserialization +class Struct + + # Deserializes JSON string by constructing new Struct object with values + # <tt>v</tt> serialized by <tt>to_json</tt>. + def self.json_create(object) + new(*object['v']) + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + klass = self.class.name + klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!" + { + JSON.create_id => klass, + 'v' => values, + } + end + + # Stores class name (Struct) with Struct values <tt>v</tt> as a JSON string. + # Only named structs are supported. + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/lib/json/add/symbol.rb b/lib/json/add/symbol.rb new file mode 100644 index 0000000..03dc9a5 --- /dev/null +++ b/lib/json/add/symbol.rb @@ -0,0 +1,25 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Symbol serialization/deserialization +class Symbol + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 's' => to_s, + } + end + + # Stores class name (Symbol) with String representation of Symbol as a JSON string. + def to_json(*a) + as_json.to_json(*a) + end + + # Deserializes JSON string by converting the <tt>string</tt> value stored in the object to a Symbol + def self.json_create(o) + o['s'].to_sym + end +end diff --git a/lib/json/add/time.rb b/lib/json/add/time.rb new file mode 100644 index 0000000..abc807a --- /dev/null +++ b/lib/json/add/time.rb @@ -0,0 +1,35 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Time serialization/deserialization +class Time + + # Deserializes JSON string by converting time since epoch to Time + def self.json_create(object) + if usec = object.delete('u') # used to be tv_usec -> tv_nsec + object['n'] = usec * 1000 + end + if respond_to?(:tv_nsec) + at(*object.values_at('s', 'n')) + else + at(object['s'], object['n'] / 1000) + end + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 's' => tv_sec, + 'n' => respond_to?(:tv_nsec) ? tv_nsec : tv_usec * 1000 + } + end + + # Stores class name (Time) with number of seconds since epoch and number of + # microseconds for Time as JSON string + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/lib/json/common.rb b/lib/json/common.rb index 1a5f048..43e249c 100644 --- a/lib/json/common.rb +++ b/lib/json/common.rb @@ -2,11 +2,11 @@ require 'json/version' module JSON class << self - # If _object_ is string-like parse the string and return the parsed result + # If _object_ is string-like, parse the string and return the parsed result # as a Ruby data structure. Otherwise generate a JSON text from the Ruby # data structure object and return it. # - # The _opts_ argument is passed through to generate/parse respectively, see + # The _opts_ argument is passed through to generate/parse respectively. See # generate and parse for their documentation. def [](object, opts = {}) if object.respond_to? :to_str @@ -16,7 +16,7 @@ module JSON end end - # Returns the JSON parser class, that is used by JSON. This might be either + # Returns the JSON parser class that is used by JSON. This is either # JSON::Ext::Parser or JSON::Pure::Parser. attr_reader :parser @@ -28,7 +28,7 @@ module JSON end # Return the constant located at _path_. The format of _path_ has to be - # either ::A::B::C or A::B::C. In any case A has to be located at the top + # either ::A::B::C or A::B::C. In any case, A has to be located at the top # level (absolute namespace path?). If there doesn't exist a constant at # the given path, an ArgumentError is raised. def deep_const_get(path) # :nodoc: @@ -81,15 +81,15 @@ module JSON $VERBOSE = old end - # Returns the JSON generator modul, that is used by JSON. This might be + # Returns the JSON generator module that is used by JSON. This is # either JSON::Ext::Generator or JSON::Pure::Generator. attr_reader :generator - # Returns the JSON generator state class, that is used by JSON. This might - # be either JSON::Ext::Generator::State or JSON::Pure::Generator::State. + # Returns the JSON generator state class that is used by JSON. This is + # either JSON::Ext::Generator::State or JSON::Pure::Generator::State. attr_accessor :state - # This is create identifier, that is used to decide, if the _json_create_ + # This is create identifier, which is used to decide if the _json_create_ # hook of a class should be called. It defaults to 'json_class'. attr_accessor :create_id end @@ -104,10 +104,10 @@ module JSON # The base exception for JSON errors. class JSONError < StandardError; end - # This exception is raised, if a parser error occurs. + # This exception is raised if a parser error occurs. class ParserError < JSONError; end - # This exception is raised, if the nesting of parsed datastructures is too + # This exception is raised if the nesting of parsed data structures is too # deep. class NestingError < ParserError; end @@ -115,13 +115,13 @@ module JSON class CircularDatastructure < NestingError; end # :startdoc: - # This exception is raised, if a generator or unparser error occurs. + # This exception is raised if a generator or unparser error occurs. class GeneratorError < JSONError; end # For backwards compatibility UnparserError = GeneratorError - # This exception is raised, if the required unicode support is missing on the - # system. Usually this means, that the iconv library is not installed. + # This exception is raised if the required unicode support is missing on the + # system. Usually this means that the iconv library is not installed. class MissingUnicodeSupport < JSONError; end module_function @@ -131,16 +131,16 @@ module JSON # _opts_ can have the following # keys: # * *max_nesting*: The maximum depth of nesting allowed in the parsed data - # structures. Disable depth checking with :max_nesting => false, it defaults + # structures. Disable depth checking with :max_nesting => false. It defaults # to 19. # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in # defiance of RFC 4627 to be parsed by the Parser. This option defaults # to false. # * *symbolize_names*: If set to true, returns symbols for the names - # (keys) in a JSON object. Otherwise strings are returned, which is also + # (keys) in a JSON object. Otherwise strings are returned. Strings are # the default. # * *create_additions*: If set to false, the Parser doesn't create - # additions even if a matchin class and create_id was found. This option + # additions even if a matching class and create_id was found. This option # defaults to true. # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array @@ -149,19 +149,19 @@ module JSON end # Parse the JSON document _source_ into a Ruby data structure and return it. - # The bang version of the parse method, defaults to the more dangerous values + # The bang version of the parse method defaults to the more dangerous values # for the _opts_ hash, so be sure only to parse trusted _source_ documents. # # _opts_ can have the following keys: # * *max_nesting*: The maximum depth of nesting allowed in the parsed data # structures. Enable depth checking with :max_nesting => anInteger. The parse! - # methods defaults to not doing max depth checking: This can be dangerous, + # methods defaults to not doing max depth checking: This can be dangerous # if someone wants to fill up your stack. # * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in # defiance of RFC 4627 to be parsed by the Parser. This option defaults # to true. # * *create_additions*: If set to false, the Parser doesn't create - # additions even if a matchin class and create_id was found. This option + # additions even if a matching class and create_id was found. This option # defaults to true. def parse!(source, opts = {}) opts = { @@ -188,7 +188,7 @@ module JSON # * *object_nl*: a string that is put at the end of a JSON object (default: ''), # * *array_nl*: a string that is put at the end of a JSON array (default: ''), # * *allow_nan*: true if NaN, Infinity, and -Infinity should be - # generated, otherwise an exception is thrown, if these values are + # generated, otherwise an exception is thrown if these values are # encountered. This options defaults to false. # * *max_nesting*: The maximum depth of nesting allowed in the data # structures from which JSON is to be generated. Disable depth checking @@ -196,9 +196,13 @@ module JSON # # See also the fast_generate for the fastest creation method with the least # amount of sanity checks, and the pretty_generate method for some - # defaults for a pretty output. + # defaults for pretty output. def generate(obj, opts = nil) - state = SAFE_STATE_PROTOTYPE.dup + if State === opts + state, opts = opts, nil + else + state = SAFE_STATE_PROTOTYPE.dup + end if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -223,9 +227,13 @@ module JSON # This method disables the checks for circles in Ruby objects. # # *WARNING*: Be careful not to pass any Ruby data structures with circles as - # _obj_ argument, because this will cause JSON to go into an infinite loop. + # _obj_ argument because this will cause JSON to go into an infinite loop. def fast_generate(obj, opts = nil) - state = FAST_STATE_PROTOTYPE.dup + if State === opts + state, opts = opts, nil + else + state = FAST_STATE_PROTOTYPE.dup + end if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -249,10 +257,14 @@ module JSON # The returned document is a prettier form of the document returned by # #unparse. # - # The _opts_ argument can be used to configure the generator, see the + # The _opts_ argument can be used to configure the generator. See the # generate method for a more detailed explanation. def pretty_generate(obj, opts = nil) - state = PRETTY_STATE_PROTOTYPE.dup + if State === opts + state, opts = opts, nil + else + state = PRETTY_STATE_PROTOTYPE.dup + end if opts if opts.respond_to? :to_hash opts = opts.to_hash @@ -273,7 +285,7 @@ module JSON # :startdoc: # Load a ruby data structure from a JSON _source_ and return it. A source can - # either be a string-like object, an IO like object, or an object responding + # either be a string-like object, an IO-like object, or an object responding # to the read method. If _proc_ was given, it will be called with any nested # Ruby object as an argument recursively in depth first order. # @@ -312,10 +324,10 @@ module JSON # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns # the result. # - # If anIO (an IO like object or an object that responds to the write method) + # If anIO (an IO-like object or an object that responds to the write method) # was given, the resulting JSON is written to it. # - # If the number of nested arrays or objects exceeds _limit_ an ArgumentError + # If the number of nested arrays or objects exceeds _limit_, an ArgumentError # exception is raised. This argument is similar (but not exactly the # same!) to the _limit_ argument in Marshal.dump. # @@ -396,11 +408,11 @@ module ::Kernel nil end - # If _object_ is string-like parse the string and return the parsed result as - # a Ruby data structure. Otherwise generate a JSON text from the Ruby data + # If _object_ is string-like, parse the string and return the parsed result as + # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data # structure object and return it. # - # The _opts_ argument is passed through to generate/parse respectively, see + # The _opts_ argument is passed through to generate/parse respectively. See # generate and parse for their documentation. def JSON(object, *args) if object.respond_to? :to_str @@ -413,10 +425,10 @@ end # Extends any Class to include _json_creatable?_ method. class ::Class - # Returns true, if this class can be used to create an instance + # Returns true if this class can be used to create an instance # from a serialised JSON string. The class has to implement a class - # method _json_create_ that expects a hash as first parameter, which includes - # the required data. + # method _json_create_ that expects a hash as first parameter. The hash + # should include the required data. def json_creatable? respond_to?(:json_create) end diff --git a/lib/json/editor.rb b/lib/json/editor.rb deleted file mode 100644 index 985a554..0000000 --- a/lib/json/editor.rb +++ /dev/null @@ -1,1369 +0,0 @@ -# To use the GUI JSON editor, start the edit_json.rb executable script. It -# requires ruby-gtk to be installed. - -require 'gtk2' -require 'json' -require 'rbconfig' -require 'open-uri' - -module JSON - module Editor - include Gtk - - # Beginning of the editor window title - TITLE = 'JSON Editor'.freeze - - # Columns constants - ICON_COL, TYPE_COL, CONTENT_COL = 0, 1, 2 - - # JSON primitive types (Containers) - CONTAINER_TYPES = %w[Array Hash].sort - # All JSON primitive types - ALL_TYPES = (%w[TrueClass FalseClass Numeric String NilClass] + - CONTAINER_TYPES).sort - - # The Nodes necessary for the tree representation of a JSON document - ALL_NODES = (ALL_TYPES + %w[Key]).sort - - DEFAULT_DIALOG_KEY_PRESS_HANDLER = lambda do |dialog, event| - case event.keyval - when Gdk::Keyval::GDK_Return - dialog.response Dialog::RESPONSE_ACCEPT - when Gdk::Keyval::GDK_Escape - dialog.response Dialog::RESPONSE_REJECT - end - end - - # Returns the Gdk::Pixbuf of the icon named _name_ from the icon cache. - def Editor.fetch_icon(name) - @icon_cache ||= {} - unless @icon_cache.key?(name) - path = File.dirname(__FILE__) - @icon_cache[name] = Gdk::Pixbuf.new(File.join(path, name + '.xpm')) - end - @icon_cache[name] - end - - # Opens an error dialog on top of _window_ showing the error message - # _text_. - def Editor.error_dialog(window, text) - dialog = MessageDialog.new(window, Dialog::MODAL, - MessageDialog::ERROR, - MessageDialog::BUTTONS_CLOSE, text) - dialog.show_all - dialog.run - rescue TypeError - dialog = MessageDialog.new(Editor.window, Dialog::MODAL, - MessageDialog::ERROR, - MessageDialog::BUTTONS_CLOSE, text) - dialog.show_all - dialog.run - ensure - dialog.destroy if dialog - end - - # Opens a yes/no question dialog on top of _window_ showing the error - # message _text_. If yes was answered _true_ is returned, otherwise - # _false_. - def Editor.question_dialog(window, text) - dialog = MessageDialog.new(window, Dialog::MODAL, - MessageDialog::QUESTION, - MessageDialog::BUTTONS_YES_NO, text) - dialog.show_all - dialog.run do |response| - return Gtk::Dialog::RESPONSE_YES === response - end - ensure - dialog.destroy if dialog - end - - # Convert the tree model starting from Gtk::TreeIter _iter_ into a Ruby - # data structure and return it. - def Editor.model2data(iter) - return nil if iter.nil? - case iter.type - when 'Hash' - hash = {} - iter.each { |c| hash[c.content] = Editor.model2data(c.first_child) } - hash - when 'Array' - array = Array.new(iter.n_children) - iter.each_with_index { |c, i| array[i] = Editor.model2data(c) } - array - when 'Key' - iter.content - when 'String' - iter.content - when 'Numeric' - content = iter.content - if /\./.match(content) - content.to_f - else - content.to_i - end - when 'TrueClass' - true - when 'FalseClass' - false - when 'NilClass' - nil - else - fail "Unknown type found in model: #{iter.type}" - end - end - - # Convert the Ruby data structure _data_ into tree model data for Gtk and - # returns the whole model. If the parameter _model_ wasn't given a new - # Gtk::TreeStore is created as the model. The _parent_ parameter specifies - # the parent node (iter, Gtk:TreeIter instance) to which the data is - # appended, alternativeley the result of the yielded block is used as iter. - def Editor.data2model(data, model = nil, parent = nil) - model ||= TreeStore.new(Gdk::Pixbuf, String, String) - iter = if block_given? - yield model - else - model.append(parent) - end - case data - when Hash - iter.type = 'Hash' - data.sort.each do |key, value| - pair_iter = model.append(iter) - pair_iter.type = 'Key' - pair_iter.content = key.to_s - Editor.data2model(value, model, pair_iter) - end - when Array - iter.type = 'Array' - data.each do |value| - Editor.data2model(value, model, iter) - end - when Numeric - iter.type = 'Numeric' - iter.content = data.to_s - when String, true, false, nil - iter.type = data.class.name - iter.content = data.nil? ? 'null' : data.to_s - else - iter.type = 'String' - iter.content = data.to_s - end - model - end - - # The Gtk::TreeIter class is reopened and some auxiliary methods are added. - class Gtk::TreeIter - include Enumerable - - # Traverse each of this Gtk::TreeIter instance's children - # and yield to them. - def each - n_children.times { |i| yield nth_child(i) } - end - - # Recursively traverse all nodes of this Gtk::TreeIter's subtree - # (including self) and yield to them. - def recursive_each(&block) - yield self - each do |i| - i.recursive_each(&block) - end - end - - # Remove the subtree of this Gtk::TreeIter instance from the - # model _model_. - def remove_subtree(model) - while current = first_child - model.remove(current) - end - end - - # Returns the type of this node. - def type - self[TYPE_COL] - end - - # Sets the type of this node to _value_. This implies setting - # the respective icon accordingly. - def type=(value) - self[TYPE_COL] = value - self[ICON_COL] = Editor.fetch_icon(value) - end - - # Returns the content of this node. - def content - self[CONTENT_COL] - end - - # Sets the content of this node to _value_. - def content=(value) - self[CONTENT_COL] = value - end - end - - # This module bundles some method, that can be used to create a menu. It - # should be included into the class in question. - module MenuExtension - include Gtk - - # Creates a Menu, that includes MenuExtension. _treeview_ is the - # Gtk::TreeView, on which it operates. - def initialize(treeview) - @treeview = treeview - @menu = Menu.new - end - - # Returns the Gtk::TreeView of this menu. - attr_reader :treeview - - # Returns the menu. - attr_reader :menu - - # Adds a Gtk::SeparatorMenuItem to this instance's #menu. - def add_separator - menu.append SeparatorMenuItem.new - end - - # Adds a Gtk::MenuItem to this instance's #menu. _label_ is the label - # string, _klass_ is the item type, and _callback_ is the procedure, that - # is called if the _item_ is activated. - def add_item(label, keyval = nil, klass = MenuItem, &callback) - label = "#{label} (C-#{keyval.chr})" if keyval - item = klass.new(label) - item.signal_connect(:activate, &callback) - if keyval - self.signal_connect(:'key-press-event') do |item, event| - if event.state & Gdk::Window::ModifierType::CONTROL_MASK != 0 and - event.keyval == keyval - callback.call item - end - end - end - menu.append item - item - end - - # This method should be implemented in subclasses to create the #menu of - # this instance. It has to be called after an instance of this class is - # created, to build the menu. - def create - raise NotImplementedError - end - - def method_missing(*a, &b) - treeview.__send__(*a, &b) - end - end - - # This class creates the popup menu, that opens when clicking onto the - # treeview. - class PopUpMenu - include MenuExtension - - # Change the type or content of the selected node. - def change_node(item) - if current = selection.selected - parent = current.parent - old_type, old_content = current.type, current.content - if ALL_TYPES.include?(old_type) - @clipboard_data = Editor.model2data(current) - type, content = ask_for_element(parent, current.type, - current.content) - if type - current.type, current.content = type, content - current.remove_subtree(model) - toplevel.display_status("Changed a node in tree.") - window.change - end - else - toplevel.display_status( - "Cannot change node of type #{old_type} in tree!") - end - end - end - - # Cut the selected node and its subtree, and save it into the - # clipboard. - def cut_node(item) - if current = selection.selected - if current and current.type == 'Key' - @clipboard_data = { - current.content => Editor.model2data(current.first_child) - } - else - @clipboard_data = Editor.model2data(current) - end - model.remove(current) - window.change - toplevel.display_status("Cut a node from tree.") - end - end - - # Copy the selected node and its subtree, and save it into the - # clipboard. - def copy_node(item) - if current = selection.selected - if current and current.type == 'Key' - @clipboard_data = { - current.content => Editor.model2data(current.first_child) - } - else - @clipboard_data = Editor.model2data(current) - end - window.change - toplevel.display_status("Copied a node from tree.") - end - end - - # Paste the data in the clipboard into the selected Array or Hash by - # appending it. - def paste_node_appending(item) - if current = selection.selected - if @clipboard_data - case current.type - when 'Array' - Editor.data2model(@clipboard_data, model, current) - expand_collapse(current) - when 'Hash' - if @clipboard_data.is_a? Hash - parent = current.parent - hash = Editor.model2data(current) - model.remove(current) - hash.update(@clipboard_data) - Editor.data2model(hash, model, parent) - if parent - expand_collapse(parent) - elsif @expanded - expand_all - end - window.change - else - toplevel.display_status( - "Cannot paste non-#{current.type} data into '#{current.type}'!") - end - else - toplevel.display_status( - "Cannot paste node below '#{current.type}'!") - end - else - toplevel.display_status("Nothing to paste in clipboard!") - end - else - toplevel.display_status("Append a node into the root first!") - end - end - - # Paste the data in the clipboard into the selected Array inserting it - # before the selected element. - def paste_node_inserting_before(item) - if current = selection.selected - if @clipboard_data - parent = current.parent or return - parent_type = parent.type - if parent_type == 'Array' - selected_index = parent.each_with_index do |c, i| - break i if c == current - end - Editor.data2model(@clipboard_data, model, parent) do |m| - m.insert_before(parent, current) - end - expand_collapse(current) - toplevel.display_status("Inserted an element to " + - "'#{parent_type}' before index #{selected_index}.") - window.change - else - toplevel.display_status( - "Cannot insert node below '#{parent_type}'!") - end - else - toplevel.display_status("Nothing to paste in clipboard!") - end - else - toplevel.display_status("Append a node into the root first!") - end - end - - # Append a new node to the selected Hash or Array. - def append_new_node(item) - if parent = selection.selected - parent_type = parent.type - case parent_type - when 'Hash' - key, type, content = ask_for_hash_pair(parent) - key or return - iter = create_node(parent, 'Key', key) - iter = create_node(iter, type, content) - toplevel.display_status( - "Added a (key, value)-pair to '#{parent_type}'.") - window.change - when 'Array' - type, content = ask_for_element(parent) - type or return - iter = create_node(parent, type, content) - window.change - toplevel.display_status("Appendend an element to '#{parent_type}'.") - else - toplevel.display_status("Cannot append to '#{parent_type}'!") - end - else - type, content = ask_for_element - type or return - iter = create_node(nil, type, content) - window.change - end - end - - # Insert a new node into an Array before the selected element. - def insert_new_node(item) - if current = selection.selected - parent = current.parent or return - parent_parent = parent.parent - parent_type = parent.type - if parent_type == 'Array' - selected_index = parent.each_with_index do |c, i| - break i if c == current - end - type, content = ask_for_element(parent) - type or return - iter = model.insert_before(parent, current) - iter.type, iter.content = type, content - toplevel.display_status("Inserted an element to " + - "'#{parent_type}' before index #{selected_index}.") - window.change - else - toplevel.display_status( - "Cannot insert node below '#{parent_type}'!") - end - else - toplevel.display_status("Append a node into the root first!") - end - end - - # Recursively collapse/expand a subtree starting from the selected node. - def collapse_expand(item) - if current = selection.selected - if row_expanded?(current.path) - collapse_row(current.path) - else - expand_row(current.path, true) - end - else - toplevel.display_status("Append a node into the root first!") - end - end - - # Create the menu. - def create - add_item("Change node", ?n, &method(:change_node)) - add_separator - add_item("Cut node", ?X, &method(:cut_node)) - add_item("Copy node", ?C, &method(:copy_node)) - add_item("Paste node (appending)", ?A, &method(:paste_node_appending)) - add_item("Paste node (inserting before)", ?I, - &method(:paste_node_inserting_before)) - add_separator - add_item("Append new node", ?a, &method(:append_new_node)) - add_item("Insert new node before", ?i, &method(:insert_new_node)) - add_separator - add_item("Collapse/Expand node (recursively)", ?e, - &method(:collapse_expand)) - - menu.show_all - signal_connect(:button_press_event) do |widget, event| - if event.kind_of? Gdk::EventButton and event.button == 3 - menu.popup(nil, nil, event.button, event.time) - end - end - signal_connect(:popup_menu) do - menu.popup(nil, nil, 0, Gdk::Event::CURRENT_TIME) - end - end - end - - # This class creates the File pulldown menu. - class FileMenu - include MenuExtension - - # Clear the model and filename, but ask to save the JSON document, if - # unsaved changes have occured. - def new(item) - window.clear - end - - # Open a file and load it into the editor. Ask to save the JSON document - # first, if unsaved changes have occured. - def open(item) - window.file_open - end - - def open_location(item) - window.location_open - end - - # Revert the current JSON document in the editor to the saved version. - def revert(item) - window.instance_eval do - @filename and file_open(@filename) - end - end - - # Save the current JSON document. - def save(item) - window.file_save - end - - # Save the current JSON document under the given filename. - def save_as(item) - window.file_save_as - end - - # Quit the editor, after asking to save any unsaved changes first. - def quit(item) - window.quit - end - - # Create the menu. - def create - title = MenuItem.new('File') - title.submenu = menu - add_item('New', &method(:new)) - add_item('Open', ?o, &method(:open)) - add_item('Open location', ?l, &method(:open_location)) - add_item('Revert', &method(:revert)) - add_separator - add_item('Save', ?s, &method(:save)) - add_item('Save As', ?S, &method(:save_as)) - add_separator - add_item('Quit', ?q, &method(:quit)) - title - end - end - - # This class creates the Edit pulldown menu. - class EditMenu - include MenuExtension - - # Copy data from model into primary clipboard. - def copy(item) - data = Editor.model2data(model.iter_first) - json = JSON.pretty_generate(data, :max_nesting => false) - c = Gtk::Clipboard.get(Gdk::Selection::PRIMARY) - c.text = json - end - - # Copy json text from primary clipboard into model. - def paste(item) - c = Gtk::Clipboard.get(Gdk::Selection::PRIMARY) - if json = c.wait_for_text - window.ask_save if @changed - begin - window.edit json - rescue JSON::ParserError - window.clear - end - end - end - - # Find a string in all nodes' contents and select the found node in the - # treeview. - def find(item) - @search = ask_for_find_term(@search) or return - iter = model.get_iter('0') or return - iter.recursive_each do |i| - if @iter - if @iter != i - next - else - @iter = nil - next - end - elsif @search.match(i[CONTENT_COL]) - set_cursor(i.path, nil, false) - @iter = i - break - end - end - end - - # Repeat the last search given by #find. - def find_again(item) - @search or return - iter = model.get_iter('0') - iter.recursive_each do |i| - if @iter - if @iter != i - next - else - @iter = nil - next - end - elsif @search.match(i[CONTENT_COL]) - set_cursor(i.path, nil, false) - @iter = i - break - end - end - end - - # Sort (Reverse sort) all elements of the selected array by the given - # expression. _x_ is the element in question. - def sort(item) - if current = selection.selected - if current.type == 'Array' - parent = current.parent - ary = Editor.model2data(current) - order, reverse = ask_for_order - order or return - begin - block = eval "lambda { |x| #{order} }" - if reverse - ary.sort! { |a,b| block[b] <=> block[a] } - else - ary.sort! { |a,b| block[a] <=> block[b] } - end - rescue => e - Editor.error_dialog(self, "Failed to sort Array with #{order}: #{e}!") - else - Editor.data2model(ary, model, parent) do |m| - m.insert_before(parent, current) - end - model.remove(current) - expand_collapse(parent) - window.change - toplevel.display_status("Array has been sorted.") - end - else - toplevel.display_status("Only Array nodes can be sorted!") - end - else - toplevel.display_status("Select an Array to sort first!") - end - end - - # Create the menu. - def create - title = MenuItem.new('Edit') - title.submenu = menu - add_item('Copy', ?c, &method(:copy)) - add_item('Paste', ?v, &method(:paste)) - add_separator - add_item('Find', ?f, &method(:find)) - add_item('Find Again', ?g, &method(:find_again)) - add_separator - add_item('Sort', ?S, &method(:sort)) - title - end - end - - class OptionsMenu - include MenuExtension - - # Collapse/Expand all nodes by default. - def collapsed_nodes(item) - if expanded - self.expanded = false - collapse_all - else - self.expanded = true - expand_all - end - end - - # Toggle pretty saving mode on/off. - def pretty_saving(item) - @pretty_item.toggled - window.change - end - - attr_reader :pretty_item - - # Create the menu. - def create - title = MenuItem.new('Options') - title.submenu = menu - add_item('Collapsed nodes', nil, CheckMenuItem, &method(:collapsed_nodes)) - @pretty_item = add_item('Pretty saving', nil, CheckMenuItem, - &method(:pretty_saving)) - @pretty_item.active = true - window.unchange - title - end - end - - # This class inherits from Gtk::TreeView, to configure it and to add a lot - # of behaviour to it. - class JSONTreeView < Gtk::TreeView - include Gtk - - # Creates a JSONTreeView instance, the parameter _window_ is - # a MainWindow instance and used for self delegation. - def initialize(window) - @window = window - super(TreeStore.new(Gdk::Pixbuf, String, String)) - self.selection.mode = SELECTION_BROWSE - - @expanded = false - self.headers_visible = false - add_columns - add_popup_menu - end - - # Returns the MainWindow instance of this JSONTreeView. - attr_reader :window - - # Returns true, if nodes are autoexpanding, false otherwise. - attr_accessor :expanded - - private - - def add_columns - cell = CellRendererPixbuf.new - column = TreeViewColumn.new('Icon', cell, - 'pixbuf' => ICON_COL - ) - append_column(column) - - cell = CellRendererText.new - column = TreeViewColumn.new('Type', cell, - 'text' => TYPE_COL - ) - append_column(column) - - cell = CellRendererText.new - cell.editable = true - column = TreeViewColumn.new('Content', cell, - 'text' => CONTENT_COL - ) - cell.signal_connect(:edited, &method(:cell_edited)) - append_column(column) - end - - def unify_key(iter, key) - return unless iter.type == 'Key' - parent = iter.parent - if parent.any? { |c| c != iter and c.content == key } - old_key = key - i = 0 - begin - key = sprintf("%s.%d", old_key, i += 1) - end while parent.any? { |c| c != iter and c.content == key } - end - iter.content = key - end - - def cell_edited(cell, path, value) - iter = model.get_iter(path) - case iter.type - when 'Key' - unify_key(iter, value) - toplevel.display_status('Key has been changed.') - when 'FalseClass' - value.downcase! - if value == 'true' - iter.type, iter.content = 'TrueClass', 'true' - end - when 'TrueClass' - value.downcase! - if value == 'false' - iter.type, iter.content = 'FalseClass', 'false' - end - when 'Numeric' - iter.content = - if value == 'Infinity' - value - else - (Integer(value) rescue Float(value) rescue 0).to_s - end - when 'String' - iter.content = value - when 'Hash', 'Array' - return - else - fail "Unknown type found in model: #{iter.type}" - end - window.change - end - - def configure_value(value, type) - value.editable = false - case type - when 'Array', 'Hash' - value.text = '' - when 'TrueClass' - value.text = 'true' - when 'FalseClass' - value.text = 'false' - when 'NilClass' - value.text = 'null' - when 'Numeric', 'String' - value.text ||= '' - value.editable = true - else - raise ArgumentError, "unknown type '#{type}' encountered" - end - end - - def add_popup_menu - menu = PopUpMenu.new(self) - menu.create - end - - public - - # Create a _type_ node with content _content_, and add it to _parent_ - # in the model. If _parent_ is nil, create a new model and put it into - # the editor treeview. - def create_node(parent, type, content) - iter = if parent - model.append(parent) - else - new_model = Editor.data2model(nil) - toplevel.view_new_model(new_model) - new_model.iter_first - end - iter.type, iter.content = type, content - expand_collapse(parent) if parent - iter - end - - # Ask for a hash key, value pair to be added to the Hash node _parent_. - def ask_for_hash_pair(parent) - key_input = type_input = value_input = nil - - dialog = Dialog.new("New (key, value) pair for Hash", nil, nil, - [ Stock::OK, Dialog::RESPONSE_ACCEPT ], - [ Stock::CANCEL, Dialog::RESPONSE_REJECT ] - ) - dialog.width_request = 640 - - hbox = HBox.new(false, 5) - hbox.pack_start(Label.new("Key:"), false) - hbox.pack_start(key_input = Entry.new) - key_input.text = @key || '' - dialog.vbox.pack_start(hbox, false) - key_input.signal_connect(:activate) do - if parent.any? { |c| c.content == key_input.text } - toplevel.display_status('Key already exists in Hash!') - key_input.text = '' - else - toplevel.display_status('Key has been changed.') - end - end - - hbox = HBox.new(false, 5) - hbox.pack_start(Label.new("Type:"), false) - hbox.pack_start(type_input = ComboBox.new(true)) - ALL_TYPES.each { |t| type_input.append_text(t) } - type_input.active = @type || 0 - dialog.vbox.pack_start(hbox, false) - - type_input.signal_connect(:changed) do - value_input.editable = false - case ALL_TYPES[type_input.active] - when 'Array', 'Hash' - value_input.text = '' - when 'TrueClass' - value_input.text = 'true' - when 'FalseClass' - value_input.text = 'false' - when 'NilClass' - value_input.text = 'null' - else - value_input.text = '' - value_input.editable = true - end - end - - hbox = HBox.new(false, 5) - hbox.pack_start(Label.new("Value:"), false) - hbox.pack_start(value_input = Entry.new) - value_input.width_chars = 60 - value_input.text = @value || '' - dialog.vbox.pack_start(hbox, false) - - dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER) - dialog.show_all - self.focus = dialog - dialog.run do |response| - if response == Dialog::RESPONSE_ACCEPT - @key = key_input.text - type = ALL_TYPES[@type = type_input.active] - content = value_input.text - return @key, type, content - end - end - return - ensure - dialog.destroy - end - - # Ask for an element to be appended _parent_. - def ask_for_element(parent = nil, default_type = nil, value_text = @content) - type_input = value_input = nil - - dialog = Dialog.new( - "New element into #{parent ? parent.type : 'root'}", - nil, nil, - [ Stock::OK, Dialog::RESPONSE_ACCEPT ], - [ Stock::CANCEL, Dialog::RESPONSE_REJECT ] - ) - hbox = HBox.new(false, 5) - hbox.pack_start(Label.new("Type:"), false) - hbox.pack_start(type_input = ComboBox.new(true)) - default_active = 0 - types = parent ? ALL_TYPES : CONTAINER_TYPES - types.each_with_index do |t, i| - type_input.append_text(t) - if t == default_type - default_active = i - end - end - type_input.active = default_active - dialog.vbox.pack_start(hbox, false) - type_input.signal_connect(:changed) do - configure_value(value_input, types[type_input.active]) - end - - hbox = HBox.new(false, 5) - hbox.pack_start(Label.new("Value:"), false) - hbox.pack_start(value_input = Entry.new) - value_input.width_chars = 60 - value_input.text = value_text if value_text - configure_value(value_input, types[type_input.active]) - - dialog.vbox.pack_start(hbox, false) - - dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER) - dialog.show_all - self.focus = dialog - dialog.run do |response| - if response == Dialog::RESPONSE_ACCEPT - type = types[type_input.active] - @content = case type - when 'Numeric' - if (t = value_input.text) == 'Infinity' - 1 / 0.0 - else - Integer(t) rescue Float(t) rescue 0 - end - else - value_input.text - end.to_s - return type, @content - end - end - return - ensure - dialog.destroy if dialog - end - - # Ask for an order criteria for sorting, using _x_ for the element in - # question. Returns the order criterium, and true/false for reverse - # sorting. - def ask_for_order - dialog = Dialog.new( - "Give an order criterium for 'x'.", - nil, nil, - [ Stock::OK, Dialog::RESPONSE_ACCEPT ], - [ Stock::CANCEL, Dialog::RESPONSE_REJECT ] - ) - hbox = HBox.new(false, 5) - - hbox.pack_start(Label.new("Order:"), false) - hbox.pack_start(order_input = Entry.new) - order_input.text = @order || 'x' - order_input.width_chars = 60 - - hbox.pack_start(reverse_checkbox = CheckButton.new('Reverse'), false) - - dialog.vbox.pack_start(hbox, false) - - dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER) - dialog.show_all - self.focus = dialog - dialog.run do |response| - if response == Dialog::RESPONSE_ACCEPT - return @order = order_input.text, reverse_checkbox.active? - end - end - return - ensure - dialog.destroy if dialog - end - - # Ask for a find term to search for in the tree. Returns the term as a - # string. - def ask_for_find_term(search = nil) - dialog = Dialog.new( - "Find a node matching regex in tree.", - nil, nil, - [ Stock::OK, Dialog::RESPONSE_ACCEPT ], - [ Stock::CANCEL, Dialog::RESPONSE_REJECT ] - ) - hbox = HBox.new(false, 5) - - hbox.pack_start(Label.new("Regex:"), false) - hbox.pack_start(regex_input = Entry.new) - hbox.pack_start(icase_checkbox = CheckButton.new('Icase'), false) - regex_input.width_chars = 60 - if search - regex_input.text = search.source - icase_checkbox.active = search.casefold? - end - - dialog.vbox.pack_start(hbox, false) - - dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER) - dialog.show_all - self.focus = dialog - dialog.run do |response| - if response == Dialog::RESPONSE_ACCEPT - begin - return Regexp.new(regex_input.text, icase_checkbox.active? ? Regexp::IGNORECASE : 0) - rescue => e - Editor.error_dialog(self, "Evaluation of regex /#{regex_input.text}/ failed: #{e}!") - return - end - end - end - return - ensure - dialog.destroy if dialog - end - - # Expand or collapse row pointed to by _iter_ according - # to the #expanded attribute. - def expand_collapse(iter) - if expanded - expand_row(iter.path, true) - else - collapse_row(iter.path) - end - end - end - - # The editor main window - class MainWindow < Gtk::Window - include Gtk - - def initialize(encoding) - @changed = false - @encoding = encoding - super(TOPLEVEL) - display_title - set_default_size(800, 600) - signal_connect(:delete_event) { quit } - - vbox = VBox.new(false, 0) - add(vbox) - #vbox.border_width = 0 - - @treeview = JSONTreeView.new(self) - @treeview.signal_connect(:'cursor-changed') do - display_status('') - end - - menu_bar = create_menu_bar - vbox.pack_start(menu_bar, false, false, 0) - - sw = ScrolledWindow.new(nil, nil) - sw.shadow_type = SHADOW_ETCHED_IN - sw.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC) - vbox.pack_start(sw, true, true, 0) - sw.add(@treeview) - - @status_bar = Statusbar.new - vbox.pack_start(@status_bar, false, false, 0) - - @filename ||= nil - if @filename - data = read_data(@filename) - view_new_model Editor.data2model(data) - end - - signal_connect(:button_release_event) do |_,event| - if event.button == 2 - c = Gtk::Clipboard.get(Gdk::Selection::PRIMARY) - if url = c.wait_for_text - location_open url - end - false - else - true - end - end - end - - # Creates the menu bar with the pulldown menus and returns it. - def create_menu_bar - menu_bar = MenuBar.new - @file_menu = FileMenu.new(@treeview) - menu_bar.append @file_menu.create - @edit_menu = EditMenu.new(@treeview) - menu_bar.append @edit_menu.create - @options_menu = OptionsMenu.new(@treeview) - menu_bar.append @options_menu.create - menu_bar - end - - # Sets editor status to changed, to indicate that the edited data - # containts unsaved changes. - def change - @changed = true - display_title - end - - # Sets editor status to unchanged, to indicate that the edited data - # doesn't containt unsaved changes. - def unchange - @changed = false - display_title - end - - # Puts a new model _model_ into the Gtk::TreeView to be edited. - def view_new_model(model) - @treeview.model = model - @treeview.expanded = true - @treeview.expand_all - unchange - end - - # Displays _text_ in the status bar. - def display_status(text) - @cid ||= nil - @status_bar.pop(@cid) if @cid - @cid = @status_bar.get_context_id('dummy') - @status_bar.push(@cid, text) - end - - # Opens a dialog, asking, if changes should be saved to a file. - def ask_save - if Editor.question_dialog(self, - "Unsaved changes to JSON model. Save?") - if @filename - file_save - else - file_save_as - end - end - end - - # Quit this editor, that is, leave this editor's main loop. - def quit - ask_save if @changed - if Gtk.main_level > 0 - destroy - Gtk.main_quit - end - nil - end - - # Display the new title according to the editor's current state. - def display_title - title = TITLE.dup - title << ": #@filename" if @filename - title << " *" if @changed - self.title = title - end - - # Clear the current model, after asking to save all unsaved changes. - def clear - ask_save if @changed - @filename = nil - self.view_new_model nil - end - - def check_pretty_printed(json) - pretty = !!((nl_index = json.index("\n")) && nl_index != json.size - 1) - @options_menu.pretty_item.active = pretty - end - private :check_pretty_printed - - # Open the data at the location _uri_, if given. Otherwise open a dialog - # to ask for the _uri_. - def location_open(uri = nil) - uri = ask_for_location unless uri - uri or return - ask_save if @changed - data = load_location(uri) or return - view_new_model Editor.data2model(data) - end - - # Open the file _filename_ or call the #select_file method to ask for a - # filename. - def file_open(filename = nil) - filename = select_file('Open as a JSON file') unless filename - data = load_file(filename) or return - view_new_model Editor.data2model(data) - end - - # Edit the string _json_ in the editor. - def edit(json) - if json.respond_to? :read - json = json.read - end - data = parse_json json - view_new_model Editor.data2model(data) - end - - # Save the current file. - def file_save - if @filename - store_file(@filename) - else - file_save_as - end - end - - # Save the current file as the filename - def file_save_as - filename = select_file('Save as a JSON file') - store_file(filename) - end - - # Store the current JSON document to _path_. - def store_file(path) - if path - data = Editor.model2data(@treeview.model.iter_first) - File.open(path + '.tmp', 'wb') do |output| - data or break - if @options_menu.pretty_item.active? - output.puts JSON.pretty_generate(data, :max_nesting => false) - else - output.write JSON.generate(data, :max_nesting => false) - end - end - File.rename path + '.tmp', path - @filename = path - toplevel.display_status("Saved data to '#@filename'.") - unchange - end - rescue SystemCallError => e - Editor.error_dialog(self, "Failed to store JSON file: #{e}!") - end - - # Load the file named _filename_ into the editor as a JSON document. - def load_file(filename) - if filename - if File.directory?(filename) - Editor.error_dialog(self, "Try to select a JSON file!") - nil - else - @filename = filename - if data = read_data(filename) - toplevel.display_status("Loaded data from '#@filename'.") - end - display_title - data - end - end - end - - # Load the data at location _uri_ into the editor as a JSON document. - def load_location(uri) - data = read_data(uri) or return - @filename = nil - toplevel.display_status("Loaded data from '#{uri}'.") - display_title - data - end - - def parse_json(json) - check_pretty_printed(json) - if @encoding && !/^utf8$/i.match(@encoding) - json = JSON.iconv 'utf-8', @encoding, json - end - JSON::parse(json, :max_nesting => false, :create_additions => false) - end - private :parse_json - - # Read a JSON document from the file named _filename_, parse it into a - # ruby data structure, and return the data. - def read_data(filename) - open(filename) do |f| - json = f.read - return parse_json(json) - end - rescue => e - Editor.error_dialog(self, "Failed to parse JSON file: #{e}!") - return - end - - # Open a file selecton dialog, displaying _message_, and return the - # selected filename or nil, if no file was selected. - def select_file(message) - filename = nil - fs = FileSelection.new(message) - fs.set_modal(true) - @default_dir = File.join(Dir.pwd, '') unless @default_dir - fs.set_filename(@default_dir) - fs.set_transient_for(self) - fs.signal_connect(:destroy) { Gtk.main_quit } - fs.ok_button.signal_connect(:clicked) do - filename = fs.filename - @default_dir = File.join(File.dirname(filename), '') - fs.destroy - Gtk.main_quit - end - fs.cancel_button.signal_connect(:clicked) do - fs.destroy - Gtk.main_quit - end - fs.show_all - Gtk.main - filename - end - - # Ask for location URI a to load data from. Returns the URI as a string. - def ask_for_location - dialog = Dialog.new( - "Load data from location...", - nil, nil, - [ Stock::OK, Dialog::RESPONSE_ACCEPT ], - [ Stock::CANCEL, Dialog::RESPONSE_REJECT ] - ) - hbox = HBox.new(false, 5) - - hbox.pack_start(Label.new("Location:"), false) - hbox.pack_start(location_input = Entry.new) - location_input.width_chars = 60 - location_input.text = @location || '' - - dialog.vbox.pack_start(hbox, false) - - dialog.signal_connect(:'key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER) - dialog.show_all - dialog.run do |response| - if response == Dialog::RESPONSE_ACCEPT - return @location = location_input.text - end - end - return - ensure - dialog.destroy if dialog - end - end - - class << self - # Starts a JSON Editor. If a block was given, it yields - # to the JSON::Editor::MainWindow instance. - def start(encoding = 'utf8') # :yield: window - Gtk.init - @window = Editor::MainWindow.new(encoding) - @window.icon_list = [ Editor.fetch_icon('json') ] - yield @window if block_given? - @window.show_all - Gtk.main - end - - # Edit the string _json_ with encoding _encoding_ in the editor. - def edit(json, encoding = 'utf8') - start(encoding) do |window| - window.edit json - end - end - - attr_reader :window - end - end -end diff --git a/lib/json/ext.rb b/lib/json/ext.rb index 1fbc3fd..7264a85 100644 --- a/lib/json/ext.rb +++ b/lib/json/ext.rb @@ -4,21 +4,8 @@ module JSON # This module holds all the modules/classes that implement JSON's # functionality as C extensions. module Ext - begin - if defined?(RUBY_ENGINE) == 'constant' and RUBY_ENGINE == 'ruby' and RUBY_VERSION =~ /\A1\.9\./ - require 'json/ext/1.9/parser' - require 'json/ext/1.9/generator' - elsif !defined?(RUBY_ENGINE) && RUBY_VERSION =~ /\A1\.8\./ - require 'json/ext/1.8/parser' - require 'json/ext/1.8/generator' - else - require 'json/ext/parser' - require 'json/ext/generator' - end - rescue LoadError - require 'json/ext/parser' - require 'json/ext/generator' - end + require 'json/ext/parser' + require 'json/ext/generator' $DEBUG and warn "Using Ext extension for JSON." JSON.parser = Parser JSON.generator = Generator diff --git a/lib/json/json.xpm b/lib/json/json.xpm deleted file mode 100644 index 2cb626b..0000000 --- a/lib/json/json.xpm +++ /dev/null @@ -1,1499 +0,0 @@ -/* XPM */ -static char * json_xpm[] = { -"64 64 1432 2", -" c None", -". c #641839", -"+ c #CF163C", -"@ c #D31C3B", -"# c #E11A38", -"$ c #5F242D", -"% c #320C22", -"& c #9B532D", -"* c #F32E34", -"= c #820F33", -"- c #4B0F34", -"; c #8E1237", -"> c #944029", -", c #961325", -"' c #A00C24", -") c #872C23", -"! c #694021", -"~ c #590D1F", -"{ c #420528", -"] c #D85A2D", -"^ c #7E092B", -"/ c #0E0925", -"( c #0D081F", -"_ c #0F081E", -": c #12071F", -"< c #360620", -"[ c #682A21", -"} c #673F21", -"| c #780E21", -"1 c #A82320", -"2 c #8D1D1F", -"3 c #970127", -"4 c #0D0123", -"5 c #0D0324", -"6 c #3B1E28", -"7 c #C28429", -"8 c #0C0523", -"9 c #0C041E", -"0 c #0E031A", -"a c #11031A", -"b c #13031B", -"c c #13031C", -"d c #11031D", -"e c #19051E", -"f c #390E20", -"g c #9C0C20", -"h c #C00721", -"i c #980320", -"j c #14031E", -"k c #CD9F32", -"l c #C29F2E", -"m c #0F0325", -"n c #0D0321", -"o c #0E0324", -"p c #D08329", -"q c #9D1B27", -"r c #1C0320", -"s c #0D011A", -"t c #120117", -"u c #130017", -"v c #150018", -"w c #160119", -"x c #17021A", -"y c #15021B", -"z c #11021E", -"A c #0F021F", -"B c #8C1821", -"C c #CF4522", -"D c #831821", -"E c #BA7033", -"F c #EDB339", -"G c #C89733", -"H c #280727", -"I c #0F051F", -"J c #0E0420", -"K c #591F27", -"L c #E47129", -"M c #612224", -"N c #0C021D", -"O c #120018", -"P c #140017", -"Q c #170017", -"R c #190018", -"S c #1B0019", -"T c #1B011A", -"U c #18011B", -"V c #15011C", -"W c #12031E", -"X c #460A21", -"Y c #A13823", -"Z c #784323", -"` c #5A0C21", -" . c #BC4530", -".. c #EB5B38", -"+. c #CE4E3B", -"@. c #DD9334", -"#. c #751A27", -"$. c #11071E", -"%. c #0F041C", -"&. c #1E0824", -"*. c #955A28", -"=. c #9A5027", -"-. c #1E0321", -";. c #11011A", -">. c #140018", -",. c #180018", -"'. c #1F001A", -"). c #20001B", -"!. c #1E001A", -"~. c #1B001A", -"{. c #16021B", -"]. c #16041E", -"^. c #220622", -"/. c #5F3525", -"(. c #DE5724", -"_. c #611021", -":. c #0F0925", -"<. c #D1892E", -"[. c #F27036", -"}. c #EC633B", -"|. c #DA293C", -"1. c #E64833", -"2. c #912226", -"3. c #11081C", -"4. c #110419", -"5. c #0F041E", -"6. c #451425", -"7. c #BF6F28", -"8. c #332225", -"9. c #0E021E", -"0. c #13001B", -"a. c #17001A", -"b. c #1C001B", -"c. c #21001C", -"d. c #23001C", -"e. c #21001B", -"f. c #19021A", -"g. c #17041E", -"h. c #150721", -"i. c #602424", -"j. c #D51223", -"k. c #540820", -"l. c #D04D2D", -"m. c #EA8933", -"n. c #875637", -"o. c #88543A", -"p. c #E5923A", -"q. c #891931", -"r. c #130B25", -"s. c #10051B", -"t. c #110217", -"u. c #12021A", -"v. c #761826", -"w. c #E2A728", -"x. c #300224", -"y. c #10011E", -"z. c #16001B", -"A. c #1B001B", -"B. c #21001A", -"C. c #1E0019", -"D. c #1D0019", -"E. c #1A011A", -"F. c #17031C", -"G. c #120720", -"H. c #4E0822", -"I. c #670721", -"J. c #C07630", -"K. c #F59734", -"L. c #BE1B35", -"M. c #0E1435", -"N. c #522037", -"O. c #DB8039", -"P. c #D45933", -"Q. c #420927", -"R. c #0F041D", -"S. c #140118", -"T. c #13021D", -"U. c #100423", -"V. c #7B6227", -"W. c #C04326", -"X. c #0E0020", -"Y. c #13001D", -"Z. c #18001B", -"`. c #1E001B", -" + c #22001C", -".+ c #22001B", -"++ c #1B011B", -"@+ c #16041D", -"#+ c #130520", -"$+ c #860521", -"%+ c #710520", -"&+ c #670A2A", -"*+ c #A66431", -"=+ c #E97536", -"-+ c #F8833A", -";+ c #F77A3A", -">+ c #C45337", -",+ c #0A1C35", -"'+ c #993638", -")+ c #F7863B", -"!+ c #F49736", -"~+ c #94462B", -"{+ c #0E031F", -"]+ c #130119", -"^+ c #160018", -"/+ c #16011B", -"(+ c #15021F", -"_+ c #120123", -":+ c #A65C28", -"<+ c #5C4D23", -"[+ c #0F001F", -"}+ c #14001D", -"|+ c #1A001B", -"1+ c #1F001B", -"2+ c #24001D", -"3+ c #25001D", -"4+ c #24001C", -"5+ c #1F001C", -"6+ c #1A011C", -"7+ c #16021E", -"8+ c #3F0421", -"9+ c #BC0522", -"0+ c #1C041E", -"a+ c #7F5531", -"b+ c #E68A38", -"c+ c #F8933E", -"d+ c #FA7942", -"e+ c #FB7543", -"f+ c #FA6F41", -"g+ c #F1793D", -"h+ c #7D3B3A", -"i+ c #28263B", -"j+ c #D45441", -"k+ c #F8A238", -"l+ c #996B2D", -"m+ c #0E0421", -"n+ c #12011A", -"o+ c #180019", -"p+ c #17001C", -"q+ c #12001F", -"r+ c #4C2B2A", -"s+ c #DB8130", -"t+ c #540023", -"u+ c #0F0120", -"v+ c #16011C", -"w+ c #22001D", -"x+ c #25001F", -"y+ c #26001F", -"z+ c #25001E", -"A+ c #24001E", -"B+ c #1D001C", -"C+ c #18011D", -"D+ c #16031F", -"E+ c #3C0522", -"F+ c #9B0821", -"G+ c #13041E", -"H+ c #F6462E", -"I+ c #E6AB37", -"J+ c #E7A03E", -"K+ c #FA9F44", -"L+ c #FB8A48", -"M+ c #FD7A4A", -"N+ c #FD794A", -"O+ c #FD7748", -"P+ c #FD7E45", -"Q+ c #FD8343", -"R+ c #FB5D42", -"S+ c #6E3A40", -"T+ c #EE8A37", -"U+ c #7E252B", -"V+ c #100520", -"W+ c #13011A", -"X+ c #170019", -"Y+ c #15001C", -"Z+ c #0F0020", -"`+ c #564427", -" @ c #E0BA29", -".@ c #5E2B25", -"+@ c #10011F", -"@@ c #17011C", -"#@ c #1E001D", -"$@ c #23001F", -"%@ c #250020", -"&@ c #24001F", -"*@ c #23001E", -"=@ c #21001E", -"-@ c #1B001C", -";@ c #17021D", -">@ c #14041E", -",@ c #AC0B25", -"'@ c #5E1420", -")@ c #F28635", -"!@ c #C2733E", -"~@ c #984C44", -"{@ c #EA9148", -"]@ c #FB844B", -"^@ c #FD7E4C", -"/@ c #FE7E4C", -"(@ c #FE7E4B", -"_@ c #FE7749", -":@ c #FD7148", -"<@ c #FB7D46", -"[@ c #F89641", -"}@ c #B95634", -"|@ c #0D0927", -"1@ c #11041D", -"2@ c #150119", -"3@ c #180017", -"4@ c #16001A", -"5@ c #13001E", -"6@ c #110023", -"7@ c #944C29", -"8@ c #EE6229", -"9@ c #3D0324", -"0@ c #12021F", -"a@ c #19011D", -"b@ c #21001F", -"c@ c #22001F", -"d@ c #20001E", -"e@ c #1F001D", -"f@ c #1C001C", -"g@ c #19011C", -"h@ c #3D1621", -"i@ c #B53622", -"j@ c #31061F", -"k@ c #841D34", -"l@ c #F2703F", -"m@ c #C14445", -"n@ c #E67349", -"o@ c #FB8E4B", -"p@ c #FD834C", -"q@ c #FE834D", -"r@ c #FE834C", -"s@ c #FE804C", -"t@ c #FD814B", -"u@ c #FB7D49", -"v@ c #F79B43", -"w@ c #AF1234", -"x@ c #0D0625", -"y@ c #13021C", -"z@ c #1A0019", -"A@ c #190019", -"B@ c #410225", -"C@ c #D39729", -"D@ c #AA5927", -"E@ c #0E0422", -"F@ c #15021E", -"G@ c #1A011D", -"H@ c #1D001D", -"I@ c #15031D", -"J@ c #240820", -"K@ c #A01023", -"L@ c #670B21", -"M@ c #3D0D33", -"N@ c #E63C3E", -"O@ c #EF7C45", -"P@ c #F59048", -"Q@ c #FB944A", -"R@ c #FD904A", -"S@ c #FE8E4B", -"T@ c #FE854A", -"U@ c #FE854B", -"V@ c #FE884C", -"W@ c #FC954B", -"X@ c #F8AB45", -"Y@ c #C37A35", -"Z@ c #0D0425", -"`@ c #13011B", -" # c #170018", -".# c #1A0018", -"+# c #1C0019", -"@# c #15001B", -"## c #100120", -"$# c #311F25", -"%# c #E68E28", -"&# c #7A1425", -"*# c #130321", -"=# c #17011E", -"-# c #1A001D", -";# c #19001B", -"># c #16021C", -",# c #130521", -"'# c #6F3123", -")# c #6D3022", -"!# c #C89433", -"~# c #EA7E3E", -"{# c #DB2943", -"]# c #EF7745", -"^# c #FB8544", -"/# c #FD9A43", -"(# c #FE9941", -"_# c #FE9D43", -":# c #FEA548", -"<# c #FEAE49", -"[# c #FCB944", -"}# c #CA9F35", -"|# c #0E0225", -"1# c #11001B", -"2# c #160019", -"3# c #12011B", -"4# c #0F0220", -"5# c #351D26", -"6# c #D85B28", -"7# c #6C0F26", -"8# c #190121", -"9# c #1B001E", -"0# c #1A001C", -"a# c #1D001B", -"b# c #130220", -"c# c #703A23", -"d# c #713A23", -"e# c #140327", -"f# c #411B36", -"g# c #C8713E", -"h# c #7A3A3F", -"i# c #CE2C3C", -"j# c #E77338", -"k# c #9C6535", -"l# c #9C6233", -"m# c #9C6332", -"n# c #9C6A35", -"o# c #C37D3C", -"p# c #FEAC41", -"q# c #FEC23E", -"r# c #826330", -"s# c #100122", -"t# c #120019", -"u# c #150017", -"v# c #190017", -"w# c #1B0018", -"x# c #12001A", -"y# c #10021F", -"z# c #1A0326", -"A# c #5F292A", -"B# c #7B4E29", -"C# c #3C0E25", -"D# c #1A0020", -"E# c #14021F", -"F# c #723B23", -"G# c #14001A", -"H# c #58042A", -"I# c #A28337", -"J# c #C8813B", -"K# c #B14B38", -"L# c #761231", -"M# c #5A132A", -"N# c #0D0726", -"O# c #0C0623", -"P# c #0B0723", -"Q# c #0B0A26", -"R# c #321C2D", -"S# c #C45B33", -"T# c #FEBB33", -"U# c #13052A", -"V# c #13011F", -"W# c #160017", -"X# c #15001A", -"Y# c #12001D", -"Z# c #94062A", -"`# c #630D2C", -" $ c #85292B", -".$ c #AA5E29", -"+$ c #1F0123", -"@$ c #19011F", -"#$ c #1E001C", -"$$ c #15031F", -"%$ c #712122", -"&$ c #712223", -"*$ c #14011B", -"=$ c #110321", -"-$ c #AF0C2B", -";$ c #E7D534", -">$ c #EAC934", -",$ c #84582D", -"'$ c #1B0824", -")$ c #11041E", -"!$ c #10021B", -"~$ c #100119", -"{$ c #100218", -"]$ c #0F041A", -"^$ c #0E0720", -"/$ c #2C1026", -"($ c #D8A328", -"_$ c #140322", -":$ c #160016", -"<$ c #14001F", -"[$ c #120024", -"}$ c #100128", -"|$ c #3C032F", -"1$ c #2C062E", -"2$ c #29022B", -"3$ c #A31D29", -"4$ c #976A25", -"5$ c #1A0321", -"6$ c #17031E", -"7$ c #1B021D", -"8$ c #20001C", -"9$ c #14041F", -"0$ c #703422", -"a$ c #6F3522", -"b$ c #8D0328", -"c$ c #920329", -"d$ c #0F0326", -"e$ c #100321", -"f$ c #11021B", -"g$ c #130117", -"h$ c #140016", -"i$ c #150015", -"j$ c #140015", -"k$ c #130116", -"l$ c #120219", -"m$ c #11031C", -"n$ c #12031D", -"o$ c #170016", -"p$ c #160020", -"q$ c #250029", -"r$ c #670033", -"s$ c #DCA238", -"t$ c #F5C736", -"u$ c #9A732E", -"v$ c #110227", -"w$ c #110324", -"x$ c #811924", -"y$ c #A04323", -"z$ c #250721", -"A$ c #1A041F", -"B$ c #1E011D", -"C$ c #1C011C", -"D$ c #18031D", -"E$ c #130721", -"F$ c #6F3623", -"G$ c #6B3622", -"H$ c #1A001A", -"I$ c #14011F", -"J$ c #12011E", -"K$ c #11011C", -"L$ c #140117", -"M$ c #170015", -"N$ c #150016", -"O$ c #120119", -"P$ c #11011B", -"Q$ c #11001A", -"R$ c #130018", -"S$ c #170118", -"T$ c #170119", -"U$ c #18021E", -"V$ c #1A0126", -"W$ c #6F2332", -"X$ c #E5563B", -"Y$ c #F1B83F", -"Z$ c #F6CC38", -"`$ c #9D7A2D", -" % c #130123", -".% c #130320", -"+% c #2A0721", -"@% c #B00E24", -"#% c #7D0B23", -"$% c #1F0522", -"%% c #1E0220", -"&% c #1D011E", -"*% c #1A031E", -"=% c #15051F", -"-% c #241322", -";% c #A32F23", -">% c #670E21", -",% c #1C001A", -"'% c #19001A", -")% c #180016", -"!% c #160118", -"~% c #140219", -"{% c #11021C", -"]% c #10021E", -"^% c #0F011D", -"/% c #170117", -"(% c #160219", -"_% c #17041D", -":% c #190523", -"<% c #8C042E", -"[% c #B65838", -"}% c #E9D73F", -"|% c #EED43E", -"1% c #D85538", -"2% c #493129", -"3% c #130120", -"4% c #15021D", -"5% c #330822", -"6% c #8A0825", -"7% c #3C0424", -"8% c #1E0322", -"9% c #1C0321", -"0% c #180421", -"a% c #130822", -"b% c #AF2D24", -"c% c #BC5623", -"d% c #2F071F", -"e% c #1A041C", -"f% c #1C031C", -"g% c #1D011C", -"h% c #160117", -"i% c #150419", -"j% c #12081D", -"k% c #0F0923", -"l% c #A77027", -"m% c #A60525", -"n% c #11021A", -"o% c #130218", -"p% c #150319", -"q% c #16061D", -"r% c #180923", -"s% c #9C1D2B", -"t% c #A32636", -"u% c #A66E3B", -"v% c #4B2E3C", -"w% c #412C36", -"x% c #36012D", -"y% c #140123", -"z% c #17001E", -"A% c #19011B", -"B% c #1A0421", -"C% c #340425", -"D% c #9E0326", -"E% c #1F0424", -"F% c #1C0524", -"G% c #180724", -"H% c #A91024", -"I% c #D55D24", -"J% c #90071E", -"K% c #3C051D", -"L% c #1C021C", -"M% c #1C011A", -"N% c #1D001A", -"O% c #160116", -"P% c #150216", -"Q% c #140217", -"R% c #140618", -"S% c #120D1D", -"T% c #231925", -"U% c #B16A2E", -"V% c #FDAC34", -"W% c #D58631", -"X% c #280E2A", -"Y% c #0D0A23", -"Z% c #0F0920", -"`% c #120C21", -" & c #1F1026", -".& c #A3352E", -"+& c #EE9F36", -"@& c #5D2A3C", -"#& c #960D3C", -"$& c #970638", -"%& c #A00330", -"&& c #4D0126", -"*& c #1C001F", -"=& c #280120", -"-& c #290223", -";& c #1F0425", -">& c #260726", -",& c #340A26", -"'& c #850925", -")& c #3A0823", -"!& c #82071D", -"~& c #5E071D", -"{& c #18051C", -"]& c #18021A", -"^& c #190118", -"/& c #160217", -"(& c #150418", -"_& c #130618", -":& c #110718", -"<& c #10081A", -"[& c #110D1D", -"}& c #291C24", -"|& c #A73B2D", -"1& c #FD6B36", -"2& c #FD853C", -"3& c #FD863B", -"4& c #C24A35", -"5& c #6B442F", -"6& c #6D302D", -"7& c #6E252E", -"8& c #8E3B32", -"9& c #DE7739", -"0& c #F48E3F", -"a& c #DD8D41", -"b& c #854F3D", -"c& c #7E2D35", -"d& c #33082B", -"e& c #1C0222", -"f& c #20001F", -"g& c #1F0222", -"h& c #1A0524", -"i& c #440C27", -"j& c #BC1427", -"k& c #20041B", -"l& c #53061C", -"m& c #25071B", -"n& c #11061A", -"o& c #130418", -"p& c #140317", -"q& c #150217", -"r& c #160318", -"s& c #12051B", -"t& c #100C1D", -"u& c #0E101E", -"v& c #0C121F", -"w& c #0C1321", -"x& c #781725", -"y& c #B25D2C", -"z& c #FA6335", -"A& c #FD633C", -"B& c #FE6D42", -"C& c #FE7C42", -"D& c #FE813F", -"E& c #FE873C", -"F& c #FD743B", -"G& c #FB683B", -"H& c #FA7A3E", -"I& c #F98242", -"J& c #F97844", -"K& c #F98943", -"L& c #F79C3D", -"M& c #A25133", -"N& c #280B28", -"O& c #1D021F", -"P& c #1F011C", -"Q& c #280321", -"R& c #1C0724", -"S& c #3F1C27", -"T& c #D33C27", -"U& c #0E061B", -"V& c #0C091C", -"W& c #0C0A1B", -"X& c #0E091A", -"Y& c #11081B", -"Z& c #100A20", -"`& c #0E0D23", -" * c #551227", -".* c #B21829", -"+* c #C42329", -"@* c #C62C29", -"#* c #C55429", -"$* c #E76F2B", -"%* c #F14232", -"&* c #F95E3A", -"** c #FC6740", -"=* c #FE6E45", -"-* c #FE7246", -";* c #FE7545", -">* c #FE7744", -",* c #FD7745", -"'* c #FD7845", -")* c #FD7847", -"!* c #FD7948", -"~* c #FD7B44", -"{* c #FC7C3B", -"]* c #6F3130", -"^* c #140B24", -"/* c #19031D", -"(* c #1C011B", -"_* c #5A011F", -":* c #B70421", -"<* c #380824", -"[* c #3E2626", -"}* c #9F5626", -"|* c #13051E", -"1* c #360A21", -"2* c #361223", -"3* c #371724", -"4* c #381824", -"5* c #3B1524", -"6* c #3E1E26", -"7* c #471A29", -"8* c #DB252E", -"9* c #ED2733", -"0* c #EE5436", -"a* c #F04237", -"b* c #F33934", -"c* c #F53D2F", -"d* c #D7312B", -"e* c #AF212B", -"f* c #3A2C31", -"g* c #F65F39", -"h* c #FB6F41", -"i* c #FD6D45", -"j* c #FE7047", -"k* c #FE7647", -"l* c #FE7847", -"m* c #FE7848", -"n* c #FE7748", -"o* c #FE7948", -"p* c #FE7C48", -"q* c #FE7C47", -"r* c #FE7642", -"s* c #FE7439", -"t* c #6D332C", -"u* c #100B21", -"v* c #16031B", -"w* c #2B001B", -"x* c #22011F", -"y* c #220521", -"z* c #1B0A23", -"A* c #421425", -"B* c #951924", -"C* c #381023", -"D* c #E94028", -"E* c #E7302B", -"F* c #EF432D", -"G* c #F4302E", -"H* c #F32C30", -"I* c #CB4432", -"J* c #DD3235", -"K* c #EF4B3A", -"L* c #F0333E", -"M* c #CC3D3F", -"N* c #E4313C", -"O* c #F34834", -"P* c #D13E2C", -"Q* c #431825", -"R* c #0E1424", -"S* c #3C202C", -"T* c #F15537", -"U* c #F97140", -"V* c #FC6E45", -"W* c #FE7547", -"X* c #FE7947", -"Y* c #FE7B48", -"Z* c #FE7D48", -"`* c #FE8047", -" = c #FE7A42", -".= c #FE7A38", -"+= c #6D442B", -"@= c #0F0B21", -"#= c #15031A", -"$= c #49001B", -"%= c #2F001C", -"&= c #21021E", -"*= c #220620", -"== c #1B0D23", -"-= c #641625", -";= c #951823", -">= c #390F25", -",= c #AC3A2A", -"'= c #B6492E", -")= c #ED7531", -"!= c #F45A34", -"~= c #F54C36", -"{= c #C72D39", -"]= c #DE283C", -"^= c #F33B40", -"/= c #F34142", -"(= c #D0393F", -"_= c #E72E39", -":= c #DB3C2E", -"<= c #461724", -"[= c #0F0D1E", -"}= c #140B1E", -"|= c #341427", -"1= c #CB4834", -"2= c #F7743F", -"3= c #FB7145", -"4= c #FE7747", -"5= c #FE7A47", -"6= c #FF7B48", -"7= c #FF7C48", -"8= c #FE7F47", -"9= c #FE8247", -"0= c #FE8642", -"a= c #FE8439", -"b= c #6D442D", -"c= c #0F0A21", -"d= c #14031A", -"e= c #20031D", -"f= c #210821", -"g= c #191024", -"h= c #CC1C25", -"i= c #961423", -"j= c #2C162C", -"k= c #BD242E", -"l= c #EF2C31", -"m= c #F54C34", -"n= c #F34037", -"o= c #F5353A", -"p= c #F7413D", -"q= c #F8423D", -"r= c #F93A39", -"s= c #F95731", -"t= c #341425", -"u= c #110A1D", -"v= c #140619", -"w= c #18051B", -"x= c #200F26", -"y= c #864833", -"z= c #F8773F", -"A= c #FC7445", -"B= c #FF7E48", -"C= c #FF7E49", -"D= c #FF7D49", -"E= c #FF7D48", -"F= c #FE8347", -"G= c #FE8743", -"H= c #FE893B", -"I= c #6E452F", -"J= c #100E23", -"K= c #14041A", -"L= c #55041D", -"M= c #540921", -"N= c #161124", -"O= c #CE6A25", -"P= c #3F1129", -"Q= c #170A29", -"R= c #0F0F29", -"S= c #15132B", -"T= c #1E182D", -"U= c #A82B3D", -"V= c #CB6633", -"W= c #CC6932", -"X= c #CC3D2D", -"Y= c #331225", -"Z= c #0F091C", -"`= c #120417", -" - c #160216", -".- c #190419", -"+- c #210F26", -"@- c #8C4934", -"#- c #F97A40", -"$- c #FC7545", -"%- c #FF7B49", -"&- c #FE7D46", -"*- c #FE7E43", -"=- c #FD7B3E", -"-- c #FA6934", -";- c #532328", -">- c #130B1D", -",- c #150519", -"'- c #14041C", -")- c #120920", -"!- c #C43624", -"~- c #A21E23", -"{- c #F87C30", -"]- c #C9302D", -"^- c #300F2A", -"/- c #591129", -"(- c #171328", -"_- c #171628", -":- c #141829", -"<- c #101A2B", -"[- c #0F172B", -"}- c #0F1226", -"|- c #0E0C20", -"1- c #100619", -"2- c #140316", -"3- c #19051B", -"4- c #3C1428", -"5- c #E04B36", -"6- c #FA7B41", -"7- c #FD7346", -"8- c #FE7548", -"9- c #FF7849", -"0- c #FF7749", -"a- c #FE7B47", -"b- c #FE7945", -"c- c #FC7740", -"d- c #FA7E39", -"e- c #C1432F", -"f- c #131523", -"g- c #130A1C", -"h- c #420621", -"i- c #D08423", -"j- c #F87739", -"k- c #C03D37", -"l- c #962B34", -"m- c #A14332", -"n- c #E54B30", -"o- c #9E3E2F", -"p- c #7F262E", -"q- c #922D2E", -"r- c #9C4B2E", -"s- c #65212C", -"t- c #101628", -"u- c #101022", -"v- c #11091C", -"w- c #130619", -"x- c #160A1E", -"y- c #43252C", -"z- c #F66439", -"A- c #FA6942", -"B- c #FD6C47", -"C- c #FE6E48", -"D- c #FE6F48", -"E- c #FE7049", -"F- c #FE714A", -"G- c #FE744A", -"H- c #FE7846", -"I- c #FD7243", -"J- c #FC703E", -"K- c #FA6C37", -"L- c #81312B", -"M- c #121123", -"N- c #15071D", -"O- c #16031A", -"P- c #17021B", -"Q- c #8F3D22", -"R- c #F8393E", -"S- c #E42A3D", -"T- c #E7473B", -"U- c #FB503B", -"V- c #FB4F3A", -"W- c #F95439", -"X- c #ED4C38", -"Y- c #F45938", -"Z- c #FB6537", -"`- c #EA5236", -" ; c #CE6232", -".; c #CD392C", -"+; c #181425", -"@; c #120F21", -"#; c #130D20", -"$; c #151225", -"%; c #903431", -"&; c #F8703D", -"*; c #FB6344", -"=; c #FD6748", -"-; c #FE6849", -";; c #FE6949", -">; c #FE6A49", -",; c #FE6C4A", -"'; c #FE704A", -"); c #FE734A", -"!; c #FE7449", -"~; c #FE7347", -"{; c #FE7145", -"]; c #FD6C42", -"^; c #FD753D", -"/; c #F36E35", -"(; c #CB452C", -"_; c #600D24", -":; c #1C061F", -"<; c #1E031F", -"[; c #5B3821", -"}; c #CE9822", -"|; c #FA4341", -"1; c #FB4341", -"2; c #FC4541", -"3; c #FC4542", -"4; c #FC4143", -"5; c #FC4D42", -"6; c #FB5042", -"7; c #FB5342", -"8; c #FC5242", -"9; c #FD4F40", -"0; c #FD503E", -"a; c #FB6339", -"b; c #F45E33", -"c; c #A12A2E", -"d; c #401E2C", -"e; c #452D2F", -"f; c #F74F38", -"g; c #FA5940", -"h; c #FC6245", -"i; c #FE6447", -"j; c #FE6449", -"k; c #FE6549", -"l; c #FE6749", -"m; c #FE6B49", -"n; c #FE6D49", -"o; c #FE6D48", -"p; c #FE6D47", -"q; c #FE6D45", -"r; c #FE6C44", -"s; c #FE6A42", -"t; c #FE663C", -"u; c #FC6233", -"v; c #752129", -"w; c #1F0922", -"x; c #750520", -"y; c #81061F", -"z; c #FA3D42", -"A; c #FB4142", -"B; c #FD4543", -"C; c #FD4844", -"D; c #FD4A45", -"E; c #FD4D45", -"F; c #FD5045", -"G; c #FD5345", -"H; c #FE5346", -"I; c #FE5445", -"J; c #FD5444", -"K; c #FC4F41", -"L; c #FA513D", -"M; c #F95339", -"N; c #F63736", -"O; c #F75737", -"P; c #F95F3B", -"Q; c #FB5840", -"R; c #FD5F43", -"S; c #FE6345", -"T; c #FE6547", -"U; c #FE6548", -"V; c #FE6448", -"W; c #FE6248", -"X; c #FE6348", -"Y; c #FE6748", -"Z; c #FE6848", -"`; c #FE6846", -" > c #FE6A45", -".> c #FE6D43", -"+> c #FE703F", -"@> c #FC6F36", -"#> c #6F302B", -"$> c #140A22", -"%> c #FA3B42", -"&> c #FC4243", -"*> c #FD4744", -"=> c #FE4A45", -"-> c #FE4C47", -";> c #FE4D47", -">> c #FE5047", -",> c #FE5347", -"'> c #FE5447", -")> c #FD5246", -"!> c #FB503F", -"~> c #FA543D", -"{> c #9B3D3B", -"]> c #A3433B", -"^> c #F9683D", -"/> c #FC6940", -"(> c #FE6342", -"_> c #FE6645", -":> c #FE6646", -"<> c #FE6147", -"[> c #FE6048", -"}> c #FE6148", -"|> c #FE6746", -"1> c #FE6A46", -"2> c #FE6F45", -"3> c #FE7441", -"4> c #FC7D39", -"5> c #6C422E", -"6> c #0F0F23", -"7> c #FA4142", -"8> c #FC4643", -"9> c #FE4D46", -"0> c #FE4E47", -"a> c #FE4F48", -"b> c #FE5148", -"c> c #FE5348", -"d> c #FE5548", -"e> c #FE5247", -"f> c #FD5445", -"g> c #FC5544", -"h> c #F96041", -"i> c #D33F3D", -"j> c #392D39", -"k> c #973C38", -"l> c #F94E3A", -"m> c #FD693E", -"n> c #FE6C43", -"o> c #FE6047", -"p> c #FE5D47", -"q> c #FE5E48", -"r> c #FE6948", -"s> c #FE6947", -"t> c #FE6B47", -"u> c #FE6E46", -"v> c #FD6D43", -"w> c #FB723D", -"x> c #D54A33", -"y> c #301C29", -"z> c #FB4A42", -"A> c #FD4B44", -"B> c #FE4F47", -"C> c #FE5048", -"D> c #FE5648", -"E> c #FE5848", -"F> c #FE5747", -"G> c #FE5547", -"H> c #FC5945", -"I> c #F95742", -"J> c #F3543D", -"K> c #A33336", -"L> c #302032", -"M> c #152433", -"N> c #CD3E38", -"O> c #FD5A3F", -"P> c #FE6343", -"Q> c #FE6446", -"R> c #FE6247", -"S> c #FE6A47", -"T> c #FC6542", -"U> c #FB6A3B", -"V> c #FA6D34", -"W> c #D73C2D", -"X> c #442428", -"Y> c #281323", -"Z> c #FD4E42", -"`> c #FD4D43", -" , c #FE4D45", -"., c #FE5248", -"+, c #FE5947", -"@, c #FE5C47", -"#, c #FE5B47", -"$, c #FE5A47", -"%, c #FE5847", -"&, c #FC5C45", -"*, c #F95B43", -"=, c #F3613F", -"-, c #E74F37", -";, c #8C2431", -">, c #161E2F", -",, c #CD4E33", -"', c #FD503A", -"), c #FE5D40", -"!, c #FE6445", -"~, c #FE6946", -"{, c #FE6847", -"], c #FE6747", -"^, c #FD6644", -"/, c #FD6241", -"(, c #FD5B3D", -"_, c #FE6739", -":, c #FE6135", -"<, c #AB4830", -"[, c #733E2A", -"}, c #161224", -"|, c #FC4E42", -"1, c #FE4D44", -"2, c #FE4E46", -"3, c #FE5147", -"4, c #FE5E47", -"5, c #FD5C46", -"6, c #FA5B44", -"7, c #F45441", -"8, c #EB393A", -"9, c #CC3433", -"0, c #47212F", -"a, c #59242F", -"b, c #FC6734", -"c, c #FC6F3A", -"d, c #FC723E", -"e, c #FD6540", -"f, c #FE6442", -"g, c #FE6643", -"h, c #FE6944", -"i, c #FE6546", -"j, c #FE6444", -"k, c #FE6143", -"l, c #FE5E41", -"m, c #FE613F", -"n, c #FE683C", -"o, c #FE7937", -"p, c #A25030", -"q, c #692629", -"r, c #151122", -"s, c #FA573F", -"t, c #FB4D40", -"u, c #FC4F43", -"v, c #FE5246", -"w, c #FF6347", -"x, c #FE5F48", -"y, c #F65942", -"z, c #F0493D", -"A, c #ED3736", -"B, c #73262F", -"C, c #10152C", -"D, c #3B292F", -"E, c #363034", -"F, c #AC3938", -"G, c #FC6B3B", -"H, c #FD763C", -"I, c #FE6D3F", -"J, c #FE6341", -"K, c #FE6642", -"L, c #FE6745", -"M, c #FE6245", -"N, c #FE6244", -"O, c #FE6841", -"P, c #FF683B", -"Q, c #EC7035", -"R, c #D0412D", -"S, c #3A1627", -"T, c #CF3938", -"U, c #F6543C", -"V, c #FB5040", -"W, c #FD5544", -"X, c #FE5A48", -"Y, c #FE5D48", -"Z, c #FE5F47", -"`, c #FF6147", -" ' c #FD5C45", -".' c #FB5B43", -"+' c #FA5A42", -"@' c #F76040", -"#' c #F4623D", -"$' c #F26D38", -"%' c #EC4130", -"&' c #380E2B", -"*' c #13122C", -"=' c #362D31", -"-' c #353435", -";' c #352E37", -">' c #2D3337", -",' c #CC5838", -"'' c #CD6F3A", -")' c #CE6E3D", -"!' c #FE793F", -"~' c #FD7541", -"{' c #FD6243", -"]' c #FE6545", -"^' c #FF6543", -"/' c #FF6240", -"(' c #FE723B", -"_' c #FE8034", -":' c #442D2C", -"<' c #311725", -"[' c #222830", -"}' c #B73B36", -"|' c #F94C3D", -"1' c #FD5543", -"2' c #FE5B48", -"3' c #FF5E47", -"4' c #FE5C48", -"5' c #FC5B44", -"6' c #F95640", -"7' c #C34E3D", -"8' c #A45A3A", -"9' c #F37438", -"0' c #F28935", -"a' c #AF422F", -"b' c #240D2B", -"c' c #88292F", -"d' c #FA8E34", -"e' c #FC7E38", -"f' c #FC5939", -"g' c #694A37", -"h' c #693437", -"i' c #382638", -"j' c #142439", -"k' c #9F483A", -"l' c #C45E3C", -"m' c #FD7240", -"n' c #FF6645", -"o' c #FF6245", -"p' c #FF6045", -"q' c #FF6146", -"r' c #FF6246", -"s' c #FF6446", -"t' c #FF6545", -"u' c #FE763F", -"v' c #FE7237", -"w' c #C65331", -"x' c #3D272A", -"y' c #0D1E2B", -"z' c #683032", -"A' c #F9453A", -"B' c #FD5341", -"C' c #FE5A46", -"D' c #FF5A48", -"E' c #FE5948", -"F' c #FD5A47", -"G' c #FC5D43", -"H' c #F95B3D", -"I' c #713F37", -"J' c #1E2D32", -"K' c #C44531", -"L' c #EF7A2F", -"M' c #6B2E2C", -"N' c #0F0E2C", -"O' c #F56633", -"P' c #FA803A", -"Q' c #FC673E", -"R' c #FD673E", -"S' c #FC6F3C", -"T' c #FA6E3B", -"U' c #C6633A", -"V' c #A06739", -"W' c #835638", -"X' c #381F38", -"Y' c #713B38", -"Z' c #7B503C", -"`' c #FE7741", -" ) c #FE7344", -".) c #FE6D46", -"+) c #FF6946", -"@) c #FF5E46", -"#) c #FF5D46", -"$) c #FF5D47", -"%) c #FF5F48", -"&) c #FF6248", -"*) c #FE6941", -"=) c #FC783C", -"-) c #C46B35", -";) c #892730", -">) c #111629", -",) c #1F2630", -"') c #AD3939", -")) c #FC5D41", -"!) c #FE5946", -"~) c #FF5848", -"{) c #FE5549", -"]) c #FC5E42", -"^) c #FA673B", -"/) c #DB7033", -"() c #392E2B", -"_) c #311A28", -":) c #3C2127", -"<) c #1D1027", -"[) c #92102C", -"}) c #F58336", -"|) c #FA673E", -"1) c #FD6642", -"2) c #FD5A41", -"3) c #FC6D41", -"4) c #FC6D3F", -"5) c #FD683E", -"6) c #F38C39", -"7) c #CE6535", -"8) c #612E34", -"9) c #1D2637", -"0) c #71513E", -"a) c #FF6847", -"b) c #FF5F47", -"c) c #FF5A46", -"d) c #FF5847", -"e) c #FF5748", -"f) c #FF594A", -"g) c #FF5E4B", -"h) c #FE654C", -"i) c #FE694B", -"j) c #FE6B48", -"k) c #FC6A43", -"l) c #F7683E", -"m) c #EC6E39", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" . + @ # $ % ", -" & * = - ; > , ' ) ! ~ ", -" { ] ^ / ( _ : < [ } | 1 2 ", -" 3 4 5 6 7 8 9 0 a b c d e f g h i j ", -" k l m n o p q r s t u v w x y z A B C D ", -" E F G H I J K L M N O P Q R S T U V W X Y Z ` ", -" ...+.@.#.$.%.&.*.=.-.;.>.,.S '.).!.~.{.].^./.(._. ", -" :.<.[.}.|.1.2.3.4.5.6.7.8.9.0.a.b.c.d.e.!.S f.g.h.i.j.k. ", -" l.m.n.o.p.q.r.s.t.u.J v.w.x.y.z.A.c.d.d.B.C.D.E.F.G.H.I. ", -" J.K.L.M.N.O.P.Q.R.t S.T.U.V.W.X.Y.Z.`. +d.d..+B.'.++@+#+$+%+ ", -" &+*+=+-+;+>+,+'+)+!+~+{+]+^+/+(+_+:+<+[+}+|+1+d.2+3+4+d.5+6+7+8+9+0+ ", -" a+b+c+d+e+f+g+h+i+j+k+l+m+n+^+o+p+q+r+s+t+u+v+b.w+x+y+z+A+w+B+C+D+E+F+G+ ", -" H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+Q ,.X+Y+Z+`+ @.@+@@@#@$@%@&@*@=@#@-@;@>@,@'@ ", -" )@!@~@{@]@^@/@(@_@:@<@[@}@|@1@2@3@R ,.4@5@6@7@8@9@0@a@#@b@c@=@d@e@f@g@>@h@i@j@ ", -" k@l@m@n@o@p@q@r@s@t@u@v@w@x@y@^+R S z@A@z.q+B@C@D@E@F@G@H@#@e@#@#@f@g@I@J@K@L@ ", -" M@N@O@P@Q@R@S@T@U@V@W@X@Y@Z@`@ #.#+#+#S A@@###$#%#&#*#=#-#f@B+B+B+f@;#>#,#'#)# ", -" !#~#{#]#^#/#(#(#_#:#<#[#}#|#1#^+.#S +#+#z@2#3#4#5#6#7#8#9#0#A.B+B+a#A.@@b#c#d# ", -" e#f#g#h#i#j#k#l#m#n#o#p#q#r#s#t#u#v#.#w#S R ^+x#y#z#A#B#C#D#-#A.a#`.`.b.g@E#d#F# ", -" G#0@H#I#J#K#L#M#N#O#P#Q#R#S#T#U#V#>.W#3@v#R R X+X#Y#s#Z#`# $.$+$@$g@f@5+5+#$6+$$%$&$ ", -" *$=$-$;$>$,$'$)$!$~${$]$^$/$($_$*$u#:$Q 3@,.X+z.<$[$}$|$1$2$3$4$5$6$7$e@8$#$G@9$0$a$ ", -" ,.4@E#b$c$d$e$f$g$h$i$j$k$l$m$n$`@>.:$o$3@,. #a.p$q$r$s$t$u$v$w$x$y$z$A$B$#@C$D$E$F$G$ ", -" R S H$v+I$J$K$n+L$:$o$o$M$N$L$O$P$Q$R$N$o$3@S$T$U$V$W$X$Y$Z$`$ %.%+%@%#%$%%%&%*%=%-%;%>% ", -" E.,%~.'%Z.4@v W#o$)%)%)%Q !%~%{%]%^%Q$u u#/%(%_%:%<%[%}%|%1%2%3%4%=%5%6%7%8%9%0%a%b%c%d% ", -" e%f%g%a#,%,%z@R 3@3@3@)%Q h%i%j%k%l%m%{+n%o%p%q%r%s%t%u%v%w%x%y%z%A%*%B%C%D%E%F%G%H%I% ", -" J%K%L%M%N%D.S v#)%)%O%P%Q%R%S%T%U%V%W%X%Y%Z%`% &.&+&@&#&$&%&&&*&f@a##@=&-&;&>&,&'&)& ", -" !&~&{&]&^&.#w#^&/%/&(&_&:&<&[&}&|&1&2&3&4&5&6&7&8&9&0&a&b&c&d&e&e@1+5+e@f&g&h&i&j& ", -" k&l&m&n&o&p&q&r&i%s&3.t&u&v&w&x&y&z&A&B&C&D&E&F&G&H&I&J&K&L&M&N&O&P&1+`.e@f&Q&R&S&T& ", -" 0 U&V&W&X&<&Y&j%Z&`& *.*+*@*#*$*%*&***=*-*;*>*>*,*'*)*!*~*{*]*^*/*(*a#B+#@_*:*<*[*}* ", -" |*1*2*3*4*5*6*7*8*9*0*a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*E.w*d.e@x*y*z*A*B* ", -" C*D*E*F*G*H*I*J*K*L*M*N*O*P*Q*R*S*T*U*V*W*l*X*o*o*Y*Z*`* =.=+=@=#='%$=%=e@&=*===-=;= ", -" >=,='=)=!=~={=]=^=/=(=_=:=<=[=}=|=1=2=3=4=5=p*6=6=7=8=9=0=a=b=c=d=A@~.b.B+e=f=g=h=i= ", -" j=k=l=m=n=o=p=q=r=s=t=u=v=w=x=y=z=A=5=Z*B=C=D=E=8=F=G=H=I=J=K=S$R z@'%L=M=N=O= ", -" P=Q=R=S=T=U=V=W=X=Y=Z=`= -.-+-@-#-$-5=p*E=D=%-%-q*&-*-=---;->-,-/%3@^+'-)-!-~- ", -" {-]-^-/-(-_-:-<-[-}-|-1-2- -3-4-5-6-7-8-n*m*9-0-9-o*a-b-c-d-e-f-g-(&h%w c h-i- ", -" j-k-l-m-n-o-p-q-r-s-t-u-v-w-,-x-y-z-A-B-C-D-E-E-F-G-_@m*H-I-J-K-L-M-N-O-P-(+Q- ", -" R-S-T-U-V-W-X-Y-Z-`- ;.;+;@;#;$;%;&;*;=;-;-;;;>;,;';);!;~;{;];^;/;(;_;:;<;[;}; ", -" |;1;2;3;4;5;6;7;8;9;0;a;b;c;d;e;f;g;h;i;j;j;k;k;l;m;n;o;p;q;r;s;t;u;v;w;x;y; ", -" z;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;k;Y;Z;`; >r;.>+>@>#>$> ", -" %>&>*>=>->;>>>,>'>,>)>F;8;!>~>{>]>^>/>(>_>:>i;<>[>X;}>i;|>1>q;2>3>4>5>6> ", -" 7>8>=>9>0>a>b>c>d>,>e>e>f>g>h>i>j>k>l>m>n>:>i;o>p>q>W;r>s>t>p;u>v>w>x>y> ", -" z>A>9>0>B>C>c>D>E>F>G>G>F>H>I>J>K>L>M>N>O>P>Q>R>o>R>T;s>S>S>S>t>1>T>U>V>W>X>Y> ", -" Z>`> ,9>B>.,D>+,@,#,$,%,$,&,*,=,-,;,>,,,',),P>!,!,_>~,t>s>{,],{,],^,/,(,_,:,<,[,}, ", -" |,`>1,2,3,G>+,4,o>o>4,@,@,5,6,7,8,9,0,a,b,c,d,e,f,g,h, >~,|>T;T;T;i,j,k,l,m,n,o,p,q,r, ", -" s,t,u,v,G>%,@,o>w,R>x,p>@,5,6,y,z,A,B,C,D,E,F,G,H,I,J,K,L,L,i,i;i;i;Q>S;M,N,P>O,P,Q,R,S, ", -" T,U,V,W,%,X,Y,Z,`,[>q>@, '.'+'@'#'$'%'&'*'='-';'>',''')'!'~'{'N,i,:>_>]'M,M,Q>_>^'/'('_':'<' ", -" ['}'|'1'$,X,2'p>3'4'2'@,5'6'7'8'9'0'a'b'c'd'e'f'g'h'i'j'k'l'd,m'g, > >n'o'p'q'r's't'.>u'v'w'x' ", -" y'z'A'B'C'X,X,2'D'E'E'F'G'H'I'J'K'L'M'N'O'P'Q'R'S'T'U'V'W'X'Y'Z'`' ).)+)r'@)#)$)%)&)l;1>*)=)-);) ", -" >),)')))!)X,E'X,~){)d>!)])^)/)()_):)<)[)})|)1)f,2)3)4)5)6)7)8)9)0)*--*a)b)c)d)e)f)g)h)i)j)k)l)m) ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" "}; diff --git a/lib/json/pure/generator.rb b/lib/json/pure/generator.rb index 3687e30..7c9b2ad 100644 --- a/lib/json/pure/generator.rb +++ b/lib/json/pure/generator.rb @@ -133,6 +133,8 @@ module JSON # * *allow_nan*: true if NaN, Infinity, and -Infinity should be # generated, otherwise an exception is thrown, if these values are # encountered. This options defaults to false. + # * *quirks_mode*: Enables quirks_mode for parser, that is for example + # generating single JSON values instead of documents is possible. def initialize(opts = {}) @indent = '' @space = '' @@ -141,6 +143,7 @@ module JSON @array_nl = '' @allow_nan = false @ascii_only = false + @quirks_mode = false configure opts end @@ -165,6 +168,10 @@ module JSON # the generated JSON, max_nesting = 0 if no maximum is checked. attr_accessor :max_nesting + # If this attribute is set to true, quirks mode is enabled, otherwise + # it's disabled. + attr_accessor :quirks_mode + # This integer returns the current depth data structure nesting in the # generated JSON. attr_accessor :depth @@ -188,10 +195,17 @@ module JSON @allow_nan end + # Returns true, if only ASCII characters should be generated. Otherwise + # returns false. def ascii_only? @ascii_only end + # Returns true, if quirks mode is enabled. Otherwise returns false. + def quirks_mode? + @quirks_mode + end + # Configure this State instance with the Hash _opts_, and return # itself. def configure(opts) @@ -203,6 +217,7 @@ module JSON @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan) @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only) @depth = opts[:depth] || 0 + @quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode) if !opts.key?(:max_nesting) # defaults to 19 @max_nesting = 19 elsif opts[:max_nesting] @@ -218,7 +233,7 @@ module JSON # passed to the configure method. def to_h result = {} - for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only depth] + for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only quirks_mode depth] result[iv.intern] = instance_variable_get("@#{iv}") end result @@ -229,7 +244,7 @@ module JSON # GeneratorError exception. def generate(obj) result = obj.to_json(self) - if result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m + if !@quirks_mode && result !~ /\A\s*(?:\[.*\]|\{.*\})\s*\Z/m raise GeneratorError, "only generation of JSON objects or arrays allowed" end result diff --git a/lib/json/pure/parser.rb b/lib/json/pure/parser.rb index d612018..e24aac1 100644 --- a/lib/json/pure/parser.rb +++ b/lib/json/pure/parser.rb @@ -68,42 +68,12 @@ module JSON # defaults to true. # * *object_class*: Defaults to Hash # * *array_class*: Defaults to Array + # * *quirks_mode*: Enables quirks_mode for parser, that is for example + # parsing single JSON values instead of documents is possible. def initialize(source, opts = {}) opts ||= {} - if defined?(::Encoding) - if source.encoding == ::Encoding::ASCII_8BIT - b = source[0, 4].bytes.to_a - source = case - when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 - source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) - when b.size >= 4 && b[0] == 0 && b[2] == 0 - source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) - when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 - source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) - - when b.size >= 4 && b[1] == 0 && b[3] == 0 - source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) - else - source.dup - end - else - source = source.encode(::Encoding::UTF_8) - end - source.force_encoding(::Encoding::ASCII_8BIT) - else - b = source - source = case - when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 - JSON.iconv('utf-8', 'utf-32be', b) - when b.size >= 4 && b[0] == 0 && b[2] == 0 - JSON.iconv('utf-8', 'utf-16be', b) - when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 - JSON.iconv('utf-8', 'utf-32le', b) - when b.size >= 4 && b[1] == 0 && b[3] == 0 - JSON.iconv('utf-8', 'utf-16le', b) - else - b - end + unless @quirks_mode = opts[:quirks_mode] + source = determine_encoding source end super source if !opts.key?(:max_nesting) # defaults to 19 @@ -113,44 +83,108 @@ module JSON else @max_nesting = 0 end - @allow_nan = !!opts[:allow_nan] - @symbolize_names = !!opts[:symbolize_names] - @create_additions = opts.key?(:create_additions) ? !!opts[:create_additions] : true - @create_id = opts[:create_id] || JSON.create_id - @object_class = opts[:object_class] || Hash - @array_class = opts[:array_class] || Array - @match_string = opts[:match_string] + @allow_nan = !!opts[:allow_nan] + @symbolize_names = !!opts[:symbolize_names] + if opts.key?(:create_additions) + @create_additions = !!opts[:create_additions] + else + @create_additions = true + end + @create_id = @create_additions ? JSON.create_id : nil + @object_class = opts[:object_class] || Hash + @array_class = opts[:array_class] || Array + @match_string = opts[:match_string] end alias source string + def quirks_mode? + !!@quirks_mode + end + + def reset + super + @current_nesting = 0 + end + # Parses the current JSON string _source_ and returns the complete data # structure as a result. def parse reset obj = nil - until eos? - case - when scan(OBJECT_OPEN) - obj and raise ParserError, "source '#{peek(20)}' not in JSON!" - @current_nesting = 1 - obj = parse_object - when scan(ARRAY_OPEN) - obj and raise ParserError, "source '#{peek(20)}' not in JSON!" - @current_nesting = 1 - obj = parse_array - when skip(IGNORE) - ; + if @quirks_mode + while !eos? && skip(IGNORE) + end + if eos? + raise ParserError, "source did not contain any JSON!" else - raise ParserError, "source '#{peek(20)}' not in JSON!" + obj = parse_value + obj == UNPARSED and raise ParserError, "source did not contain any JSON!" end + else + until eos? + case + when scan(OBJECT_OPEN) + obj and raise ParserError, "source '#{peek(20)}' not in JSON!" + @current_nesting = 1 + obj = parse_object + when scan(ARRAY_OPEN) + obj and raise ParserError, "source '#{peek(20)}' not in JSON!" + @current_nesting = 1 + obj = parse_array + when skip(IGNORE) + ; + else + raise ParserError, "source '#{peek(20)}' not in JSON!" + end + end + obj or raise ParserError, "source did not contain any JSON!" end - obj or raise ParserError, "source did not contain any JSON!" obj end private + def determine_encoding(source) + if defined?(::Encoding) + if source.encoding == ::Encoding::ASCII_8BIT + b = source[0, 4].bytes.to_a + source = + case + when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 + source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8) + when b.size >= 4 && b[0] == 0 && b[2] == 0 + source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8) + when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 + source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8) + when b.size >= 4 && b[1] == 0 && b[3] == 0 + source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8) + else + source.dup + end + else + source = source.encode(::Encoding::UTF_8) + end + source.force_encoding(::Encoding::ASCII_8BIT) + else + b = source + source = + case + when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0 + JSON.iconv('utf-8', 'utf-32be', b) + when b.size >= 4 && b[0] == 0 && b[2] == 0 + JSON.iconv('utf-8', 'utf-16be', b) + when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0 + JSON.iconv('utf-8', 'utf-32le', b) + when b.size >= 4 && b[1] == 0 && b[3] == 0 + JSON.iconv('utf-8', 'utf-16le', b) + else + b + end + end + source + end + # Unescape characters in strings. UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr } UNESCAPE_MAP.update({ diff --git a/lib/json/version.rb b/lib/json/version.rb index 2175ac0..5983945 100644 --- a/lib/json/version.rb +++ b/lib/json/version.rb @@ -1,6 +1,6 @@ module JSON # JSON version - VERSION = '1.5.4' + VERSION = '1.6.1' VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: diff --git a/tests/test_json.rb b/tests/test_json.rb index b367e90..eafd758 100755 --- a/tests/test_json.rb +++ b/tests/test_json.rb @@ -104,6 +104,42 @@ class TC_JSON < Test::Unit::TestCase assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } ')) end + def test_parse_json_primitive_values + assert_raise(JSON::ParserError) { JSON.parse('') } + assert_raise(JSON::ParserError) { JSON.parse('', :quirks_mode => true) } + assert_raise(JSON::ParserError) { JSON.parse(' /* foo */ ') } + assert_raise(JSON::ParserError) { JSON.parse(' /* foo */ ', :quirks_mode => true) } + parser = JSON::Parser.new('null') + assert_equal false, parser.quirks_mode? + assert_raise(JSON::ParserError) { parser.parse } + assert_raise(JSON::ParserError) { JSON.parse('null') } + assert_equal nil, JSON.parse('null', :quirks_mode => true) + parser = JSON::Parser.new('null', :quirks_mode => true) + assert_equal true, parser.quirks_mode? + assert_equal nil, parser.parse + assert_raise(JSON::ParserError) { JSON.parse('false') } + assert_equal false, JSON.parse('false', :quirks_mode => true) + assert_raise(JSON::ParserError) { JSON.parse('true') } + assert_equal true, JSON.parse('true', :quirks_mode => true) + assert_raise(JSON::ParserError) { JSON.parse('23') } + assert_equal 23, JSON.parse('23', :quirks_mode => true) + assert_raise(JSON::ParserError) { JSON.parse('1') } + assert_equal 1, JSON.parse('1', :quirks_mode => true) + assert_raise(JSON::ParserError) { JSON.parse('3.141') } + assert_in_delta 3.141, JSON.parse('3.141', :quirks_mode => true), 1E-3 + assert_raise(JSON::ParserError) { JSON.parse('18446744073709551616') } + assert_equal 2 ** 64, JSON.parse('18446744073709551616', :quirks_mode => true) + assert_raise(JSON::ParserError) { JSON.parse('"foo"') } + assert_equal 'foo', JSON.parse('"foo"', :quirks_mode => true) + assert_raise(JSON::ParserError) { JSON.parse('NaN', :allow_nan => true) } + assert JSON.parse('NaN', :quirks_mode => true, :allow_nan => true).nan? + assert_raise(JSON::ParserError) { JSON.parse('Infinity', :allow_nan => true) } + assert JSON.parse('Infinity', :quirks_mode => true, :allow_nan => true).infinite? + assert_raise(JSON::ParserError) { JSON.parse('-Infinity', :allow_nan => true) } + assert JSON.parse('-Infinity', :quirks_mode => true, :allow_nan => true).infinite? + assert_raise(JSON::ParserError) { JSON.parse('[ 1, ]', :quirks_mode => true) } + end + if Array.method_defined?(:permutation) def test_parse_more_complex_arrays a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] diff --git a/tests/test_json_addition.rb b/tests/test_json_addition.rb index a8181e8..9f578a4 100755 --- a/tests/test_json_addition.rb +++ b/tests/test_json_addition.rb @@ -3,7 +3,9 @@ require 'test/unit' require File.join(File.dirname(__FILE__), 'setup_variant') -load 'json/add/core.rb' +require 'json/add/core' +require 'json/add/complex' +require 'json/add/rational' require 'date' class TC_JSONAddition < Test::Unit::TestCase @@ -164,4 +166,9 @@ class TC_JSONAddition < Test::Unit::TestCase d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(12,24)) assert_equal d, JSON.parse(d.to_json) end + + def test_rational_complex + assert_equal Rational(2, 9), JSON(JSON(Rational(2, 9))) + assert_equal Complex(2, 9), JSON(JSON(Complex(2, 9))) + end end diff --git a/tests/test_json_generate.rb b/tests/test_json_generate.rb index 9b0cff4..da96603 100755 --- a/tests/test_json_generate.rb +++ b/tests/test_json_generate.rb @@ -50,6 +50,7 @@ EOT parsed_json = parse(json) assert_equal({"1"=>2}, parsed_json) assert_raise(GeneratorError) { generate(666) } + assert_equal '666', generate(666, :quirks_mode => true) end def test_generate_pretty @@ -67,6 +68,7 @@ EOT parsed_json = parse(json) assert_equal({"1"=>2}, parsed_json) assert_raise(GeneratorError) { pretty_generate(666) } + assert_equal '666', pretty_generate(666, :quirks_mode => true) end def test_fast_generate @@ -79,9 +81,24 @@ EOT parsed_json = parse(json) assert_equal({"1"=>2}, parsed_json) assert_raise(GeneratorError) { fast_generate(666) } + assert_equal '666', fast_generate(666, :quirks_mode => true) end - + def test_own_state + state = State.new + json = generate(@hash, state) + assert_equal(JSON.parse(@json2), JSON.parse(json)) + parsed_json = parse(json) + assert_equal(@hash, parsed_json) + json = generate({1=>2}, state) + assert_equal('{"1":2}', json) + parsed_json = parse(json) + assert_equal({"1"=>2}, parsed_json) + assert_raise(GeneratorError) { generate(666, state) } + state.quirks_mode = true + assert state.quirks_mode? + assert_equal '666', generate(666, state) + end def test_states json = generate({1=>2}, nil) @@ -107,6 +124,7 @@ EOT :allow_nan => false, :array_nl => "\n", :ascii_only => false, + :quirks_mode => false, :depth => 0, :indent => " ", :max_nesting => 19, @@ -122,6 +140,7 @@ EOT :allow_nan => false, :array_nl => "", :ascii_only => false, + :quirks_mode => false, :depth => 0, :indent => "", :max_nesting => 19, @@ -137,6 +156,7 @@ EOT :allow_nan => false, :array_nl => "", :ascii_only => false, + :quirks_mode => false, :depth => 0, :indent => "", :max_nesting => 0, @@ -177,4 +197,17 @@ EOT assert_raises(JSON::NestingError) { ary.to_json(s) } assert_equal 19, s.depth end + + def test_gc + bignum_too_long_to_embed_as_string = 1234567890123456789012345 + expect = bignum_too_long_to_embed_as_string.to_s + stress, GC.stress = GC.stress, true + + 10.times do |i| + tmp = bignum_too_long_to_embed_as_string.to_json + assert_equal expect, tmp + end + ensure + GC.stress = stress + end if GC.respond_to?(:stress=) end diff --git a/tools/server.rb b/tools/server.rb index 084377f..184a01c 100755 --- a/tools/server.rb +++ b/tools/server.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# encoding: utf-8 require 'webrick' include WEBrick |