Example #1
0
  /* ------------------------------------------------------------ */
  protected Object parseObject(Source source) {
    if (source.next() != '{') throw new IllegalStateException();
    Map<String, Object> map = newMap();

    char next = seekTo("\"}", source);

    while (source.hasNext()) {
      if (next == '}') {
        source.next();
        break;
      }

      String name = parseString(source);
      seekTo(':', source);
      source.next();

      Object value = contextFor(name).parse(source);
      map.put(name, value);

      seekTo(",}", source);
      next = source.next();
      if (next == '}') break;
      else next = seekTo("\"}", source);
    }

    String classname = (String) map.get("class");
    if (classname != null) {
      try {
        Class c = Loader.loadClass(JSON.class, classname);
        return convertTo(c, map);
      } catch (ClassNotFoundException e) {
        LOG.warn(e);
      }
    }
    return map;
  }
Example #2
0
/**
 * JSON Parser and Generator.
 *
 * <p>This class provides some static methods to convert POJOs to and from JSON notation. The
 * mapping from JSON to java is:
 *
 * <pre>
 *   object ==> Map
 *   array  ==> Object[]
 *   number ==> Double or Long
 *   string ==> String
 *   null   ==> null
 *   bool   ==> Boolean
 * </pre>
 *
 * <p>The java to JSON mapping is:
 *
 * <pre>
 *   String --> string
 *   Number --> number
 *   Map    --> object
 *   List   --> array
 *   Array  --> array
 *   null   --> null
 *   Boolean--> boolean
 *   Object --> string (dubious!)
 * </pre>
 *
 * <p>The interface {@link JSON.Convertible} may be implemented by classes that wish to externalize
 * and initialize specific fields to and from JSON objects. Only directed acyclic graphs of objects
 * are supported.
 *
 * <p>The interface {@link JSON.Generator} may be implemented by classes that know how to render
 * themselves as JSON and the {@link #toString(Object)} method will use {@link
 * JSON.Generator#addJSON(Appendable)} to generate the JSON. The class {@link JSON.Literal} may be
 * used to hold pre-generated JSON object.
 *
 * <p>The interface {@link JSON.Convertor} may be implemented to provide static convertors for
 * objects that may be registered with {@link #registerConvertor(Class,
 * org.eclipse.jetty.util.ajax.JSON.Convertor)} . These convertors are looked up by class, interface
 * and super class by {@link #getConvertor(Class)}.
 */
@SuppressWarnings({"unchecked", "restriction", "rawtypes", "nls"})
public class JSON {
  static final RedirectToAptanaLog LOG = RedirectToAptanaLog.getSingleton();
  public static final JSON DEFAULT = new JSON();

  private Map<String, Convertor> _convertors = new ConcurrentHashMap<String, Convertor>();
  private int _stringBufferSize = 1024;

  public JSON() {}

  /* ------------------------------------------------------------ */
  /** @return the initial stringBuffer size to use when creating JSON strings (default 1024) */
  public int getStringBufferSize() {
    return _stringBufferSize;
  }

  /* ------------------------------------------------------------ */
  /**
   * @param stringBufferSize the initial stringBuffer size to use when creating JSON strings
   *     (default 1024)
   */
  public void setStringBufferSize(int stringBufferSize) {
    _stringBufferSize = stringBufferSize;
  }

  /* ------------------------------------------------------------ */
  /**
   * Register a {@link Convertor} for a class or interface.
   *
   * @param forClass The class or interface that the convertor applies to
   * @param convertor the convertor
   */
  public static void registerConvertor(Class forClass, Convertor convertor) {
    DEFAULT.addConvertor(forClass, convertor);
  }

