LCOV - code coverage report
Current view: top level - lib - json.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 281 281 100.0 %
Date: 2014-11-22 Functions: 29 29 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* json.cpp -- written by Alexis WILKE for Made to Order Software Corp. (c) 2005-2014 */
       2             : 
       3             : /*
       4             : 
       5             : Copyright (c) 2005-2014 Made to Order Software Corp.
       6             : 
       7             : http://snapwebsites.org/project/as2js
       8             : 
       9             : Permission is hereby granted, free of charge, to any
      10             : person obtaining a copy of this software and
      11             : associated documentation files (the "Software"), to
      12             : deal in the Software without restriction, including
      13             : without limitation the rights to use, copy, modify,
      14             : merge, publish, distribute, sublicense, and/or sell
      15             : copies of the Software, and to permit persons to whom
      16             : the Software is furnished to do so, subject to the
      17             : following conditions:
      18             : 
      19             : The above copyright notice and this permission notice
      20             : shall be included in all copies or substantial
      21             : portions of the Software.
      22             : 
      23             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
      24             : ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
      25             : LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
      26             : FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
      27             : EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
      28             : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
      29             : WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
      30             : ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      31             : SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      32             : SOFTWARE.
      33             : 
      34             : */
      35             : 
      36             : #include    "as2js/json.h"
      37             : 
      38             : #include    "as2js/exceptions.h"
      39             : #include    "as2js/message.h"
      40             : 
      41             : #include    <iomanip>
      42             : 
      43             : 
      44             : /** \file
      45             :  * \brief Implementation of the JSON reader and writer.
      46             :  *
      47             :  * This file includes the implementation of the as2js::JSON class.
      48             :  *
      49             :  * The parser makes use of the lexer and an input stream.
      50             :  *
      51             :  * The writer makes use of an output stream.
      52             :  *
      53             :  * Note that our JSON parser supports the following extensions that
      54             :  * are NOT part of a valid JSON file:
      55             :  *
      56             :  * \li C-like comments using the standard slash (/) asterisk (*) to
      57             :  *     start the comment and asterisk (*) slash (/) to end it.
      58             :  * \li C++-like comments using the standard double slash (/) and ending
      59             :  *     the line with a newline character.
      60             :  * \li The NaN special value.
      61             :  * \li The +Infinity value.
      62             :  * \li The -Infinity value.
      63             :  * \li The +<number> value.
      64             :  * \li Decimal numbers are read as decimal numbers and not floating point
      65             :  *     numbers. We support full 64 bit integers.
      66             :  * \li Strings using single quote (') characters.
      67             :  * \li Strings can include \U######## characters (large Unicode, 8 digits.)
      68             :  *
      69             :  * Note that all comments are discarded while reading a JSON file.
      70             :  *
      71             :  * The writer, however, generates:
      72             :  *
      73             :  * \li Strings using double quotes (").
      74             :  * \li Only uses the small unicode \u#### encoding. Large Unicode characters
      75             :  *     are output as is (in the format used by your output stream.)
      76             :  * \li Does not output any comments (although you may include a comment in
      77             :  *     the header parameter.)
      78             :  *
      79             :  * However, it will:
      80             :  *
      81             :  * \li Generate integers that are 64 bit.
      82             :  * \li Output NaN for undefined numbers.
      83             :  * \li Output Infinity and -Infinity for number representing infinity.
      84             :  *
      85             :  * We may later introduce a flag to allow / disallow these values.
      86             :  */
      87             : 
      88             : 
      89             : namespace as2js
      90             : {
      91             : 
      92             : /** \brief Private implementation functions.
      93             :  *
      94             :  * Our JSON implementation makes use of functions that are defined in
      95             :  * this unnamed namespace.
      96             :  */
      97             : namespace
      98             : {
      99             : 
     100             : /** \brief Append a raw string to a stringified string.
     101             :  *
     102             :  * This function appends a string (str) to a stringified
     103             :  * string (result). In the process, it adds quotes to the
     104             :  * resulting string.
     105             :  *
     106             :  * \param[in,out] result  Where the output string is appended with a
     107             :  *                        valid string for a JSON file.
     108             :  * \param[in] str  The raw input string which needs to be stringified.
     109             :  */
     110        5729 : void append_string(String& result, String const& str)
     111             : {
     112        5729 :     result += '"';
     113        5729 :     size_t const max_chars(str.length());
     114      102582 :     for(size_t idx(0); idx < max_chars; ++idx)
     115             :     {
     116       96853 :         switch(str[idx])
     117             :         {
     118             :         case '\b':
     119         600 :             result += '\\';
     120         600 :             result += 'b';
     121         600 :             break;
     122             : 
     123             :         case '\f':
     124         784 :             result += '\\';
     125         784 :             result += 'f';
     126         784 :             break;
     127             : 
     128             :         case '\n':
     129         887 :             result += '\\';
     130         887 :             result += 'n';
     131         887 :             break;
     132             : 
     133             :         case '\r':
     134         950 :             result += '\\';
     135         950 :             result += 'r';
     136         950 :             break;
     137             : 
     138             :         case '\t':
     139         810 :             result += '\\';
     140         810 :             result += 't';
     141         810 :             break;
     142             : 
     143             :         case '"':
     144        5267 :             result += '\\';
     145        5267 :             result += '"';
     146        5267 :             break;
     147             : 
     148             :         case '\'':
     149        5217 :             result += '\\';
     150        5217 :             result += '\'';
     151        5217 :             break;
     152             : 
     153             :         default:
     154       82338 :             if(str[idx] < 0x0020)
     155             :             {
     156             :                 // other controls must be escaped using Unicode
     157       21013 :                 std::stringstream ss;
     158       21013 :                 ss << std::hex << "\\u" << std::setfill('0') << std::setw(4) << static_cast<int>(str[idx]);
     159       21013 :                 result += ss.str().c_str();
     160             :             }
     161             :             else
     162             :             {
     163       61325 :                 result += str[idx];
     164             :             }
     165       82338 :             break;
     166             : 
     167             :         }
     168             :     }
     169        5729 :     result += '"';
     170        5729 : }
     171             : 
     172             : }
     173             : // no name namespace
     174             : 
     175             : 
     176             : /** \brief Initialize a JSONValue saving_t object.
     177             :  *
     178             :  * This function initializes a JSONValue saving_t object attached to
     179             :  * the specified JSONValue \p value.
     180             :  *
     181             :  * While saving we cannot know whether the JSON is currently cyclical
     182             :  * or not. We use this saving_t object to mark all the objects being
     183             :  * saved with a flag. If the flag is already set, this constructor
     184             :  * fails with an exception.
     185             :  *
     186             :  * To avoid cyclical JSON trees, make sure to always allocate any
     187             :  * new value that you add to your tree.
     188             :  *
     189             :  * \exception exception_cyclical_structure
     190             :  * This exception is raised if the JSONValue about to be saved is
     191             :  * already marked as being saved, meaning that a child of JSONValue
     192             :  * points back to this JSONValue.
     193             :  *
     194             :  * \param[in] value  The value being marked as being saved.
     195             :  */
     196         241 : JSON::JSONValue::saving_t::saving_t(JSONValue const& value)
     197         241 :     : f_value(const_cast<JSONValue&>(value))
     198             : {
     199         241 :     if(f_value.f_saving)
     200             :     {
     201          20 :         throw exception_cyclical_structure("JSON cannot stringify a set of objects and arrays which are cyclical");
     202             :     }
     203         221 :     f_value.f_saving = true;
     204         221 : }
     205             : 
     206             : 
     207             : /** \brief Destroy a JSONValue saving_t object.
     208             :  *
     209             :  * The destructor of a JSONValue marks the attached JSONValue object
     210             :  * as saved. In other words, it allows it to be saved again.
     211             :  *
     212             :  * It is used to make sure that the same JSON tree can be saved
     213             :  * multiple times.
     214             :  *
     215             :  * Note that since this happens once the value is saved, if it
     216             :  * appears multiple times in the tree, but is not cyclical, the
     217             :  * save will work.
     218             :  */
     219         221 : JSON::JSONValue::saving_t::~saving_t()
     220             : {
     221         221 :     f_value.f_saving = false;
     222         221 : }
     223             : 
     224             : 
     225             : 
     226             : 
     227             : /** \brief Initialize a JSONValue object.
     228             :  *
     229             :  * The NULL constructor only accepts a position and it marks this JSON
     230             :  * value as a NULL value.
     231             :  *
     232             :  * The type of this JSONValue will be set to JSON_TYPE_NULL.
     233             :  *
     234             :  * \param[in] position  The position where this JSONValue was read from.
     235             :  */
     236         214 : JSON::JSONValue::JSONValue(Position const &position)
     237             :     : f_type(type_t::JSON_TYPE_NULL)
     238         214 :     , f_position(position)
     239             : {
     240         214 : }
     241             : 
     242             : 
     243             : /** \brief Initialize a JSONValue object.
     244             :  *
     245             :  * The integer constructor accepts a position and an integer. It creates
     246             :  * an integer JSONValue object.
     247             :  *
     248             :  * The value cannot be modified, however, it can be retrieved using the
     249             :  * get_int64() function.
     250             :  *
     251             :  * The type of this JSONValue will be set to JSON_TYPE_INT64.
     252             :  *
     253             :  * \param[in] position  The position where this JSONValue was read from.
     254             :  * \param[in] integer  The integer to save in this JSONValue.
     255             :  */
     256        4547 : JSON::JSONValue::JSONValue(Position const &position, Int64 integer)
     257             :     : f_type(type_t::JSON_TYPE_INT64)
     258             :     , f_position(position)
     259        4547 :     , f_integer(integer)
     260             : {
     261        4547 : }
     262             : 
     263             : 
     264             : /** \brief Initialize a JSONValue object.
     265             :  *
     266             :  * The floating point constructor accepts a position and a floating point
     267             :  * number.
     268             :  *
     269             :  * The value cannot be modified, however, it can be retrieved using
     270             :  * the get_float64() function.
     271             :  *
     272             :  * The type of this JSON will be JSON_TYPE_FLOAT64.
     273             :  *
     274             :  * \param[in] position  The position where this JSONValue was read from.
     275             :  * \param[in] floating_point  The floating point to save in the JSONValue.
     276             :  */
     277        1144 : JSON::JSONValue::JSONValue(Position const &position, Float64 floating_point)
     278             :     : f_type(type_t::JSON_TYPE_FLOAT64)
     279             :     , f_position(position)
     280        1144 :     , f_float(floating_point)
     281             : {
     282        1144 : }
     283             : 
     284             : 
     285             : /** \brief Initialize a JSONValue object.
     286             :  *
     287             :  * The string constructor accepts a position and a string parameter.
     288             :  *
     289             :  * The value cannot be modified, however, it can be retrieved using
     290             :  * the get_string() function.
     291             :  *
     292             :  * The type of this JSONValue will be set to JSON_TYPE_STRING.
     293             :  *
     294             :  * \param[in] position  The position where this JSONValue was read from.
     295             :  * \param[in] string  The string to save in this JSONValue object.
     296             :  */
     297       52063 : JSON::JSONValue::JSONValue(Position const &position, String const& string)
     298             :     : f_type(type_t::JSON_TYPE_STRING)
     299             :     , f_position(position)
     300       52063 :     , f_string(string)
     301             : {
     302       52063 : }
     303             : 
     304             : 
     305             : /** \brief Initialize a JSONValue object.
     306             :  *
     307             :  * The Boolean constructor accepts a position and a Boolean value: true
     308             :  * or false.
     309             :  *
     310             :  * The value cannot be modified, however, it can be tested using
     311             :  * the get_type() function and check the type of object.
     312             :  *
     313             :  * The type of this JSONValue will be set to JSON_TYPE_TRUE when the
     314             :  * \p boolean parameter is true, and to JSON_TYPE_FALSE when the
     315             :  * \p boolean parameter is false.
     316             :  *
     317             :  * \param[in] position  The position where this JSONValue was read from.
     318             :  * \param[in] boolean  The boolean value to save in this JSONValue object.
     319             :  */
     320         464 : JSON::JSONValue::JSONValue(Position const &position, bool boolean)
     321             :     : f_type(boolean ? type_t::JSON_TYPE_TRUE : type_t::JSON_TYPE_FALSE)
     322         464 :     , f_position(position)
     323             : {
     324         464 : }
     325             : 
     326             : 
     327             : /** \brief Initialize a JSONValue object.
     328             :  *
     329             :  * The array constructor accepts a position and an array as parameters.
     330             :  *
     331             :  * The array in this JSONValue can be modified using the set_item()
     332             :  * function. Also, it can be retrieved using the get_array() function.
     333             :  *
     334             :  * The type of this JSONValue will be set to JSON_TYPE_ARRAY.
     335             :  *
     336             :  * \param[in] position  The position where this JSONValue was read from.
     337             :  * \param[in] array  The array value to save in this JSONValue object.
     338             :  */
     339        8808 : JSON::JSONValue::JSONValue(Position const &position, array_t const& array)
     340             :     : f_type(type_t::JSON_TYPE_ARRAY)
     341             :     , f_position(position)
     342        8808 :     , f_array(array)
     343             : {
     344        8808 : }
     345             : 
     346             : 
     347             : /** \brief Initialize a JSONValue object.
     348             :  *
     349             :  * The object constructor accepts a position and an object.
     350             :  *
     351             :  * The object in this JSONValue can be modified using the set_member()
     352             :  * function. Also, it can be retrieved using the get_object() function.
     353             :  *
     354             :  * The type of this JSONValue will be set to JSON_TYPE_OBJECT.
     355             :  *
     356             :  * \param[in] position  The position where this JSONValue was read from.
     357             :  * \param[in] object  The object value to save in this JSONValue object.
     358             :  */
     359       27028 : JSON::JSONValue::JSONValue(Position const &position, object_t const& object)
     360             :     : f_type(type_t::JSON_TYPE_OBJECT)
     361             :     , f_position(position)
     362       27028 :     , f_object(object)
     363             : {
     364       27028 : }
     365             : 
     366             : 
     367             : /** \brief Retrieve the type of this JSONValue object.
     368             :  *
     369             :  * The type of a JSONValue cannot be modified. This value is read-only.
     370             :  *
     371             :  * The type determines what get_...() and what set_...() (if any)
     372             :  * functions can be called against this JSONValue object. If an invalid
     373             :  * function is called, then an exception is raised. To know which functions
     374             :  * are valid for this object, you need to check out its type.
     375             :  *
     376             :  * Note that the Boolean JSONValue objects do not have any getter or
     377             :  * setter functions. Their type defines their value: JSON_TYPE_TRUE and
     378             :  * JSON_TYPE_FALSE.
     379             :  *
     380             :  * The type is one of:
     381             :  *
     382             :  * \li JSON_TYPE_ARRAY
     383             :  * \li JSON_TYPE_FALSE
     384             :  * \li JSON_TYPE_FLOAT64
     385             :  * \li JSON_TYPE_INT64
     386             :  * \li JSON_TYPE_NULL
     387             :  * \li JSON_TYPE_OBJECT
     388             :  * \li JSON_TYPE_STRING
     389             :  * \li JSON_TYPE_TRUE
     390             :  *
     391             :  * A JSONValue cannot have the special type JSON_TYPE_UNKNOWN.
     392             :  *
     393             :  * \return The type of this JSONValue.
     394             :  */
     395    23277817 : JSON::JSONValue::type_t JSON::JSONValue::get_type() const
     396             : {
     397    23277817 :     return f_type;
     398             : }
     399             : 
     400             : 
     401             : /** \brief Get the integer.
     402             :  *
     403             :  * This function is used to retrieve the integer from a JSON_TYPE_INT64
     404             :  * JSONValue.
     405             :  *
     406             :  * It is not possible to change the integer value directly. Instead you
     407             :  * have to create a new JSONValue with the new value and replace this
     408             :  * object with the new one.
     409             :  *
     410             :  * \exception exception_internal_error
     411             :  * This exception is raised if the JSONValue object is not of type
     412             :  * JSON_TYPE_INT64.
     413             :  *
     414             :  * \return An Int64 object.
     415             :  */
     416     8355151 : Int64 JSON::JSONValue::get_int64() const
     417             : {
     418     8355151 :     if(f_type != type_t::JSON_TYPE_INT64)
     419             :     {
     420         452 :         throw exception_internal_error("get_int64() called with a non-int64 value type");
     421             :     }
     422     8354699 :     return f_integer;
     423             : }
     424             : 
     425             : 
     426             : /** \brief Get the floating point.
     427             :  *
     428             :  * This function is used to retrieve the floating point from a
     429             :  * JSON_TYPE_FLOAT64 JSONValue.
     430             :  *
     431             :  * It is not possible to change the floating point value directly. Instead
     432             :  * you have to create a new JSONValue with the new value and replace this
     433             :  * object with the new one.
     434             :  *
     435             :  * \exception exception_internal_error
     436             :  * This exception is raised if the JSONValue object is not of type
     437             :  * JSON_TYPE_FLOAT64.
     438             :  *
     439             :  * \return A Float64 object.
     440             :  */
     441      526811 : Float64 JSON::JSONValue::get_float64() const
     442             : {
     443      526811 :     if(f_type != type_t::JSON_TYPE_FLOAT64)
     444             :     {
     445         450 :         throw exception_internal_error("get_float64() called with a non-float64 value type");
     446             :     }
     447      526361 :     return f_float;
     448             : }
     449             : 
     450             : 
     451             : /** \brief Get the string.
     452             :  *
     453             :  * This function lets you retrieve the string of a JSON_TYPE_STRING object.
     454             :  *
     455             :  * \exception exception_internal_error
     456             :  * This exception is raised if the JSONValue object is not of type
     457             :  * JSON_TYPE_STRING.
     458             :  *
     459             :  * \return The string of this JSONValue object.
     460             :  */
     461    42906729 : String const& JSON::JSONValue::get_string() const
     462             : {
     463    42906729 :     if(f_type != type_t::JSON_TYPE_STRING)
     464             :     {
     465         452 :         throw exception_internal_error("get_string() called with a non-string value type");
     466             :     }
     467    42906277 :     return f_string;
     468             : }
     469             : 
     470             : 
     471             : /** \brief Get a reference to this JSONValue array.
     472             :  *
     473             :  * This function is used to retrieve a read-only reference to the array
     474             :  * of a JSON_TYPE_ARRAY JSONValue.
     475             :  *
     476             :  * You may change the array using the set_item() function. Note that if
     477             :  * you did not make a copy of the array returned by this function, you
     478             :  * will see the changes. It also means that iterators are likely not
     479             :  * going to work once a call to set_item() was made.
     480             :  *
     481             :  * \exception exception_internal_error
     482             :  * This exception is raised if the JSONValue object is not of type
     483             :  * JSON_TYPE_ARRAY.
     484             :  *
     485             :  * \return A constant reference to a JSONValue array_t object.
     486             :  */
     487    15551593 : JSON::JSONValue::array_t const& JSON::JSONValue::get_array() const
     488             : {
     489    15551593 :     if(f_type != type_t::JSON_TYPE_ARRAY)
     490             :     {
     491         630 :         throw exception_internal_error("get_array() called with a non-array value type");
     492             :     }
     493    15550963 :     return f_array;
     494             : }
     495             : 
     496             : 
     497             : /** \brief Change the value of an array item.
     498             :  *
     499             :  * This function is used to change the value of an array item. The index
     500             :  * (\p idx) defines the position of the item to change. The \p value is
     501             :  * the new value to save at that position.
     502             :  *
     503             :  * Note that the pointer to the value cannot be set to NULL.
     504             :  *
     505             :  * The index (\p idx) can be any value between 0 and the current size of
     506             :  * the array. If idx is larger, then an exception is raised.
     507             :  *
     508             :  * When \p idx is set to the current size of the array, the \p value is
     509             :  * pushed at the end of the array (i.e. a new item is added to the existing
     510             :  * array.)
     511             :  *
     512             :  * \exception exception_internal_error
     513             :  * If the JSONValue is not of type JSON_TYPE_ARRAY, then this function
     514             :  * raises this exception.
     515             :  *
     516             :  * \exception exception_index_out_of_range
     517             :  * If idx is out of range (larger than the array size) then this exception
     518             :  * is raised. Note that idx is unsigned so it cannot be negative.
     519             :  *
     520             :  * \exception exception_invalid_data
     521             :  * If the value pointer is a NULL pointer, then this exception is raised.
     522             :  *
     523             :  * \param[in] idx  The index where \p value is to be saved.
     524             :  * \param[in] value  The new value to be saved.
     525             :  */
     526         770 : void JSON::JSONValue::set_item(size_t idx, JSONValue::pointer_t value)
     527             : {
     528         770 :     if(f_type != type_t::JSON_TYPE_ARRAY)
     529             :     {
     530         630 :         throw exception_internal_error("set_item() called with a non-array value type");
     531             :     }
     532         140 :     if(idx > f_array.size())
     533             :     {
     534          40 :         throw exception_index_out_of_range("JSON::JSONValue::set_item() called with an index out of bounds");
     535             :     }
     536         100 :     if(!value)
     537             :     {
     538           2 :         throw exception_invalid_data("JSON::JSONValue::set_item() called with a null pointer as the value");
     539             :     }
     540          98 :     if(idx == f_array.size())
     541             :     {
     542             :         // append value
     543          88 :         f_array.push_back(value);
     544             :     }
     545             :     else
     546             :     {
     547             :         // replace previous value
     548          10 :         f_array[idx] = value;
     549             :     }
     550          98 : }
     551             : 
     552             : 
     553             : /** \brief Get a reference to this JSONValue object.
     554             :  *
     555             :  * This function is used to retrieve a read-only reference to the object
     556             :  * of a JSON_TYPE_OBJECT JSONValue.
     557             :  *
     558             :  * You may change the object using the set_member() function. Note that
     559             :  * if you did not make a copy of the object returned by this function,
     560             :  * you will see the changes. It also means that iterators are likely not
     561             :  * going to work once a call to set_member() was made.
     562             :  *
     563             :  * \exception exception_internal_error
     564             :  * This exception is raised if the JSONValue object is not of type
     565             :  * JSON_TYPE_OBJECT.
     566             :  *
     567             :  * \return A constant reference to a JSONValue object_t object.
     568             :  */
     569    27329701 : JSON::JSONValue::object_t const& JSON::JSONValue::get_object() const
     570             : {
     571    27329701 :     if(f_type != type_t::JSON_TYPE_OBJECT)
     572             :     {
     573         630 :         throw exception_internal_error("get_object() called with a non-object value type");
     574             :     }
     575    27329071 :     return f_object;
     576             : }
     577             : 
     578             : 
     579             : /** \brief Change the value of an object member.
     580             :  *
     581             :  * This function is used to change the value of an object member. The
     582             :  * \p name defines the member to change. The \p value is the new value
     583             :  * to save along that name.
     584             :  *
     585             :  * Note that the pointer to the value cannot be set to NULL.
     586             :  *
     587             :  * The \p name can be any string except the empty string. If name is set
     588             :  * to the empty string, then an exception is raised.
     589             :  *
     590             :  * If a member with the same name already exists, it gets overwritten
     591             :  * with this new value. If the name is new, then the object is modified
     592             :  * which may affect your copy of the object, if you have one.
     593             :  *
     594             :  * \exception exception_internal_error
     595             :  * If the JSONValue is not of type JSON_TYPE_OBJECT, then this function
     596             :  * raises this exception.
     597             :  *
     598             :  * \exception exception_invalid_index
     599             :  * If name is the empty string then this exception is raised.
     600             :  *
     601             :  * \exception exception_invalid_data
     602             :  * If the value pointer is a NULL pointer, then this exception is raised.
     603             :  *
     604             :  * \param[in] name  The name of the object field.
     605             :  * \param[in] value  The new value to be saved.
     606             :  */
     607        2110 : void JSON::JSONValue::set_member(String const& name, JSONValue::pointer_t value)
     608             : {
     609        2110 :     if(f_type != type_t::JSON_TYPE_OBJECT)
     610             :     {
     611         630 :         throw exception_internal_error("set_member() called with a non-object value type");
     612             :     }
     613        1480 :     if(name.empty())
     614             :     {
     615             :         // TBD: is that really not allowed?
     616           2 :         throw exception_invalid_index("JSON::JSONValue::set_member() called with an empty string as the member name");
     617             :     }
     618        1478 :     if(!value)
     619             :     {
     620           2 :         throw exception_invalid_data("JSON::JSONValue::set_member() called with a null pointer as the value");
     621             :     }
     622             : 
     623             :     // this one is easy enough
     624        1476 :     f_object[name] = value;
     625        1476 : }
     626             : 
     627             : 
     628             : /** \brief Get a constant reference to the JSONValue position.
     629             :  *
     630             :  * This function returns a constant reference to the JSONValue position.
     631             :  *
     632             :  * This position object is specific to this JSONValue so each one of
     633             :  * them can have a different position.
     634             :  *
     635             :  * The position of a JSONValue cannot be modified. When creating a
     636             :  * JSONValue, the position passed in as a parameter is copied in
     637             :  * the f_position of the JSONValue.
     638             :  *
     639             :  * \return The position of the JSONValue object in the source.
     640             :  */
     641        2060 : Position const& JSON::JSONValue::get_position() const
     642             : {
     643        2060 :     return f_position;
     644             : }
     645             : 
     646             : 
     647             : /** \brief Get the JSONValue as a string.
     648             :  *
     649             :  * This function transforms a JSONValue object into a string.
     650             :  *
     651             :  * This is used to serialize the JSONValue and output it to a string.
     652             :  *
     653             :  * This function may raise an exception in the event the JSONValue is
     654             :  * cyclic, meaning that a child JSONValue points back at one of
     655             :  * its parent JSONValue's.
     656             :  *
     657             :  * \exception exception_internal_error
     658             :  * This exception is raised if a JSONValue object is of type
     659             :  * JSON_TYPE_UNKNOWN, which should not happen.
     660             :  *
     661             :  * \return A string representing the JSONValue.
     662             :  */
     663        9201 : String JSON::JSONValue::to_string() const
     664             : {
     665        9201 :     String result;
     666             : 
     667        9201 :     switch(f_type)
     668             :     {
     669             :     case type_t::JSON_TYPE_ARRAY:
     670        1218 :         result += "[";
     671        1218 :         if(f_array.size() > 0)
     672             :         {
     673         112 :             saving_t s(*this);
     674         102 :             result += f_array[0]->to_string(); // recursive
     675         102 :             size_t const max_elements(f_array.size());
     676        3964 :             for(size_t i(1); i < max_elements; ++i)
     677             :             {
     678        3872 :                 result += ",";
     679        3872 :                 result += f_array[i]->to_string(); // recursive
     680         102 :             }
     681             :         }
     682        1198 :         result += "]";
     683        1198 :         break;
     684             : 
     685             :     case type_t::JSON_TYPE_FALSE:
     686        1087 :         return "false";
     687             : 
     688             :     case type_t::JSON_TYPE_FLOAT64:
     689             :     {
     690        1278 :         Float64 f(f_float.get());
     691        1278 :         if(f.is_NaN())
     692             :         {
     693           5 :             return "NaN";
     694             :         }
     695        1273 :         if(f.is_positive_infinity())
     696             :         {
     697           6 :             return "Infinity";
     698             :         }
     699        1267 :         if(f.is_negative_infinity())
     700             :         {
     701           3 :             return "-Infinity";
     702             :         }
     703        1264 :         return std::to_string(f_float.get());
     704             :     }
     705             : 
     706             :     case type_t::JSON_TYPE_INT64:
     707        1370 :         return std::to_string(f_integer.get());
     708             : 
     709             :     case type_t::JSON_TYPE_NULL:
     710         995 :         return "null";
     711             : 
     712             :     case type_t::JSON_TYPE_OBJECT:
     713        1102 :         result += "{";
     714        1102 :         if(f_object.size() > 0)
     715             :         {
     716         129 :             saving_t s(*this);
     717         119 :             object_t::const_iterator obj(f_object.begin());
     718         119 :             append_string(result, obj->first);
     719         119 :             result += ":";
     720         119 :             result += obj->second->to_string(); // recursive
     721        4453 :             for(++obj; obj != f_object.end(); ++obj)
     722             :             {
     723        4344 :                 result += ",";
     724        4344 :                 append_string(result, obj->first);
     725        4344 :                 result += ":";
     726        4344 :                 result += obj->second->to_string(); // recursive
     727         119 :             }
     728             :         }
     729        1082 :         result += "}";
     730        1082 :         break;
     731             : 
     732             :     case type_t::JSON_TYPE_STRING:
     733        1266 :         append_string(result, f_string);
     734        1266 :         break;
     735             : 
     736             :     case type_t::JSON_TYPE_TRUE:
     737         885 :         return "true";
     738             : 
     739             :     case type_t::JSON_TYPE_UNKNOWN:
     740             :         throw exception_internal_error("JSON type \"Unknown\" is not valid and should never be used (it should not be possible to use it to create a JSONValue in the first place!)"); // LCOV_EXCL_LINE
     741             : 
     742             :     }
     743             : 
     744        3586 :     return result;
     745             : }
     746             : 
     747             : 
     748             : 
     749             : 
     750             : /** \brief Read a JSON value.
     751             :  *
     752             :  * This function opens a FileInput stream, setups a default Position
     753             :  * and then calls parse() to parse the file in a JSON tree.
     754             :  *
     755             :  * \param[in] filename  The name of a JSON file.
     756             :  */
     757          29 : JSON::JSONValue::pointer_t JSON::load(String const& filename)
     758             : {
     759          29 :     Position pos;
     760          29 :     pos.set_filename(filename);
     761             : 
     762             :     // we could not find this module, try to load the it
     763          58 :     FileInput::pointer_t in(new FileInput());
     764          29 :     if(!in->open(filename))
     765             :     {
     766           1 :         Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_NOT_FOUND, pos);
     767           1 :         msg << "cannot open JSON file \"" << filename << "\".";
     768             :         // should we throw here?
     769           1 :         return JSONValue::pointer_t();
     770             :     }
     771             : 
     772          57 :     return parse(in);
     773             : }
     774             : 
     775             : 
     776             : /** \brief Parse a JSON object.
     777             :  *
     778             :  * This function is used to read a JSON input stream.
     779             :  *
     780             :  * If a recoverable error occurs, the function returns with a JSONValue
     781             :  * smart pointer. If errors occur, then a message is created and sent,
     782             :  * but as much as possible of the input file is read in.
     783             :  *
     784             :  * Note that the resulting value may be a NULL pointer if too much failed.
     785             :  *
     786             :  * An empty file is not a valid JSON file. To the minimum you must have:
     787             :  *
     788             :  * \code
     789             :  * null;
     790             :  * \endcode
     791             :  *
     792             :  * \param[in] in  The input stream to be parsed.
     793             :  *
     794             :  * \return A pointer to a JSONValue tree, it may be a NULL pointer.
     795             :  */
     796      112755 : JSON::JSONValue::pointer_t JSON::parse(Input::pointer_t in)
     797             : {
     798             :     // Parse the JSON file
     799             :     //
     800             :     // Note:
     801             :     // We do not allow external options because it does not make sense
     802             :     // (i.e. JSON is very simple and no additional options should affect
     803             :     // the lexer!)
     804      112755 :     Options::pointer_t options(new Options);
     805             :     // Make sure it is marked as JSON (line terminators change in this case)
     806      112755 :     options->set_option(Options::option_t::OPTION_JSON, 1);
     807      112755 :     f_lexer.reset(new Lexer(in, options));
     808      112755 :     f_value = read_json_value(f_lexer->get_next_token());
     809             : 
     810      112755 :     if(!f_value)
     811             :     {
     812      103347 :         Message msg(message_level_t::MESSAGE_LEVEL_FATAL, err_code_t::AS_ERR_CANNOT_COMPILE, in->get_position());
     813      103347 :         msg << "could not interpret this JSON input \"" << in->get_position().get_filename() << "\".";
     814             :         // should we throw here?
     815             :     }
     816             : 
     817      112755 :     f_lexer.reset(); // release 'in' and 'options' pointers
     818             : 
     819      112755 :     return f_value;
     820             : }
     821             : 
     822             : 
     823             : /** \brief Read only JSON value.
     824             :  *
     825             :  * This function transform the specified \p n node in a JSONValue object.
     826             :  *
     827             :  * The type of object is defined from the type of node we just received
     828             :  * from the lexer.
     829             :  *
     830             :  * \li NODE_FALSE -- create a false JSONValue
     831             :  * \li NODE_FLOAT64 -- create a floating point JSONValue
     832             :  * \li NODE_INT64 -- create an integer JSONValue
     833             :  * \li NODE_NULL -- create a null JSONValue
     834             :  * \li NODE_STRING -- create a string JSONValue
     835             :  * \li NODE_TRUE -- create a true JSONValue
     836             :  *
     837             :  * If the lexer returned a NODE_SUBTRACT, then we assume we are about to
     838             :  * read an integer or a floating point. We do that and then calculate the
     839             :  * opposite and save the result as a FLOAT64 or INT64 JSONValue.
     840             :  *
     841             :  * If the lexer returned a NODE_OPEN_SQUARE_BRACKET then the function
     842             :  * enters the mode used to read an array.
     843             :  *
     844             :  * If the lexer returned a NODE_OPEN_CURVLY_BRACKET then the function
     845             :  * enters the mode used to read an object.
     846             :  *
     847             :  * Note that the function is somewhat weak in regard to error handling.
     848             :  * If the input is not valid as per as2js JSON documentation, then an
     849             :  * error is emitted and the process stops early.
     850             :  *
     851             :  * \param[in] n  The node to be transform in a JSON value.
     852             :  */
     853      189807 : JSON::JSONValue::pointer_t JSON::read_json_value(Node::pointer_t n)
     854             : {
     855      189807 :     if(n->get_type() == Node::node_t::NODE_EOF)
     856             :     {
     857           1 :         Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_EOF, n->get_position());
     858           1 :         msg << "the end of the file was reached while reading JSON data.";
     859           1 :         return JSONValue::pointer_t();
     860             :     }
     861      189806 :     switch(n->get_type())
     862             :     {
     863             :     case Node::node_t::NODE_ADD:
     864             :         // positive number...
     865           6 :         n = f_lexer->get_next_token();
     866           6 :         switch(n->get_type())
     867             :         {
     868             :         case Node::node_t::NODE_FLOAT64:
     869           3 :             return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_float64()));
     870             : 
     871             :         case Node::node_t::NODE_INT64:
     872           2 :             return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_int64()));
     873             : 
     874             :         default:
     875           1 :             Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_TOKEN, n->get_position());
     876           1 :             msg << "unexpected token (" << n->get_type_name() << ") found after a '+' sign, a number was expected.";
     877           1 :             return JSONValue::pointer_t();
     878             : 
     879             :         }
     880             :         /*NOT_REACHED*/
     881             :         break;
     882             : 
     883             :     case Node::node_t::NODE_FALSE:
     884          19 :         return JSONValue::pointer_t(new JSONValue(n->get_position(), false));
     885             : 
     886             :     case Node::node_t::NODE_FLOAT64:
     887         804 :         return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_float64()));
     888             : 
     889             :     case Node::node_t::NODE_INT64:
     890        2973 :         return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_int64()));
     891             : 
     892             :     case Node::node_t::NODE_NULL:
     893          26 :         return JSONValue::pointer_t(new JSONValue(n->get_position()));
     894             : 
     895             :     case Node::node_t::NODE_OPEN_CURVLY_BRACKET: // read an object
     896             :         {
     897       24609 :             JSONValue::object_t obj;
     898             : 
     899       49218 :             Position pos(n->get_position());
     900       24609 :             n = f_lexer->get_next_token();
     901       24609 :             if(n->get_type() != Node::node_t::NODE_CLOSE_CURVLY_BRACKET)
     902             :             {
     903             :                 for(;;)
     904             :                 {
     905       63734 :                     if(n->get_type() != Node::node_t::NODE_STRING)
     906             :                     {
     907           5 :                         Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_STRING_EXPECTED, n->get_position());
     908           5 :                         msg << "expected a string as the JSON object member name.";
     909           5 :                         return JSONValue::pointer_t();
     910             :                     }
     911       63729 :                     String name(n->get_string());
     912       63729 :                     n = f_lexer->get_next_token();
     913       63729 :                     if(n->get_type() != Node::node_t::NODE_COLON)
     914             :                     {
     915           2 :                         Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_COLON_EXPECTED, n->get_position());
     916           2 :                         msg << "expected a colon (:) as the JSON object member name and member value separator.";
     917           2 :                         return JSONValue::pointer_t();
     918             :                     }
     919             :                     // skip the colon
     920       63727 :                     n = f_lexer->get_next_token();
     921      102858 :                     JSONValue::pointer_t value(read_json_value(n)); // recursive
     922       63727 :                     if(!value)
     923             :                     {
     924             :                         // empty values mean we got an error, stop short!
     925           2 :                         return value;
     926             :                     }
     927       63725 :                     if(obj.find(name) != obj.end())
     928             :                     {
     929             :                         // TBD: we should verify that JSON indeed forbids such
     930             :                         //      nonsense; because we may have it wrong
     931           1 :                         Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_OBJECT_MEMBER_DEFINED_TWICE, n->get_position());
     932           1 :                         msg << "the same object member \"" << name << "\" was defined twice, which is not allowed in JSON.";
     933             :                         // continue because (1) the existing element is valid
     934             :                         // and (2) the new element is valid
     935             :                     }
     936             :                     else
     937             :                     {
     938       63724 :                         obj[name] = value;
     939             :                     }
     940       63725 :                     n = f_lexer->get_next_token();
     941       63725 :                     if(n->get_type() == Node::node_t::NODE_CLOSE_CURVLY_BRACKET)
     942             :                     {
     943       24593 :                         break;
     944             :                     }
     945       39132 :                     if(n->get_type() != Node::node_t::NODE_COMMA)
     946             :                     {
     947           1 :                         Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_COMMA_EXPECTED, n->get_position());
     948           1 :                         msg << "expected a comma (,) to separate two JSON object members.";
     949           1 :                         return JSONValue::pointer_t();
     950             :                     }
     951       39131 :                     n = f_lexer->get_next_token();
     952       39131 :                 }
     953             :             }
     954             : 
     955       49208 :             return JSONValue::pointer_t(new JSONValue(pos, obj));
     956             :         }
     957             :         break;
     958             : 
     959             :     case Node::node_t::NODE_OPEN_SQUARE_BRACKET: // read an array
     960             :         {
     961        8589 :             JSONValue::array_t array;
     962             : 
     963       17178 :             Position pos(n->get_position());
     964        8589 :             n = f_lexer->get_next_token();
     965        8589 :             if(n->get_type() != Node::node_t::NODE_CLOSE_SQUARE_BRACKET)
     966             :             {
     967             :                 for(;;)
     968             :                 {
     969       13325 :                     JSONValue::pointer_t value(read_json_value(n)); // recursive
     970       13325 :                     if(!value)
     971             :                     {
     972             :                         // empty values mean we got an error, stop short!
     973           4 :                         return value;
     974             :                     }
     975       13321 :                     array.push_back(value);
     976       13321 :                     n = f_lexer->get_next_token();
     977       13321 :                     if(n->get_type() == Node::node_t::NODE_CLOSE_SQUARE_BRACKET)
     978             :                     {
     979        8578 :                         break;
     980             :                     }
     981        4743 :                     if(n->get_type() != Node::node_t::NODE_COMMA)
     982             :                     {
     983           2 :                         Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_COMMA_EXPECTED, n->get_position());
     984           2 :                         msg << "expected a comma (,) to separate two JSON array items.";
     985           2 :                         return JSONValue::pointer_t();
     986             :                     }
     987        4741 :                     n = f_lexer->get_next_token();
     988        4741 :                 }
     989             :             }
     990             : 
     991       17172 :             return JSONValue::pointer_t(new JSONValue(pos, array));
     992             :         }
     993             :         break;
     994             : 
     995             :     case Node::node_t::NODE_STRING:
     996       49305 :         return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_string()));
     997             : 
     998             :     case Node::node_t::NODE_SUBTRACT:
     999             :         // negative number...
    1000          66 :         n = f_lexer->get_next_token();
    1001          66 :         switch(n->get_type())
    1002             :         {
    1003             :         case Node::node_t::NODE_FLOAT64:
    1004             :             {
    1005          32 :                 Float64 f(n->get_float64());
    1006          32 :                 if(!f.is_NaN())
    1007             :                 {
    1008          32 :                     f.set(-f.get());
    1009          32 :                     n->set_float64(f);
    1010             :                 }
    1011             :                 // else ... should we err about this one?
    1012             :             }
    1013          32 :             return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_float64()));
    1014             : 
    1015             :         case Node::node_t::NODE_INT64:
    1016             :             {
    1017          33 :                 Int64 i(n->get_int64());
    1018          33 :                 i.set(-i.get());
    1019          33 :                 n->set_int64(i);
    1020             :             }
    1021          33 :             return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_int64()));
    1022             : 
    1023             :         default:
    1024           1 :             Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_TOKEN, n->get_position());
    1025           1 :             msg << "unexpected token (" << n->get_type_name() << ") found after a '-' sign, a number was expected.";
    1026           1 :             return JSONValue::pointer_t();
    1027             : 
    1028             :         }
    1029             :         /*NOT_REACHED*/
    1030             :         break;
    1031             : 
    1032             :     case Node::node_t::NODE_TRUE:
    1033          75 :         return JSONValue::pointer_t(new JSONValue(n->get_position(), true));
    1034             : 
    1035             :     default:
    1036      103334 :         Message msg(message_level_t::MESSAGE_LEVEL_ERROR, err_code_t::AS_ERR_UNEXPECTED_TOKEN, n->get_position());
    1037      103334 :         msg << "unexpected token (" << n->get_type_name() << ") found in a JSON input stream.";
    1038      103334 :         return JSONValue::pointer_t();
    1039             : 
    1040             :     }
    1041             : }
    1042             : 
    1043             : 
    1044             : /** \brief Save the JSON in the specified file.
    1045             :  *
    1046             :  * This function is used to save this JSON in the specified file.
    1047             :  *
    1048             :  * One can also specified a header, in most cases a comment that
    1049             :  * gives copyright, license information and eventually some information
    1050             :  * explaining what that file is about.
    1051             :  *
    1052             :  * \param[in] filename  The name of the file on disk.
    1053             :  * \param[in] header  A header to be saved before the JSON data.
    1054             :  *
    1055             :  * \return true if the save() succeeded.
    1056             :  *
    1057             :  * \sa output()
    1058             :  */
    1059          13 : bool JSON::save(String const& filename, String const& header) const
    1060             : {
    1061          13 :     FileOutput::pointer_t out(new FileOutput());
    1062          13 :     if(!out->open(filename))
    1063             :     {
    1064           1 :         Message msg(message_level_t::MESSAGE_LEVEL_FATAL, err_code_t::AS_ERR_CANNOT_COMPILE, out->get_position());
    1065           1 :         msg << "could not open output file \"" << filename << "\".";
    1066           1 :         return false;
    1067             :     }
    1068             : 
    1069          12 :     return output(out, header);
    1070             : }
    1071             : 
    1072             : 
    1073             : /** \brief Output this JSON to the specified output.
    1074             :  *
    1075             :  * This function saves this JSON to the specified output object: \p out.
    1076             :  *
    1077             :  * If a header is specified (i.e. \p header is not an empty string) then
    1078             :  * it gets saved before any JSON data.
    1079             :  *
    1080             :  * The output file is made a UTF-8 text file as the function first
    1081             :  * saves a BOM in the file. Note that this means you should NOT
    1082             :  * save anything in that file before calling this function. You
    1083             :  * may, however, write more data (a footer) on return.
    1084             :  *
    1085             :  * \note
    1086             :  * Functions called by this function may generate the cyclic exception.
    1087             :  * This happens if your JSON tree is cyclic which means that a child
    1088             :  * element points back to one of its parent.
    1089             :  *
    1090             :  * \exception exception_invalid_data
    1091             :  * This exception is raised in the event the JSON does not have
    1092             :  * any data to be saved. This happens if you create a JSON object
    1093             :  * and never load or parse a valid JSON or call the set_value()
    1094             :  * function.
    1095             :  *
    1096             :  * \param[in] out  The output stream where the JSON is to be saved.
    1097             :  * \param[in] header  A string representing a header. It should
    1098             :  *                    be written in a C or C++ comment for the
    1099             :  *                    parser to be able to re-read the file seamlessly.
    1100             :  *
    1101             :  * \return true if the data was successfully written to \p out.
    1102             :  */
    1103          33 : bool JSON::output(Output::pointer_t out, String const& header) const
    1104             : {
    1105          33 :     if(!f_value)
    1106             :     {
    1107             :         // should we instead output "null"?
    1108           1 :         throw exception_invalid_data("this JSON has no value to output");
    1109             :     }
    1110             : 
    1111             :     // start with a BOM so the file is clearly marked as being UTF-8
    1112          32 :     as2js::String bom;
    1113          32 :     bom += String::STRING_BOM;
    1114          32 :     out->write(bom);
    1115             : 
    1116          32 :     if(!header.empty())
    1117             :     {
    1118          14 :         out->write(header);
    1119          14 :         out->write("\n");
    1120             :     }
    1121             : 
    1122          32 :     out->write(f_value->to_string());
    1123             : 
    1124          32 :     return true;
    1125             : }
    1126             : 
    1127             : 
    1128             : /** \brief Set the value of this JSON object.
    1129             :  *
    1130             :  * This function is used to define the value of this JSON object. This
    1131             :  * is used whenever you create a JSON in memory and want to save it
    1132             :  * on disk or send it to a client.
    1133             :  *
    1134             :  * \param[in] value  The JSONValue to save in this JSON object.
    1135             :  */
    1136          13 : void JSON::set_value(JSON::JSONValue::pointer_t value)
    1137             : {
    1138          13 :     f_value = value;
    1139          13 : }
    1140             : 
    1141             : 
    1142             : /** \brief Retrieve the value of the JSON object.
    1143             :  *
    1144             :  * This function returns the current value of this JSON object. This
    1145             :  * is the function you need to call after a call to the load() or
    1146             :  * parse() functions used to read a JSON file from an input stream.
    1147             :  *
    1148             :  * Note that this function gives you the root JSONValue object of
    1149             :  * the JSON object. You can then read the data or modify it as
    1150             :  * required. If you make changes, you may later call the save()
    1151             :  * or output() functions to save the changes to a file or
    1152             :  * an output stream.
    1153             :  *
    1154             :  * \return A pointer to the JSONValue of this JSON object.
    1155             :  */
    1156          52 : JSON::JSONValue::pointer_t JSON::get_value() const
    1157             : {
    1158          52 :     return f_value;
    1159             : }
    1160             : 
    1161             : 
    1162          63 : }
    1163             : // namespace as2js
    1164             : 
    1165             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.10