/**
 *  Copyright 2006- Yonik Seeley
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.noggit;

import java.io.IOException;
import java.io.StringReader;
import java.util.Map;
import java.util.Random;

import junit.framework.TestCase;

/**
 * @author yonik
 * @version $Id: TestJSONParser.java 1099557 2011-05-04 18:54:26Z yonik $
 */
public class TestJSONParser extends TestCase {

  public static Random r = new Random();

  // these are to aid in debugging if an unexpected error occurs
  static int parserType;
  static int bufferSize;
  static String parserInput;
  static JSONParser lastParser;

  static int flags = JSONParser.FLAGS_DEFAULT;  // the default

  public static String lastParser() {
    return "parserType=" + parserType
            + (parserType==1 ? " bufferSize=" + bufferSize : "")
            + " parserInput='" + parserInput + "'" + "flags : " + lastParser.flags;
  }

  public static JSONParser getParser(String s) {
    return getParser(s, r.nextInt(2), -1);
  }

  public static JSONParser getParser(String s, int type, int bufSize) {
    parserInput = s;
    parserType = type;

    JSONParser parser=null;
    switch (type) {
      case 0:
        // test directly using input buffer
        parser = new JSONParser(s.toCharArray(),0,s.length());
        break;
      case 1:
        // test using Reader...
        // small input buffers can help find bugs on boundary conditions

        if (bufSize < 1) bufSize = r.nextInt(25) + 1;
        bufferSize = bufSize;// record in case there is an error
        parser = new JSONParser(new StringReader(s), new char[bufSize]);
        break;
    }
    if (parser == null) return null;

    lastParser = parser;

    if (flags != JSONParser.FLAGS_DEFAULT) {
      parser.setFlags(flags);
    }

    return parser;
  }

  /** for debugging purposes
   public void testSpecific() throws Exception {
   JSONParser parser = getParser("[0",1,1);
   for (;;) {
   int ev = parser.nextEvent();
   if (ev == JSONParser.EOF) {
   break;
   } else {
   System.out.println("got " + JSONParser.getEventString(ev));
   }
   }
   }
   **/

  public static byte[] events = new byte[256];
  static {
    events['{'] = JSONParser.OBJECT_START;
    events['}'] = JSONParser.OBJECT_END;
    events['['] = JSONParser.ARRAY_START;
    events[']'] = JSONParser.ARRAY_END;
    events['s'] = JSONParser.STRING;
    events['b'] = JSONParser.BOOLEAN;
    events['l'] = JSONParser.LONG;
    events['n'] = JSONParser.NUMBER;
    events['N'] = JSONParser.BIGNUMBER;
    events['0'] = JSONParser.NULL;
    events['e'] = JSONParser.EOF;
  }

  // match parser states with the expected states
  public static void parse(JSONParser p, String input, String expected) throws IOException {
    expected += "e";
    for (int i=0; i<expected.length(); i++) {
      int ev = p.nextEvent();
      int expect = events[expected.charAt(i)];
      if (ev != expect) {
        fail("Expected " + expect + ", got " + ev
                + "\n\tINPUT=" + input
                + "\n\tEXPECTED=" + expected
                + "\n\tAT=" + i + " (" + expected.charAt(i) + ")");
      }
    }
  }

  public static void parse(String input, String expected) throws IOException {
    String in = input;
    if ((flags & JSONParser.ALLOW_SINGLE_QUOTES)==0 || r.nextBoolean()) {
      in = in.replace('\'', '"');
    }

    for (int i=0; i<Integer.MAX_VALUE; i++) {
      JSONParser p = getParser(in,i,-1);
      if (p==null) break;
      parse(p,in,expected);
    }

    testCorruption(input, 100000);

  }