  /* ------------------------------------------------------------ */
  public static JSON getDefault() {
    return DEFAULT;
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public static void setDefault(JSON json) {}

  /* ------------------------------------------------------------ */
  public static String toString(Object object) {
    StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
    DEFAULT.append(buffer, object);
    return buffer.toString();
  }

  /* ------------------------------------------------------------ */
  public static String toString(Map object) {
    StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
    DEFAULT.appendMap(buffer, object);
    return buffer.toString();
  }

  /* ------------------------------------------------------------ */
  public static String toString(Object[] array) {
    StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
    DEFAULT.appendArray(buffer, array);
    return buffer.toString();
  }

  /* ------------------------------------------------------------ */
  /**
   * @param s String containing JSON object or array.
   * @return A Map, Object array or primitive array parsed from the JSON.
   */
  public static Object parse(String s) {
    return DEFAULT.parse(new StringSource(s), false);
  }

  /* ------------------------------------------------------------ */
  /**
   * @param s String containing JSON object or array.
   * @param stripOuterComment If true, an outer comment around the JSON is ignored.
   * @return A Map, Object array or primitive array parsed from the JSON.
   */
  public static Object parse(String s, boolean stripOuterComment) {
    return DEFAULT.parse(new StringSource(s), stripOuterComment);
  }

  /* ------------------------------------------------------------ */
  /**
   * @param in Reader containing JSON object or array.
   * @return A Map, Object array or primitive array parsed from the JSON.
   */
  public static Object parse(Reader in) throws IOException {
    return DEFAULT.parse(new ReaderSource(in), false);
  }

  /* ------------------------------------------------------------ */
  /**
   * @param in Reader containing JSON object or array.
   * @param stripOuterComment If true, an outer comment around the JSON is ignored.
   * @return A Map, Object array or primitive array parsed from the JSON.
   */
  public static Object parse(Reader in, boolean stripOuterComment) throws IOException {
    return DEFAULT.parse(new ReaderSource(in), stripOuterComment);
  }

  /* ------------------------------------------------------------ */
  /**
   * @deprecated use {@link #parse(Reader)}
   * @param in Reader containing JSON object or array.
   * @return A Map, Object array or primitive array parsed from the JSON.
   */
  @Deprecated
  public static Object parse(InputStream in) throws IOException {
    return DEFAULT.parse(new StringSource(IO.toString(in)), false);
  }

  /* ------------------------------------------------------------ */
  /**
   * @deprecated use {@link #parse(Reader, boolean)}
   * @param in Stream containing JSON object or array.
   * @param stripOuterComment If true, an outer comment around the JSON is ignored.
   * @return A Map, Object array or primitive array parsed from the JSON.
   */
  @Deprecated
  public static Object parse(InputStream in, boolean stripOuterComment) throws IOException {
    return DEFAULT.parse(new StringSource(IO.toString(in)), stripOuterComment);
  }

  /* ------------------------------------------------------------ */
  /**
   * Convert Object to JSON
   *
   * @param object The object to convert
   * @return The JSON String
   */
  public String toJSON(Object object) {
    StringBuilder buffer = new StringBuilder(getStringBufferSize());
    append(buffer, object);
    return buffer.toString();
  }

  /* ------------------------------------------------------------ */
  /**
   * Convert JSON to Object
   *
   * @param json The json to convert
   * @return The object
   */
  public Object fromJSON(String json) {
    Source source = new StringSource(json);
    return parse(source);
  }

  @Deprecated
  public void append(StringBuffer buffer, Object object) {
    append((Appendable) buffer, object);
  }

  /* ------------------------------------------------------------ */
  /**
   * Append object as JSON to string buffer.
   *
   * @param buffer the buffer to append to
   * @param object the object to append
   */
  public void append(Appendable buffer, Object object) {
    try {
      if (object == null) buffer.append("null");
      else if (object instanceof Convertible) appendJSON(buffer, (Convertible) object);
      else if (object instanceof Generator) appendJSON(buffer, (Generator) object);
      else if (object instanceof Map) appendMap(buffer, (Map) object);
      else if (object instanceof Collection) appendArray(buffer, (Collection) object);
      else if (object.getClass().isArray()) appendArray(buffer, object);
      else if (object instanceof Number) appendNumber(buffer, (Number) object);
      else if (object instanceof Boolean) appendBoolean(buffer, (Boolean) object);
      else if (object instanceof Character) appendString(buffer, object.toString());
      else if (object instanceof String) appendString(buffer, (String) object);
      else {
        Convertor convertor = getConvertor(object.getClass());
        if (convertor != null) appendJSON(buffer, convertor, object);
        else appendString(buffer, object.toString());
      }
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendNull(StringBuffer buffer) {
    appendNull((Appendable) buffer);
  }

  /* ------------------------------------------------------------ */
  public void appendNull(Appendable buffer) {
    try {
      buffer.append("null");
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendJSON(
      final StringBuffer buffer, final Convertor convertor, final Object object) {
    appendJSON((Appendable) buffer, convertor, object);
  }

  /* ------------------------------------------------------------ */
  public void appendJSON(final Appendable buffer, final Convertor convertor, final Object object) {
    appendJSON(
        buffer,
        new Convertible() {
          public void fromJSON(Map object) {}

          public void toJSON(Output out) {
            convertor.toJSON(object, out);
          }
        });
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendJSON(final StringBuffer buffer, Convertible converter) {
    appendJSON((Appendable) buffer, converter);
  }

  /* ------------------------------------------------------------ */
  public void appendJSON(final Appendable buffer, Convertible converter) {
    ConvertableOutput out = new ConvertableOutput(buffer);
    converter.toJSON(out);
    out.complete();
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendJSON(StringBuffer buffer, Generator generator) {
    generator.addJSON(buffer);
  }

  /* ------------------------------------------------------------ */
  public void appendJSON(Appendable buffer, Generator generator) {
    generator.addJSON(buffer);
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendMap(StringBuffer buffer, Map<?, ?> map) {
    appendMap((Appendable) buffer, map);
  }

  /* ------------------------------------------------------------ */
  public void appendMap(Appendable buffer, Map<?, ?> map) {
    try {
      if (map == null) {
        appendNull(buffer);
        return;
      }

      buffer.append('{');
      Iterator<?> iter = map.entrySet().iterator();
      while (iter.hasNext()) {
        Map.Entry<?, ?> entry = (Map.Entry<?, ?>) iter.next();
        QuotedStringTokenizer.quote(buffer, entry.getKey().toString());
        buffer.append(':');
        append(buffer, entry.getValue());
        if (iter.hasNext()) buffer.append(',');
      }

      buffer.append('}');
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendArray(StringBuffer buffer, Collection collection) {
    appendArray((Appendable) buffer, collection);
  }

  /* ------------------------------------------------------------ */
  public void appendArray(Appendable buffer, Collection collection) {
    try {
      if (collection == null) {
        appendNull(buffer);
        return;
      }

      buffer.append('[');
      Iterator iter = collection.iterator();
      boolean first = true;
      while (iter.hasNext()) {
        if (!first) buffer.append(',');

        first = false;
        append(buffer, iter.next());
      }

      buffer.append(']');
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendArray(StringBuffer buffer, Object array) {
    appendArray((Appendable) buffer, array);
  }

  /* ------------------------------------------------------------ */
  public void appendArray(Appendable buffer, Object array) {
    try {
      if (array == null) {
        appendNull(buffer);
        return;
      }

      buffer.append('[');
      int length = Array.getLength(array);

      for (int i = 0; i < length; i++) {
        if (i != 0) buffer.append(',');
        append(buffer, Array.get(array, i));
      }

      buffer.append(']');
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendBoolean(StringBuffer buffer, Boolean b) {
    appendBoolean((Appendable) buffer, b);
  }

  /* ------------------------------------------------------------ */
  public void appendBoolean(Appendable buffer, Boolean b) {
    try {
      if (b == null) {
        appendNull(buffer);
        return;
      }
      buffer.append(b.booleanValue() ? "true" : "false");
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendNumber(StringBuffer buffer, Number number) {
    appendNumber((Appendable) buffer, number);
  }

  /* ------------------------------------------------------------ */
  public void appendNumber(Appendable buffer, Number number) {
    try {
      if (number == null) {
        appendNull(buffer);
        return;
      }
      buffer.append(String.valueOf(number));
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /* ------------------------------------------------------------ */
  @Deprecated
  public void appendString(StringBuffer buffer, String string) {
    appendString((Appendable) buffer, string);
  }

  /* ------------------------------------------------------------ */
  public void appendString(Appendable buffer, String string) {
    if (string == null) {
      appendNull(buffer);
      return;
    }

    QuotedStringTokenizer.quote(buffer, string);
  }

  // Parsing utilities

  /* ------------------------------------------------------------ */
  protected String toString(char[] buffer, int offset, int length) {
    return new String(buffer, offset, length);
  }

  /* ------------------------------------------------------------ */
  protected Map<String, Object> newMap() {
    return new HashMap<String, Object>();
  }

  /* ------------------------------------------------------------ */
  protected Object[] newArray(int size) {
    return new Object[size];
  }

  /* ------------------------------------------------------------ */
  protected JSON contextForArray() {
    return this;
  }

  /* ------------------------------------------------------------ */
  protected JSON contextFor(String field) {
    return this;
  }

  /* ------------------------------------------------------------ */
  protected Object convertTo(Class type, Map map) {
    if (type != null && Convertible.class.isAssignableFrom(type)) {
      try {
        Convertible conv = (Convertible) type.newInstance();
        conv.fromJSON(map);
        return conv;
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    Convertor convertor = getConvertor(type);
    if (convertor != null) {
      return convertor.fromJSON(map);
    }
    return map;
  }

  /* ------------------------------------------------------------ */
  /**
   * Register a {@link Convertor} for a class or interface.
   *
   * @param forClass The class or interface that the convertor applies to
   * @param convertor the convertor
   */
  public void addConvertor(Class forClass, Convertor convertor) {
    _convertors.put(forClass.getName(), convertor);
  }

  /* ------------------------------------------------------------ */
  /**
   * Lookup a convertor for a class.
   *
   * <p>If no match is found for the class, then the interfaces for the class are tried. If still no
   * match is found, then the super class and it's interfaces are tried recursively.
   *
   * @param forClass The class
   * @return a {@link JSON.Convertor} or null if none were found.
   */
  protected Convertor getConvertor(Class forClass) {
    Class cls = forClass;
    Convertor convertor = _convertors.get(cls.getName());
    if (convertor == null && this != DEFAULT) convertor = DEFAULT.getConvertor(cls);

    while (convertor == null && cls != null && cls != Object.class) {
      Class[] ifs = cls.getInterfaces();
      int i = 0;
      while (convertor == null && ifs != null && i < ifs.length)
        convertor = _convertors.get(ifs[i++].getName());
      if (convertor == null) {
        cls = cls.getSuperclass();
        convertor = _convertors.get(cls.getName());
      }
    }
    return convertor;
  }

  /* ------------------------------------------------------------ */
  /**
   * Register a {@link JSON.Convertor} for a named class or interface.
   *
   * @param name name of a class or an interface that the convertor applies to
   * @param convertor the convertor
   */
  public void addConvertorFor(String name, Convertor convertor) {
    _convertors.put(name, convertor);
  }

  /* ------------------------------------------------------------ */
  /**
   * Lookup a convertor for a named class.
   *
   * @param name name of the class
   * @return a {@link JSON.Convertor} or null if none were found.
   */
  public Convertor getConvertorFor(String name) {
    String clsName = name;
    Convertor convertor = _convertors.get(clsName);
    if (convertor == null && this != DEFAULT) convertor = DEFAULT.getConvertorFor(clsName);
    return convertor;
  }

  /* ------------------------------------------------------------ */
  public Object parse(Source source, boolean stripOuterComment) {
    int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"
    if (!stripOuterComment) return parse(source);

    int strip_state = 1; // 0=no strip, 1=wait for /*, 2= wait for */

    Object o = null;
    while (source.hasNext()) {
      char c = source.peek();

      // handle // or /* comment
      if (comment_state == 1) {
        switch (c) {
          case '/':
            comment_state = -1;
            break;
          case '*':
            comment_state = 2;
            if (strip_state == 1) {
              comment_state = 0;
              strip_state = 2;
            }
        }
      }
      // handle /* */ comment
      else if (comment_state > 1) {
        switch (c) {
          case '*':
            comment_state = 3;
            break;
          case '/':
            if (comment_state == 3) {
              comment_state = 0;
              if (strip_state == 2) return o;
            } else comment_state = 2;
            break;
          default:
            comment_state = 2;
        }
      }
      // handle // comment
      else if (comment_state < 0) {
        switch (c) {
          case '\r':
          case '\n':
            comment_state = 0;
          default:
            break;
        }
      }
      // handle unknown
      else {
        if (!Character.isWhitespace(c)) {
          if (c == '/') comment_state = 1;
          else if (c == '*') comment_state = 3;
          else if (o == null) {
            o = parse(source);
            continue;
          }
        }
      }

      source.next();
    }

    return o;
  }

  /* ------------------------------------------------------------ */
  public Object parse(Source source) {
    int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"

    while (source.hasNext()) {
      char c = source.peek();

      // handle // or /* comment
      if (comment_state == 1) {
        switch (c) {
          case '/':
            comment_state = -1;
            break;
          case '*':
            comment_state = 2;
        }
      }
      // handle /* */ comment
      else if (comment_state > 1) {
        switch (c) {
          case '*':
            comment_state = 3;
            break;
          case '/':
            if (comment_state == 3) comment_state = 0;
            else comment_state = 2;
            break;
          default:
            comment_state = 2;
        }
      }
      // handle // comment
      else if (comment_state < 0) {
        switch (c) {
          case '\r':
          case '\n':
            comment_state = 0;
            break;
          default:
            break;
        }
      }
      // handle unknown
      else {
        switch (c) {
          case '{':
            return parseObject(source);
          case '[':
            return parseArray(source);
          case '"':
            return parseString(source);
          case '-':
            return parseNumber(source);

          case 'n':
            complete("null", source);
            return null;
          case 't':
            complete("true", source);
            return Boolean.TRUE;
          case 'f':
            complete("false", source);
            return Boolean.FALSE;
          case 'u':
            complete("undefined", source);
            return null;
          case 'N':
            complete("NaN", source);
            return null;

          case '/':
            comment_state = 1;
            break;

          default:
            if (Character.isDigit(c)) return parseNumber(source);
            else if (Character.isWhitespace(c)) break;
            return handleUnknown(source, c);
        }
      }
      source.next();
    }

    return null;
  }

  /* ------------------------------------------------------------ */
  protected Object handleUnknown(Source source, char c) {
    throw new IllegalStateException("unknown char '" + c + "'(" + (int) c + ") in " + source);
  }

  /* ------------------------------------------------------------ */
  protected Object parseObject(Source source) {
    if (source.next() != '{') throw new IllegalStateException();
    Map<String, Object> map = newMap();

    char next = seekTo("\"}", source);

    while (source.hasNext()) {
      if (next == '}') {
        source.next();
        break;
      }

      String name = parseString(source);
      seekTo(':', source);
      source.next();

      Object value = contextFor(name).parse(source);
      map.put(name, value);

      seekTo(",}", source);
      next = source.next();
      if (next == '}') break;
      else next = seekTo("\"}", source);
    }

    String classname = (String) map.get("class");
    if (classname != null) {
      try {
        Class c = Loader.loadClass(JSON.class, classname);
        return convertTo(c, map);
      } catch (ClassNotFoundException e) {
        LOG.warn(e);
      }
    }
    return map;
  }

  /* ------------------------------------------------------------ */
  protected Object parseArray(Source source) {
    if (source.next() != '[') throw new IllegalStateException();

    int size = 0;
    ArrayList list = null;
    Object item = null;
    boolean coma = true;

    while (source.hasNext()) {
      char c = source.peek();
      switch (c) {
        case ']':
          source.next();
          switch (size) {
            case 0:
              return newArray(0);
            case 1:
              Object array = newArray(1);
              Array.set(array, 0, item);
              return array;
            default:
              return list.toArray(newArray(list.size()));
          }

        case ',':
          if (coma) throw new IllegalStateException();
          coma = true;
          source.next();
          break;

        default:
          if (Character.isWhitespace(c)) source.next();
          else {
            coma = false;
            if (size++ == 0) item = contextForArray().parse(source);
            else if (list == null) {
              list = new ArrayList();
              list.add(item);
              item = contextForArray().parse(source);
              list.add(item);
              item = null;
            } else {
              item = contextForArray().parse(source);
              list.add(item);
              item = null;
            }
          }
      }
    }

    throw new IllegalStateException("unexpected end of array");
  }

  /* ------------------------------------------------------------ */
  protected String parseString(Source source) {
    if (source.next() != '"') throw new IllegalStateException();

    boolean escape = false;

    StringBuilder b = null;
    final char[] scratch = source.scratchBuffer();

    if (scratch != null) {
      int i = 0;
      while (source.hasNext()) {
        if (i >= scratch.length) {
          // we have filled the scratch buffer, so we must
          // use the StringBuffer for a large string
          b = new StringBuilder(scratch.length * 2);
          b.append(scratch, 0, i);
          break;
        }

        char c = source.next();

        if (escape) {
          escape = false;
          switch (c) {
            case '"':
              scratch[i++] = '"';
              break;
            case '\\':
              scratch[i++] = '\\';
              break;
            case '/':
              scratch[i++] = '/';
              break;
            case 'b':
              scratch[i++] = '\b';
              break;
            case 'f':
              scratch[i++] = '\f';
              break;
            case 'n':
              scratch[i++] = '\n';
              break;
            case 'r':
              scratch[i++] = '\r';
              break;
            case 't':
              scratch[i++] = '\t';
              break;
            case 'u':
              char uc =
                  (char)
                      ((TypeUtil.convertHexDigit((byte) source.next()) << 12)
                          + (TypeUtil.convertHexDigit((byte) source.next()) << 8)
                          + (TypeUtil.convertHexDigit((byte) source.next()) << 4)
                          + (TypeUtil.convertHexDigit((byte) source.next())));
              scratch[i++] = uc;
              break;
            default:
              scratch[i++] = c;
          }
        } else if (c == '\\') {
          escape = true;
          continue;
        } else if (c == '\"') {
          // Return string that fits within scratch buffer
          return toString(scratch, 0, i);
        } else scratch[i++] = c;
      }

      // Missing end quote, but return string anyway ?
      if (b == null) return toString(scratch, 0, i);
    } else b = new StringBuilder(getStringBufferSize());

    // parse large string into string buffer
    final StringBuilder builder = b;
    while (source.hasNext()) {
      char c = source.next();

      if (escape) {
        escape = false;
        switch (c) {
          case '"':
            builder.append('"');
            break;
          case '\\':
            builder.append('\\');
            break;
          case '/':
            builder.append('/');
            break;
          case 'b':
            builder.append('\b');
            break;
          case 'f':
            builder.append('\f');
            break;
          case 'n':
            builder.append('\n');
            break;
          case 'r':
            builder.append('\r');
            break;
          case 't':
            builder.append('\t');
            break;
          case 'u':
            char uc =
                (char)
                    ((TypeUtil.convertHexDigit((byte) source.next()) << 12)
                        + (TypeUtil.convertHexDigit((byte) source.next()) << 8)
                        + (TypeUtil.convertHexDigit((byte) source.next()) << 4)
                        + (TypeUtil.convertHexDigit((byte) source.next())));
            builder.append(uc);
            break;
          default:
            builder.append(c);
        }
      } else if (c == '\\') {
        escape = true;
        continue;
      } else if (c == '\"') break;
      else builder.append(c);
    }
    return builder.toString();
  }

  /* ------------------------------------------------------------ */
  public Number parseNumber(Source source) {
    boolean minus = false;
    long number = 0;
    StringBuilder buffer = null;

    longLoop:
    while (source.hasNext()) {
      char c = source.peek();
      switch (c) {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          number = number * 10 + (c - '0');
          source.next();
          break;

        case '-':
        case '+':
          if (number != 0) throw new IllegalStateException("bad number");
          minus = true;
          source.next();
          break;

        case '.':
        case 'e':
        case 'E':
          buffer = new StringBuilder(16);
          if (minus) buffer.append('-');
          buffer.append(number);
          buffer.append(c);
          source.next();
          break longLoop;

        default:
          break longLoop;
      }
    }

    if (buffer == null) return minus ? -1 * number : number;

    doubleLoop:
    while (source.hasNext()) {
      char c = source.peek();
      switch (c) {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case '-':
        case '.':
        case '+':
        case 'e':
        case 'E':
          buffer.append(c);
          source.next();
          break;

        default:
          break doubleLoop;
      }
    }
    return new Double(buffer.toString());
  }

  /* ------------------------------------------------------------ */
  protected void seekTo(char seek, Source source) {
    while (source.hasNext()) {
      char c = source.peek();
      if (c == seek) return;

      if (!Character.isWhitespace(c))
        throw new IllegalStateException("Unexpected '" + c + " while seeking '" + seek + "'");
      source.next();
    }

    throw new IllegalStateException("Expected '" + seek + "'");
  }

  /* ------------------------------------------------------------ */
  protected char seekTo(String seek, Source source) {
    while (source.hasNext()) {
      char c = source.peek();
      if (seek.indexOf(c) >= 0) {
        return c;
      }

      if (!Character.isWhitespace(c))
        throw new IllegalStateException(
            "Unexpected '" + c + "' while seeking one of '" + seek + "'");
      source.next();
    }

    throw new IllegalStateException("Expected one of '" + seek + "'");
  }

  /* ------------------------------------------------------------ */
  protected static void complete(String seek, Source source) {
    int i = 0;
    while (source.hasNext() && i < seek.length()) {
      char c = source.next();
      if (c != seek.charAt(i++))
        throw new IllegalStateException("Unexpected '" + c + " while seeking  \"" + seek + "\"");
    }

    if (i < seek.length()) throw new IllegalStateException("Expected \"" + seek + "\"");
  }

  private final class ConvertableOutput implements Output {
    private final Appendable _buffer;
    char c = '{';

    private ConvertableOutput(Appendable buffer) {
      _buffer = buffer;
    }

    public void complete() {
      try {
        if (c == '{') _buffer.append("{}");
        else if (c != 0) _buffer.append("}");
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    public void add(Object obj) {
      if (c == 0) throw new IllegalStateException();
      append(_buffer, obj);
      c = 0;
    }

    public void addClass(Class type) {
      try {
        if (c == 0) throw new IllegalStateException();
        _buffer.append(c);
        _buffer.append("\"class\":");
        append(_buffer, type.getName());
        c = ',';
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    public void add(String name, Object value) {
      try {
        if (c == 0) throw new IllegalStateException();
        _buffer.append(c);
        QuotedStringTokenizer.quote(_buffer, name);
        _buffer.append(':');
        append(_buffer, value);
        c = ',';
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    public void add(String name, double value) {
      try {
        if (c == 0) throw new IllegalStateException();
        _buffer.append(c);
        QuotedStringTokenizer.quote(_buffer, name);
        _buffer.append(':');
        appendNumber(_buffer, new Double(value));
        c = ',';
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    public void add(String name, long value) {
      try {
        if (c == 0) throw new IllegalStateException();
        _buffer.append(c);
        QuotedStringTokenizer.quote(_buffer, name);
        _buffer.append(':');
        appendNumber(_buffer, value);
        c = ',';
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    public void add(String name, boolean value) {
      try {
        if (c == 0) throw new IllegalStateException();
        _buffer.append(c);
        QuotedStringTokenizer.quote(_buffer, name);
        _buffer.append(':');
        appendBoolean(_buffer, value ? Boolean.TRUE : Boolean.FALSE);
        c = ',';
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
  }

  /* ------------------------------------------------------------ */
  public interface Source {
    boolean hasNext();

    char next();

    char peek();

    char[] scratchBuffer();
  }

  /* ------------------------------------------------------------ */
  public static class StringSource implements Source {
    private final String string;
    private int index;
    private char[] scratch;

    public StringSource(String s) {
      string = s;
    }

    public boolean hasNext() {
      if (index < string.length()) return true;
      scratch = null;
      return false;
    }

    public char next() {
      return string.charAt(index++);
    }

    public char peek() {
      return string.charAt(index);
    }

    @Override
    public String toString() {
      return string.substring(0, index) + "|||" + string.substring(index);
    }

    public char[] scratchBuffer() {
      if (scratch == null) scratch = new char[string.length()];
      return scratch;
    }
  }

  /* ------------------------------------------------------------ */
  public static class ReaderSource implements Source {
    private Reader _reader;
    private int _next = -1;
    private char[] scratch;

    public ReaderSource(Reader r) {
      _reader = r;
    }

    public void setReader(Reader reader) {
      _reader = reader;
      _next = -1;
    }

    public boolean hasNext() {
      getNext();
      if (_next < 0) {
        scratch = null;
        return false;
      }
      return true;
    }

    public char next() {
      getNext();
      char c = (char) _next;
      _next = -1;
      return c;
    }

    public char peek() {
      getNext();
      return (char) _next;
    }

    private void getNext() {
      if (_next < 0) {
        try {
          _next = _reader.read();
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    }

    public char[] scratchBuffer() {
      if (scratch == null) scratch = new char[1024];
      return scratch;
    }
  }

  /* ------------------------------------------------------------ */
  /** JSON Output class for use by {@link Convertible}. */
  public interface Output {
    public void addClass(Class c);

    public void add(Object obj);

    public void add(String name, Object value);

    public void add(String name, double value);

    public void add(String name, long value);

    public void add(String name, boolean value);
  }

  /* ------------------------------------------------------------ */
  /* ------------------------------------------------------------ */
  /**
   * JSON Convertible object. Object can implement this interface in a similar way to the {@link
   * Externalizable} interface is used to allow classes to provide their own serialization
   * mechanism.
   *
   * <p>A JSON.Convertible object may be written to a JSONObject or initialized from a Map of field
   * names to values.
   *
   * <p>If the JSON is to be convertible back to an Object, then the method {@link
   * Output#addClass(Class)} must be called from within toJSON()
   */
  public interface Convertible {
    public void toJSON(Output out);

    public void fromJSON(Map object);
  }

  /* ------------------------------------------------------------ */
  /**
   * Static JSON Convertor.
   *
   * <p>may be implemented to provide static convertors for objects that may be registered with
   * {@link JSON#registerConvertor(Class, org.eclipse.jetty.util.ajax.JSON.Convertor)} . These
   * convertors are looked up by class, interface and super class by {@link
   * JSON#getConvertor(Class)}. Convertors should be used when the classes to be converted cannot
   * implement {@link Convertible} or {@link Generator}.
   */
  public interface Convertor {
    public void toJSON(Object obj, Output out);

    public Object fromJSON(Map object);
  }

  /* ------------------------------------------------------------ */
  /**
   * JSON Generator. A class that can add it's JSON representation directly to a StringBuffer. This
   * is useful for object instances that are frequently converted and wish to avoid multiple
   * Conversions
   */
  public interface Generator {
    public void addJSON(Appendable buffer);
  }

  /* ------------------------------------------------------------ */
  /**
   * A Literal JSON generator A utility instance of {@link JSON.Generator} that holds a
   * pre-generated string on JSON text.
   */
  public static class Literal implements Generator {
    private String _json;

    /* ------------------------------------------------------------ */
    /**
     * Construct a literal JSON instance for use by {@link JSON#toString(Object)}. If {@link
     * Log#isDebugEnabled()} is true, the JSON will be parsed to check validity
     *
     * @param json A literal JSON string.
     */
    public Literal(String json) {
      if (LOG.isDebugEnabled()) // TODO: Make this a configurable option on JSON instead!
      parse(json);
      _json = json;
    }

    @Override
    public String toString() {
      return _json;
    }

    public void addJSON(Appendable buffer) {
      try {
        buffer.append(_json);
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
  }
}
Example #3
0
 /**
  * Construct a literal JSON instance for use by {@link JSON#toString(Object)}. If {@link
  * Log#isDebugEnabled()} is true, the JSON will be parsed to check validity
  *
  * @param json A literal JSON string.
  */
 public Literal(String json) {
   if (LOG.isDebugEnabled()) // TODO: Make this a configurable option on JSON instead!
   parse(json);
   _json = json;
 }