/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2006-2009, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id: JSON.scala 18468 2009-08-12 14:25:05Z dchenbecker $


package scala.util.parsing.json
import scala.util.parsing.combinator._
import scala.util.parsing.combinator.syntactical._
import scala.util.parsing.combinator.lexical._

/** 
 * This object provides a simple interface to the JSON parser class. The default conversion
 * for numerics is into a double. If you wish to override this behavior at the global level,
 * you can set the globalNumberParser property to your own (String => Any) function. If you only
 * want to override at the per-thread level then you can set the perThreadNumberParser property to your
 * function. For example:
 * 
 * <pre>
 * val myConversionFunc = {input : String => BigDecimal(input)}
 * 
 * // Global override
 * JSON.globalNumberParser = myConversionFunc
 * 
 * // Per-thread override
 * JSON.perThreadNumberParser = myConversionFunc
 * </pre>
 *
 *  @author Derek Chen-Becker <"java"+@+"chen-becker"+"."+"org">
 */
object JSON extends Parser {

  /**
   * Parse the given JSON string and return a list of elements. If the
   * string is a JSON object it will be a list of pairs. If it's a JSON 
   * array it will be be a list of individual elements.
   *
   * @param input the given JSON string.
   * @return      an optional list of of elements. 
   */
  def parse(input: String): Option[List[Any]] =
    phrase(root)(new lexical.Scanner(input)) match {
      case Success(result, _) => Some(result)
      case _ => None
    }
  
  /**
   * Parse the given JSON string and return either a <code>List[Any]</code>
   * if the JSON string specifies an <code>Array</code>, or a
   * <code>Map[String,Any]</code> if the JSON string specifies an object.
   *
   * @param input the given JSON string.
   * @return      an optional list or map.
   */
  def parseFull(input: String): Option[Any] =
    parse(input) match {
      case Some(data) => Some(resolveType(data))
      case None => None
    }

  /**
   * A utility method to resolve a parsed JSON list into objects or 
   * arrays. See the parse method for details.
   */
  def resolveType(input: List[_]): Any = {
    var objMap = Map[String, Any]()
    
    if (input.forall { 
      case (key: String, value: List[_]) =>
        objMap = objMap.+[Any](key -> resolveType(value))
        true
      case (key : String, value) =>
        objMap += key -> value
        true
      case _ => false
    }) objMap
    else
      input.map {
          case l : List[_] => resolveType(l)
          case x => x
      }
  }
  
  /**
   * The global (VM) default function for converting a string to a numeric value. 
   */
  def globalNumberParser_=(f: NumericParser) { defaultNumberParser = f }
  def globalNumberParser : NumericParser = defaultNumberParser
  
  /**
   * Defines the function used to convert a numeric string literal into a numeric format on a per-thread
   * basis. Use globalNumberParser for a global override
   */
   def perThreadNumberParser_=(f : NumericParser) { numberParser.set(f) }
   def perThreadNumberParser : NumericParser = numberParser.get()
}