  public static void testCorruption(String input, int iter) {
    char[] arr = new char[input.length()];

    for (int i=0; i<iter; i++) {
      input.getChars(0, arr.length, arr, 0);
      int changes = r.nextInt(arr.length>>1) + 1;
      for (int j=0; j<changes; j++) {
        char ch;
        switch (r.nextInt(31)) {
          case 0: ch = 0; break;
          case 1: ch = '['; break;
          case 2: ch = ']'; break;
          case 3: ch = '{'; break;
          case 4: ch = '}'; break;
          case 5: ch = '"'; break;
          case 6: ch = '\''; break;
          case 7: ch = ' '; break;
          case 8: ch = '\r'; break;
          case 9: ch = '\n'; break;
          case 10:ch = '\t'; break;
          case 11:ch = ','; break;
          case 12:ch = ':'; break;
          case 13:ch = '.'; break;
          case 14:ch = 'a'; break;
          case 15:ch = 'e'; break;
          case 16:ch = '0'; break;
          case 17:ch = '1'; break;
          case 18:ch = '+'; break;
          case 19:ch = '-'; break;
          case 20:ch = 't'; break;
          case 21:ch = 'f'; break;
          case 22:ch = 'n'; break;
          case 23:ch = '/'; break;
          case 24:ch = '\\'; break;
          case 25:ch = 'u'; break;
          case 26:ch = '\u00a0'; break;
          default:ch = (char)r.nextInt(256);
        }

        arr[r.nextInt(arr.length)] = ch;
      }


      JSONParser parser = getParser(new String(arr));
      parser.setFlags( r.nextInt() );  // set random parser flags

      int ret = 0;
      try {
        for (;;) {
          int ev = parser.nextEvent();
          if (r.nextBoolean()) {
            // see if we can read the event
            switch (ev) {
              case JSONParser.STRING: ret += parser.getString().length(); break;
              case JSONParser.BOOLEAN: ret += parser.getBoolean() ? 1 : 2; break;
              case JSONParser.BIGNUMBER: ret += parser.getNumberChars().length(); break;
              case JSONParser.NUMBER: ret += parser.getDouble(); break;
              case JSONParser.LONG: ret += parser.getLong(); break;
              default: ret += ev;
            }
          }

          if (ev == JSONParser.EOF) break;
        }
      } catch (IOException ex) {
        // shouldn't happen
        System.out.println(ret);  // use ret
      } catch (JSONParser.ParseException ex) {
        // OK
      } catch (Throwable ex) {
        ex.printStackTrace();
        System.out.println(lastParser());
        throw new RuntimeException(ex);
      }
    }
  }



  public static class Num {
    public String digits;
    public Num(String digits) {
      this.digits = digits;
    }
    @Override
    public String toString() { return new String("NUMBERSTRING("+digits+")"); }
    @Override
    public boolean equals(Object o) {
      return (getClass() == o.getClass() && digits.equals(((Num) o).digits));
    }
    @Override
    public int hashCode() {
      return digits.hashCode();
    }
  }

  public static class BigNum extends Num {
    @Override
    public String toString() { return new String("BIGNUM("+digits+")"); }
    public BigNum(String digits) { super(digits); }
  }

  // Oh, what I wouldn't give for Java5 varargs and autoboxing
  public static Long o(int l) { return new Long(l); }
  public static Long o(long l) { return new Long(l); }
  public static Double o(double d) { return new Double(d); }
  public static Boolean o(boolean b) { return new Boolean(b); }
  public static Num n(String digits) { return new Num(digits); }
  public static Num bn(String digits) { return new BigNum(digits); }
  public static Object t = new Boolean(true);
  public static Object f = new Boolean(false);
  public static Object a = new Object(){@Override
                                        public String toString() {return "ARRAY_START";}};
  public static Object A = new Object(){@Override
                                        public String toString() {return "ARRAY_END";}};
  public static Object m = new Object(){@Override
                                        public String toString() {return "OBJECT_START";}};
  public static Object M = new Object(){@Override
                                        public String toString() {return "OBJECT_END";}};
  public static Object N = new Object(){@Override
                                        public String toString() {return "NULL";}};
  public static Object e = new Object(){@Override
                                        public String toString() {return "EOF";}};

  // match parser states with the expected states
  public static void parse(JSONParser p, String input, Object[] expected) throws IOException {
    for (int i=0; i<expected.length; i++) {
      int ev = p.nextEvent();
      Object exp = expected[i];
      Object got = null;

      switch(ev) {
        case JSONParser.ARRAY_START: got=a; break;
        case JSONParser.ARRAY_END: got=A; break;
        case JSONParser.OBJECT_START: got=m; break;
        case JSONParser.OBJECT_END: got=M; break;
        case JSONParser.LONG: got=o(p.getLong()); break;
        case JSONParser.NUMBER:
          if (exp instanceof Double) {
            got = o(p.getDouble());
          } else {
            got = n(p.getNumberChars().toString());
          }
          break;
        case JSONParser.BIGNUMBER: got=bn(p.getNumberChars().toString()); break;
        case JSONParser.NULL: got=N; p.getNull(); break; // optional
        case JSONParser.BOOLEAN: got=o(p.getBoolean()); break;
        case JSONParser.EOF: got=e; break;
        case JSONParser.STRING: got=p.getString(); break;
        default: got="Unexpected Event Number " + ev;
      }

      if (!(exp==got || exp.equals(got))) {
        fail("Fail: String='" + input + "'" + "\n\tINPUT=" + got + "\n\tEXPECTED=" + exp + "\n\tAT RULE " + i);
      }
    }
  }


  public static void parse(String input, Object[] expected) throws IOException {
    parse(input, (flags & JSONParser.ALLOW_SINGLE_QUOTES)==0 || r.nextBoolean(), expected);
  }

  public static void parse(String input, boolean changeSingleQuote, Object[] expected) throws IOException {
    String in = input;
    if (changeSingleQuote) {
      in = in.replace('\'', '"');
    }
    for (int i=0; i<Integer.MAX_VALUE; i++) {
      JSONParser p = getParser(in,i,-1);
      if (p == null) break;
      parse(p,in,expected);
    }
  }


  public static void err(String input) throws IOException {
    try {
      JSONParser p = getParser(input);
      while (p.nextEvent() != JSONParser.EOF) {}
    } catch (Exception e) {
      return;
    }
    fail("Input should failed:'" + input + "'");
  }

  public void testNull() throws IOException {
    flags = JSONParser.FLAGS_STRICT;
    err("nul");
    err("n");
    err("nullz");
    err("[nullz]");
    flags = JSONParser.FLAGS_DEFAULT;

    parse("[null]","[0]");
    parse("{'hi':null}",new Object[]{m,"hi",N,M,e});
  }

  public void testBool() throws IOException {
    flags = JSONParser.FLAGS_STRICT;
    err("[True]");
    err("[False]");
    err("[TRUE]");
    err("[FALSE]");
    err("[truex]");
    err("[falsex]");
    err("[tru]");
    err("[fals]");
    err("[tru");
    err("[fals");
    err("t");
    err("f");
    flags = JSONParser.FLAGS_DEFAULT;

    parse("[false,true, false , true ]",new Object[]{a,f,t,f,t,A,e});
  }

  public void testString() throws IOException {
    // NOTE: single quotes are converted to double quotes by this
    // testsuite!
    err("[']");
    err("[',]");
    err("{'}");
    err("{',}");

    err("['\\u111']");
    err("['\\u11']");
    err("['\\u1']");
    err("['\\']");


    flags = JSONParser.FLAGS_STRICT;
    err("['\\ ']");  // escape of non-special char
    err("['\\U1111']");  // escape of non-special char
    flags = JSONParser.FLAGS_DEFAULT;

    parse("['\\ ']", new Object[]{a, " ", A, e});  // escape of non-special char
    parse("['\\U1111']", new Object[]{a, "U1111", A, e});  // escape of non-special char



    parse("['']",new Object[]{a,"",A,e});
    parse("['\\\\']",new Object[]{a,"\\",A,e});
    parse("['X\\\\']",new Object[]{a,"X\\",A,e});
    parse("['\\\\X']",new Object[]{a,"\\X",A,e});
    parse("[\"\\\"\"]",new Object[]{a,"\"",A,e});

    parse("['\\'']", true, new Object[]{a,"\"",A,e});
    parse("['\\'']", false, new Object[]{a,"'",A,e});


    String esc="\\n\\r\\tX\\b\\f\\/\\\\X\\\"";
    String exp="\n\r\tX\b\f/\\X\"";
    parse("['" + esc + "']",new Object[]{a,exp,A,e});
    parse("['" + esc+esc+esc+esc+esc + "']",new Object[]{a,exp+exp+exp+exp+exp,A,e});

    esc="\\u004A";
    exp="\u004A";
    parse("['" + esc + "']",new Object[]{a,exp,A,e});

    esc="\\u0000\\u1111\\u2222\\u12AF\\u12BC\\u19DE";
    exp="\u0000\u1111\u2222\u12AF\u12BC\u19DE";
    parse("['" + esc + "']",new Object[]{a,exp,A,e});

  }

  public void testNumbers() throws IOException {
    flags = JSONParser.FLAGS_STRICT;

    err("[00]");
    err("[003]");
    err("[00.3]");
    err("[1e1.1]");
    err("[+1]");
    err("[NaN]");
    err("[Infinity]");
    err("[--1]");

    flags = JSONParser.FLAGS_DEFAULT;

    String lmin    = "-9223372036854775808";
    String lminNot = "-9223372036854775809";
    String lmax    = "9223372036854775807";
    String lmaxNot = "9223372036854775808";

    String bignum="12345678987654321357975312468642099775533112244668800152637485960987654321";

    parse("[0,1,-1,543,-876]", new Object[]{a,o(0),o(1),o(-1),o(543),o(-876),A,e});
    parse("[-0]",new Object[]{a,o(0),A,e});


    parse("["+lmin +"," + lmax+"]",
            new Object[]{a,o(Long.MIN_VALUE),o(Long.MAX_VALUE),A,e});

    parse("["+bignum+"]", new Object[]{a,bn(bignum),A,e});
    parse("["+"-"+bignum+"]", new Object[]{a,bn("-"+bignum),A,e});

    parse("["+lminNot+"]",new Object[]{a,bn(lminNot),A,e});
    parse("["+lmaxNot+"]",new Object[]{a,bn(lmaxNot),A,e});

    parse("["+lminNot + "," + lmaxNot + "]",
            new Object[]{a,bn(lminNot),bn(lmaxNot),A,e});

    // bignum many digits on either side of decimal
    String t = bignum + "." + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});
    err("[" + t+".1" + "]"); // extra decimal
    err("[" + "-"+t+".1" + "]");

    // bignum exponent w/o fraction
    t = "1" + "e+" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});
    t = "1" + "E+" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});
    t = "1" + "e" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});
    t = "1" + "E" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});
    t = "1" + "e-" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});
    t = "1" + "E-" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});

    t = bignum + "e+" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});
    t = bignum + "E-" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});
    t = bignum + "e" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});

    t = bignum + "." + bignum + "e" + bignum;
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});

    err("[1E]");
    err("[1E-]");
    err("[1E+]");
    err("[1E+.3]");
    err("[1E+0.3]");
    err("[1E+1e+3]");
    err("["+bignum+"e"+"]");
    err("["+bignum+"e-"+"]");
    err("["+bignum+"e+"+"]");
    err("["+bignum+"."+bignum+"."+bignum+"]");


    double[] vals = new double[] {0,0.1,1.1,
            Double.MAX_VALUE,
            Double.MIN_VALUE,
            2.2250738585072014E-308, /* Double.MIN_NORMAL */
    };
    for (int i=0; i<vals.length; i++) {
      double d = vals[i];
      parse("["+d+","+-d+"]", new Object[]{a,o(d),o(-d),A,e});
    }

    // MIN_NORMAL has the max number of digits (23), so check that
    // adding an extra digit causes BIGNUM to be returned.
    t = "2.2250738585072014E-308" + "0";
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});
    // check it works with a leading zero too
    t = "0.2250738585072014E-308" + "0";
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});

    // check that overflow detection is working properly w/ numbers that don't cause a wrap to negatives
    // when multiplied by 10
    t = "1910151821265210155" + "0";
    parse("["+t+","+"-"+t+"]", new Object[]{a,bn(t),bn("-"+t),A,e});

    for (int i=0; i<1000000; i++) {
      long val = r.nextLong();
      String sval = Long.toString(val);
      JSONParser parser = getParser("["+val+"]");
      parser.nextEvent();
      assertTrue(parser.nextEvent() == JSONParser.LONG);
      if (r.nextBoolean()) {
        assertEquals(val, parser.getLong());
      } else {
        CharArr chars = parser.getNumberChars();
        assertEquals(sval, chars.toString());
      }
    }

  }

  public void testArray() throws IOException {
    parse("[]","[]");
    parse("[ ]","[]");
    parse(" \r\n\t[\r\t\n ]\r\n\t ","[]");

    parse("[0]","[l]");
    parse("['0']","[s]");
    parse("[0,'0',0.1]","[lsn]");

    parse("[[[[[]]]]]","[[[[[]]]]]");
    parse("[[[[[0]]]]]","[[[[[l]]]]]");

    err("]");
    err("[");
    err("[[]");
    err("[]]");
    err("[}");
    err("{]");
    err("['a':'b']");

    flags=JSONParser.FLAGS_STRICT;
    err("[,]");         // test that extra commas fail
    err("[[],]");
    err("['a',]");
    err("['a',]");
    flags=JSONParser.FLAGS_DEFAULT;

    parse("[,]","[]");  // test extra commas
    parse("[,,]","[]");
    parse("[,,,]","[]");
    parse("[[],]","[[]]");
    parse("[[,],]","[[]]");
    parse("[[,,],,]","[[]]");
    parse("[,[,,],,]","[[]]");
    parse("[,5,[,,5],,]","[l[l]]");

  }

  public void testObject() throws IOException {
    parse("{}","{}");
    parse("{}","{}");
    parse(" \r\n\t{\r\t\n }\r\n\t ","{}");

    parse("{'':null}","{s0}");

    err("}");
    err("[}]");
    err("{");
    err("[{]");
    err("{{}");
    err("[{{}]");
    err("{}}");
    err("[{}}]");
    err("{1}");
    err("[{1}]");
    err("{'a'}");
    err("{'a','b'}");
    err("{[]:'b'}");
    err("{{'a':'b'}:'c'}");
    err("{'a','b'}}");

    // bare strings allow these to pass
    flags=JSONParser.FLAGS_STRICT;
    err("{null:'b'}");
    err("{true:'b'}");
    err("{false:'b'}");
    err("{,}");         // test that extra commas fail
    err("{{},}");
    err("{'a':'b',}");
    flags=JSONParser.FLAGS_DEFAULT;

    parse("{}", new Object[]{m,M,e});
    parse("{,}", new Object[]{m,M,e});
    parse("{,,}", new Object[]{m,M,e});
    parse("{'a':{},}", new Object[]{m,"a",m,M,M,e});
    parse("{'a':{},,}", new Object[]{m,"a",m,M,M,e});
    parse("{,'a':{,},,}", new Object[]{m,"a",m,M,M,e});
    parse("{'a':'b'}", new Object[]{m,"a","b",M,e});
    parse("{'a':5}", new Object[]{m,"a",o(5),M,e});
    parse("{'a':null}", new Object[]{m,"a",N,M,e});
    parse("{'a':[]}", new Object[]{m,"a",a,A,M,e});
    parse("{'a':{'b':'c'}}", new Object[]{m,"a",m,"b","c",M,M,e});

    String big = "Now is the time for all good men to come to the aid of their country!";
    String bigger = big+big+big+big+big;
    parse("{'"+bigger+"':'"+bigger+"','a':'b'}", new Object[]{m,bigger,bigger,"a","b",M,e});


    flags=JSONParser.ALLOW_UNQUOTED_KEYS;
    parse("{a:'b'}", new Object[]{m,"a","b",M,e});
    parse("{null:'b'}", new Object[]{m,"null","b",M,e});
    parse("{true: 'b'}", new Object[]{m,"true","b",M,e});
    parse("{ false :'b'}", new Object[]{m,"false","b",M,e});
    parse("{null:null, true : true , false : false , x:'y',a:'b'}", new Object[]{m,"null",N,"true",t,"false",f,"x","y","a","b",M,e});
    flags = JSONParser.FLAGS_DEFAULT | JSONParser.ALLOW_MISSING_COLON_COMMA_BEFORE_OBJECT;
    parse("{'a'{'b':'c'}}", new Object[]{m, "a", m, "b", "c", M, M, e});
    parse("{'a': [{'b':'c'} {'b':'c'}]}", new Object[]{m, "a",a, m, "b", "c", M, m, "b", "c", M,A, M, e});
    parse("{'a' [{'b':'c'} {'b':'c'}]}", new Object[]{m, "a", a, m, "b", "c", M, m, "b", "c", M, A, M, e});
    parse("{'a':[['b']['c']]}", new Object[]{m, "a", a, a, "b", A, a, "c", A, A, M, e});
    parse("{'a': {'b':'c'} 'd': {'e':'f'}}", new Object[]{m, "a", m, "b", "c",M,  "d", m,"e","f", M, M, e});
    parse("{'a': {'b':'c'} d: {'e':'f'}}", new Object[]{m, "a", m, "b", "c",M,  "d", m,"e","f", M, M, e});

    flags = JSONParser.FLAGS_DEFAULT | JSONParser.ALLOW_MISSING_COLON_COMMA_BEFORE_OBJECT | JSONParser.OPTIONAL_OUTER_BRACES;
    parse("'a':{'b':'c'}", new Object[]{m, "a", m, "b", "c", M, M, e});
    parse("'a':{'b':'c'}", true, new Object[]{m, "a", m, "b", "c", M, M, e});
    parse("a:'b'", new Object[]{m, "a", "b", M, e});


    flags = JSONParser.FLAGS_DEFAULT;

  }

  public void testBareString() throws Exception {
    flags=JSONParser.ALLOW_UNQUOTED_STRING_VALUES | JSONParser.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER;
    String[] strings = new String[] {"t","f","n","a","tru","fals","nul","abc","trueX","falseXY","nullZ","truetrue", "$true", "a.bc.def","a_b-c/d"};

    for (String s : strings) {
      parse(s, new Object[]{s, e});
      parse("[" + s + "]", new Object[]{a, s, A, e});
      parse("[ " + s + ", "+s +" ]", new Object[]{a, s, s, A, e});
      parse("[" + s + ","+s +"]",    new Object[]{a, s, s, A, e});
      parse("\u00a0[\u00a0\r\n\t\u00a0" + s + "\u00a0,\u00a0\u00a0"+s +"\u00a0]\u00a0",    new Object[]{a, s, s, A, e});
    }

    flags |= JSONParser.ALLOW_UNQUOTED_KEYS;
    for (String s : strings) {
      parse("{" + s + ":" + s + "}", new Object[]{m, s, s, M, e});
      parse("{ " + s + " \t\n\r:\t\n\r " + s + "\t\n\r}", new Object[]{m, s, s, M, e});
    }

    parse("{true:true, false:false, null:null}",new Object[]{m,"true",t,"false",f,"null",N,M,e});

    flags=JSONParser.FLAGS_DEFAULT;
  }



  public void testAPI() throws IOException {
    JSONParser p = new JSONParser("[1,2]");
    assertEquals(JSONParser.ARRAY_START, p.nextEvent());
    // no nextEvent for the next objects...
    assertEquals(1,p.getLong());
    assertEquals(2,p.getLong());
  }


  public void testComments() throws IOException {
    parse("#pre comment\n{//before a\n  'a' /* after a **/ #before separator\n  : /* after separator */ {/*before b*/'b'#after b\n://before c\n'c'/*after c*/}/*after close*/}#post comment no EOL", new Object[]{m,"a",m,"b","c",M,M,e});
  }

  // rfc7159 permits any JSON values to be top level values
  public void testTopLevelValues() throws Exception {
    parse("\"\"", new Object[]{""});
    parse("\"hello\"", new Object[]{"hello"});
    parse("true", new Object[]{t});
    parse("false", new Object[]{f});
    parse("null", new Object[]{N});
    parse("42", new Object[]{42L});
    parse("1.414", new Object[]{1.414d});
    parse("/*comment*/1.414/*more comment*/", new Object[]{1.414d});
  }
}
