diff --git a/.gitignore b/.gitignore index 1eee3a2..4baf84a 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,5 @@ lib/searpc-fcall.h lib/searpc-signature.h tests/test-searpc cscope* -pysearpc/rpc_table.py \ No newline at end of file +pysearpc/rpc_table.py +*.pc diff --git a/Makefile.am b/Makefile.am index 994ae96..a6e31af 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,8 +3,15 @@ AM_CPPFLAGS = -I$(top_srcdir)/lib ACLOCAL_AMFLAGS = -I m4 GNU_STANDARD_FILES = README.markdown COPYING AUTHORS ChangeLog NEWS +pcfiles = libsearpc.pc + +pkgconfig_DATA = $(pcfiles) +pkgconfigdir = $(libdir)/pkgconfig + EXTRA_DIST = $(GNU_STANDARD_FILES) +EXTRA_DIST += libsearpc.pc.in + if COMPILE_PYTHON PYTHONDIR = pysearpc endif @@ -13,4 +20,4 @@ if COMPILE_DEMO MAKE_DEMO = demo endif -SUBDIRS = lib ${PYTHONDIR} ${MAKE_DEMO} tests \ No newline at end of file +SUBDIRS = json-glib lib ${PYTHONDIR} ${MAKE_DEMO} tests \ No newline at end of file diff --git a/README.markdown b/README.markdown index ce72ac3..2e654ae 100644 --- a/README.markdown +++ b/README.markdown @@ -229,5 +229,4 @@ The following packages are required to build libsearpc: * glib-2.0 >= 2.16.0 * gobject-2.0 >= 2.16.0 -* json-glib-1.0 >= 0.10.2 -* pygobject-2.0 >= 2.0 (if you choose to build pysearpc) +* simplejson (for pysearpc) diff --git a/configure.ac b/configure.ac index f3024dc..6d81bae 100644 --- a/configure.ac +++ b/configure.ac @@ -94,23 +94,12 @@ AC_SUBST(SERVER_PKG_RPATH) # Checks for libraries. -GLIB_REQUIRED=2.16.0 -JSON_GLIB_REQUIRED=0.10.2 - -# check and subst glib -PKG_CHECK_MODULES(GLIB2, [glib-2.0 >= $GLIB_REQUIRED]) -AC_SUBST(GLIB2_CFLAGS) -AC_SUBST(GLIB2_LIBS) +GLIB_REQUIRED=2.26.0 # check and subst gobject -PKG_CHECK_MODULES(GOBJECT, [gobject-2.0 >= $GLIB_REQUIRED]) -AC_SUBST(GOBJECT_CFLAGS) -AC_SUBST(GOBJECT_LIBS) - -# check and subst json-glib -PKG_CHECK_MODULES(JSON_GLIB, [json-glib-1.0 >= $JSON_GLIB_REQUIRED]) -AC_SUBST(JSON_GLIB_CFLAGS) -AC_SUBST(JSON_GLIB_LIBS) +PKG_CHECK_MODULES(GLIB, [gobject-2.0 >= $GLIB_REQUIRED]) +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) # conditinally check python and subst if test x${compile_python} = xyes; then @@ -134,11 +123,13 @@ if test x${compile_python} = xyes; then fi fi +AC_CONFIG_SUBDIRS([json-glib]) AC_CONFIG_FILES([Makefile lib/Makefile demo/Makefile pysearpc/Makefile + libsearpc.pc tests/Makefile]) AC_OUTPUT diff --git a/demo/Makefile.am b/demo/Makefile.am index f1dc5fa..11e9f63 100644 --- a/demo/Makefile.am +++ b/demo/Makefile.am @@ -1,17 +1,20 @@ -AM_CFLAGS = @GLIB2_CFLAGS@ @GOBJECT_CFLAGS@ @JSON_GLIB_CFLAGS@ -I${top_builddir}/lib -I${top_srcdir}/lib +AM_CFLAGS = @GLIB_CFLAGS@ \ + -I${top_srcdir}/lib noinst_PROGRAMS = searpc-demo-server searpc-demo-client searpc-async-client searpc_demo_server_SOURCES = searpc-demo-server.c searpc-demo-packet.h -searpc_demo_server_LDADD = ${top_builddir}/lib/libsearpc.la @LIB_WS32@ +searpc_demo_server_LDADD = ${top_builddir}/lib/libsearpc.la @LIB_WS32@ \ + @GLIB_CFLAGS@ searpc_demo_client_SOURCES = searpc-demo-client.c searpc-demo-packet.h -searpc_demo_client_LDADD = ${top_builddir}/lib/libsearpc.la @LIB_WS32@ +searpc_demo_client_LDADD = ${top_builddir}/lib/libsearpc.la @LIB_WS32@ \ + @GLIB_CFLAGS@ searpc_async_client_SOURCES = demo-async-client.c searpc-demo-packet.h -searpc_async_client_LDADD = ${top_builddir}/lib/libsearpc.la @LIB_WS32@ - +searpc_async_client_LDADD = ${top_builddir}/lib/libsearpc.la @LIB_WS32@ \ + @GLIB_CFLAGS@ diff --git a/json-glib/.gitignore b/json-glib/.gitignore new file mode 100644 index 0000000..d5338fa --- /dev/null +++ b/json-glib/.gitignore @@ -0,0 +1,42 @@ +ABOUT-NLS +INSTALL +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +/build/autotools/*.m4 +!/build/autotools/as-compiler-flag.m4 +!/build/autotools/Makefile.am.* +compile +configure +config.guess +config.h +config.h.in +config.h.in~ +config.log +config.rpath +config.status +config.sub +depcomp +install-sh +.deps +.libs +*.o +*.lo +/json-glib/json-enum-types.[ch] +/json-glib/json-marshal.[ch] +/json-glib/json-version.h +/json-glib/Json-1.0.gir +/json-glib/Json-1.0.typelib +/json-glib/*.la +/json-glib/gcov-report.txt +/json-glib/stamp-enum-types +/json-glib/stamp-marshal +libtool +ltmain.sh +missing +stamp-h1 +test-report.xml +test-report.html +.*.swp +*.stamp diff --git a/json-glib/COPYING b/json-glib/COPYING new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/json-glib/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/json-glib/ChangeLog b/json-glib/ChangeLog new file mode 100644 index 0000000..bf84356 --- /dev/null +++ b/json-glib/ChangeLog @@ -0,0 +1,7443 @@ +commit 6d64c9a4525001700b437af09676013954eb6eb5 +Author: Emmanuele Bassi +Date: Wed Oct 26 12:32:10 2011 +0100 + + Release JSON-GLib 0.14.2 + + NEWS | 5 +++++ + configure.ac | 4 ++-- + 2 files changed, 7 insertions(+), 2 deletions(-) + +commit 86bf97d993e638f0131fbf17360d03bc7b34e4a0 +Author: Emmanuele Bassi +Date: Mon Oct 17 11:19:57 2011 +0100 + + docs: Fix typo in JsonObject's description + + https://bugzilla.gnome.org/show_bug.cgi?id=660893 + (cherry picked from commit 7636e9de9414304f940837ee828d23e4608e9f32) + + Signed-off-by: Emmanuele Bassi + + json-glib/json-object.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 45a2f813243652ee2ee82de07348b3218d308861 +Author: Emmanuele Bassi +Date: Wed Oct 12 12:31:27 2011 +0100 + + Use g_value_[sg]et_schar() with GLib ≥ 2.31 + + The non-explicitly signed variant is deprecated. + (cherry picked from commit 4ab8059df6ad106bf8958642aa1126925d79128c) + + Signed-off-by: Emmanuele Bassi + + json-glib/json-gobject.c | 10 +++++++++- + 1 files changed, 9 insertions(+), 1 deletions(-) + +commit 692d8275b0d2a7883af4088c95a8403d902ffd10 +Author: Matthias Clasen +Date: Wed Oct 12 07:15:23 2011 -0400 + + Adapt to the demise of single includes in GLib + (cherry picked from commit f904ad102004258dc7b839b93a7904517cc4c623) + + Signed-off-by: Emmanuele Bassi + + json-glib/json-scanner.h | 4 +--- + json-glib/tests/gvariant.c | 2 +- + json-glib/tests/node.c | 2 +- + 3 files changed, 3 insertions(+), 5 deletions(-) + +commit e95d570170a89c1175adf06720ba1efad96372bd +Author: Emmanuele Bassi +Date: Mon Oct 17 11:00:52 2011 +0100 + + Conditionally use g_list_free_full() + + JSON-GLib depends on GLib ≥ 2.26, and g_list_free_full() was introduced + in GLib 2.28, so we have to check against the GLib version if we want to + use it. + + https://bugzilla.gnome.org/show_bug.cgi?id=661244 + + json-glib/json-path.c | 19 ++++++++++++++++++- + 1 files changed, 18 insertions(+), 1 deletions(-) + +commit d498c15566cba0123d35d271b727542309ba7d83 +Author: Chun-wei Fan +Date: Tue Sep 27 15:21:06 2011 +0800 + + Update VS9 property sheet + + Seperate intermediate folders to avoid errors/warnings due to files in use + during compilation. + + build/win32/vs9/json-glib.vsprops | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 3543fd3220d5ab567be5d924ef94ee69ae93d5e7 +Author: Emmanuele Bassi +Date: Mon Sep 19 14:41:39 2011 +0100 + + Post-release version bump to 0.14.1 + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 35279b587f0b884fd606be77fd06e4fef919a300 +Author: Emmanuele Bassi +Date: Mon Sep 19 14:37:45 2011 +0100 + + Release JSON-GLib 0.14.0 + + configure.ac | 4 +- + po/pl.po | 74 +++++++++++++++++++++++++++++----------------------------- + 2 files changed, 39 insertions(+), 39 deletions(-) + +commit 3830326516285e6e86d3fa9c58ef7531679dfec0 +Author: Piotr Drąg +Date: Fri Sep 9 16:27:37 2011 +0200 + + Added Polish translation + + po/pl.po | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 172 insertions(+), 0 deletions(-) + +commit 67a460d9ea8b485e740d1df34943b8df974f4255 +Author: Emmanuele Bassi +Date: Fri Sep 9 15:00:41 2011 +0100 + + Post-release version bump to 0.13.91 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 1cb4ed1fac84026a19b35ce10c54df8a6ed12959 +Author: Emmanuele Bassi +Date: Fri Sep 9 14:57:13 2011 +0100 + + Release JSON-GLib 0.13.90 (0.14.0-rc1) + + NEWS | 2 ++ + configure.ac | 2 +- + 2 files changed, 3 insertions(+), 1 deletions(-) + +commit 18a83ddad87c92ae78010c678a516087ba012859 +Author: Emmanuele Bassi +Date: Fri Sep 9 14:58:17 2011 +0100 + + build: Fix the path to the 'missing' script + + Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 9f75475871cc8723e0a41d6c8971f741a405cb9f +Author: Emmanuele Bassi +Date: Fri Sep 9 14:00:36 2011 +0100 + + reader: Do more strict validation + + JsonReader should perform more strict validation, and put itself in an + error state whenever the user asks for values or information on nodes + that are not there, or not of the right type. + + https://bugzilla.gnome.org/show_bug.cgi?id=658632 + + json-glib/json-reader.c | 177 ++++++++++++++++++++++++++++++++++++++++------ + json-glib/json-reader.h | 10 +++- + 2 files changed, 163 insertions(+), 24 deletions(-) + +commit e7f2d505de39a9dbdea1a5879c13408c7a394a18 +Author: Chun-wei Fan +Date: Wed Aug 31 13:33:41 2011 +0800 + + Update Win32 and VS Readme files + + Tell people about the GNOME Live! page that outlines building + JSON-GLib and its dependencies with Visual C++. + + build/win32/vs10/README.txt | 5 +++++ + build/win32/vs9/README.txt | 5 +++++ + 2 files changed, 10 insertions(+), 0 deletions(-) + +commit c2f5b8bc64f9ac06b2dda8662924d88cc759718e +Author: Emmanuele Bassi +Date: Tue Aug 23 20:42:15 2011 +0100 + + gobject: Deserialize CONSTRUCT properties + + While we have to give up deserializing CONSTRUCT_ONLY properties with + JsonSerializable, CONSTRUCT properties should just be deserialized like + any other property. + + Sadly, there's still a refuse in the json_gobject_new() code that skips + CONSTRUCT properties along with CONSTRUCT_ONLY ones — a remnant of a + period when we deserialized them both without JsonSerializable. + + https://bugzilla.gnome.org/show_bug.cgi?id=655526 + + json-glib/json-gobject.c | 13 +++++-------- + json-glib/tests/serialize-complex.c | 19 ++++++++++++++++++- + json-glib/tests/serialize-full.c | 3 ++- + 3 files changed, 25 insertions(+), 10 deletions(-) + +commit bcfac335a8e4086ff6afa5f267f9886a4d1b6370 +Author: Tristan Van Berkom +Date: Sun Jul 31 02:38:08 2011 -0400 + + Handle integer to float/double conversions in json_deserialize_pspec() + + It happens that while serializing that float/double properties are + set to round numbers, this results in deserialization as gint64 + values and a parse failure. This patch addresses it by accepting + round gint64 numbers as valid values for float/double properties. + + json-glib/json-gobject.c | 22 +++++++++++++++++++++- + 1 files changed, 21 insertions(+), 1 deletions(-) + +commit 996d89911d71f54f9a476242f1bd43c077d4428c +Author: Tristan Van Berkom +Date: Wed Jul 13 15:27:07 2011 -0400 + + Added an additional warning to json_gobject_new() + + The same warning that is fired when failing to deserialize + properties normally needed to be added for the case that + we fail to deserialize construct-only properties. + + json-glib/json-gobject.c | 7 ++++++- + 1 files changed, 6 insertions(+), 1 deletions(-) + +commit 4315308a19e4cf474451cecf8b69aceddda91f49 +Author: Tristan Van Berkom +Date: Sat Jul 9 15:23:36 2011 -0400 + + Avoid serializing default property values only after + consulting the JsonSerializable. + + This patch gives the JsonSerializable class a chance to decide + whether it's appropriate to serialize a property before + JsonGObject checks for a default value and skips the property. + + json-glib/json-gobject.c | 10 ++-------- + json-glib/json-serializable.c | 4 ++++ + 2 files changed, 6 insertions(+), 8 deletions(-) + +commit 4ba9a6a81709221ba58d0f8e067de660eb96914e +Author: Tristan Van Berkom +Date: Thu Jul 7 16:35:21 2011 -0400 + + Fixed badly handled fundamental types in json_deserialize_pspec(). + + This patch fixes json_deserialize_pspec() to return FALSE when + failing to deserialize some properties. Consequently the patch + ensures we get the intended warnings when failing to deserialize + pspecs for some fundamental types: + Failed to deserialize property "foo" of type "int" on object "bar" + + json-glib/json-gobject.c | 42 ++++++++++++++++++++++++++++++------------ + 1 files changed, 30 insertions(+), 12 deletions(-) + +commit 24fa4503ad5d85bf60027bd77c434b2a596b1c17 +Author: Laurent Bigonville +Date: Wed Jul 6 12:34:56 2011 +0200 + + Fix GVariant creation on some architectures (bug #650483) + + Fix casting issue in Gvariant creation on some architectures. + + json-glib/json-gvariant.c | 46 +++++++++++++++++++++++++++++++++++++++----- + 1 files changed, 40 insertions(+), 6 deletions(-) + +commit dd3c30c5821f9f6bac4cc9016c49c7e6250377eb +Author: Emmanuele Bassi +Date: Tue Jul 5 15:07:20 2011 +0100 + + tests/path: Link expressions and results + + Use a structure to keep the testable expressions and their results + together. + + json-glib/tests/path.c | 74 ++++++++++++++++++++++++++++++----------------- + 1 files changed, 47 insertions(+), 27 deletions(-) + +commit acb33a7e6bd3d05d36e7278fafdcb4c35ec88388 +Author: Emmanuele Bassi +Date: Tue Jul 5 14:23:13 2011 +0100 + + build: Replace AC_HELP_STRING with AS_HELP_STRING + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit c296ca9ed06b8e4b54a4bd8003593b3a98b94f6b +Author: Emmanuele Bassi +Date: Tue Jul 5 14:22:13 2011 +0100 + + build: Allow m4 substitution for conditional defaults + + To compensate for AC_HELP_STRING()'s braindamage. + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 1fb457af51a5632956fd679deca0021becb5468b +Author: Emmanuele Bassi +Date: Tue Jul 5 14:22:01 2011 +0100 + + build: Clean up configure.ac's preamble + + configure.ac | 7 +++++-- + 1 files changed, 5 insertions(+), 2 deletions(-) + +commit 75e370e53cb5d31d4a366fc5f126583872f1349a +Author: Chun-wei Fan +Date: Wed Jun 29 14:29:12 2011 +0800 + + Added README.txt's for Visual Studio support + + Added README.txt's and included them in distribution to tell + people how JSON-GLib can be build on Windows using Visual C++. + + build/win32/vs10/Makefile.am | 1 + + build/win32/vs10/README.txt | 87 ++++++++++++++++++++++++++++++++++++++++++ + build/win32/vs9/Makefile.am | 1 + + build/win32/vs9/README.txt | 86 +++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 175 insertions(+), 0 deletions(-) + +commit 7da6eb52937588537754be7e0b496f51077165d2 +Author: Chun-wei Fan +Date: Wed Jun 29 12:54:52 2011 +0800 + + Re-attempt to correct EOL on VS2010 solution + + This time I realized that I needed to set autocrlf=false on my Windows side + ... ugh... + + This is one of those files that must have CRLF line endings to work + correctly :| + + build/win32/vs10/json-glib.sln | 332 ++++++++++++++++++++-------------------- + 1 files changed, 166 insertions(+), 166 deletions(-) + +commit 4ef07b2c6e7d4d444f8ec6ce93c27f7189b942a0 +Author: Travis Reitter +Date: Thu Jun 23 16:54:38 2011 -0700 + + Link the tests against the appropriate libraries. + + This avoids some linking failures. + + Closes: bgo#653295 - json-glib tests need to link against libgobject-2 + + json-glib/tests/Makefile.am | 5 ++++- + 1 files changed, 4 insertions(+), 1 deletions(-) + +commit c9ac9ce2d417210ccb06d0445a809ce9cd57c7a5 +Author: Chun-wei Fan +Date: Mon Jun 20 11:59:57 2011 +0800 + + Update VS 2010 property sheet + + Make up for missed backslash + + build/win32/vs10/json-glib.props | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 230801c694169e50ca45df7df431adbdb89cab18 +Author: Chun-wei Fan +Date: Mon Jun 20 11:58:03 2011 +0800 + + Refine Visual C++ 2010 projects + + Should have realized that my previous commit on Visual C++ 2010 projects + (refinement) can be consolidated into the property sheet... + + build/win32/vs10/array.vcxproj | 8 -------- + build/win32/vs10/boxed.vcxproj | 8 -------- + build/win32/vs10/builder.vcxproj | 8 -------- + build/win32/vs10/generator.vcxproj | 8 -------- + build/win32/vs10/gvariant.vcxproj | 10 +--------- + build/win32/vs10/json-glib.props | 2 +- + build/win32/vs10/json-glib.vcxprojin | 8 -------- + build/win32/vs10/node.vcxproj | 10 +--------- + build/win32/vs10/object.vcxproj | 10 +--------- + build/win32/vs10/parser.vcxproj | 10 +--------- + build/win32/vs10/path.vcxproj | 10 +--------- + build/win32/vs10/reader.vcxproj | 8 -------- + build/win32/vs10/serialize-complex.vcxproj | 8 -------- + build/win32/vs10/serialize-full.vcxproj | 8 -------- + build/win32/vs10/serialize-simple.vcxproj | 8 -------- + 15 files changed, 6 insertions(+), 118 deletions(-) + +commit d6fdf92ee000666ceb7892151673b4b56517cdd9 +Author: Chun-wei Fan +Date: Mon Jun 20 01:11:18 2011 +0800 + + Further refinements to Visual C++ 2010 projects + + -Seperate the intermediate folders for each configuration so that MSB6003 + (cannot write into MSBuild log file as file is in use) errors and other + related MSBuild errors can be avoided, and will avoid rebuilding/relinking + projects when no sources are changed. + + -Fixed dependencies for the "install" project + + build/win32/vs10/array.vcxproj | 8 ++++++-- + build/win32/vs10/boxed.vcxproj | 8 ++++++-- + build/win32/vs10/builder.vcxproj | 8 ++++++-- + build/win32/vs10/generator.vcxproj | 8 ++++++-- + build/win32/vs10/gvariant.vcxproj | 9 ++++++++- + build/win32/vs10/install.vcxproj | 18 +++++++++--------- + build/win32/vs10/json-glib.vcxprojin | 8 ++++++-- + build/win32/vs10/node.vcxproj | 8 ++++++-- + build/win32/vs10/object.vcxproj | 8 ++++++-- + build/win32/vs10/parser.vcxproj | 8 ++++++-- + build/win32/vs10/path.vcxproj | 9 ++++++++- + build/win32/vs10/reader.vcxproj | 8 ++++++-- + build/win32/vs10/serialize-complex.vcxproj | 8 ++++++-- + build/win32/vs10/serialize-full.vcxproj | 8 ++++++-- + build/win32/vs10/serialize-simple.vcxproj | 8 ++++++-- + 15 files changed, 97 insertions(+), 35 deletions(-) + +commit 8518effca75d2249b8186083b950644ba4098d47 +Author: Chun-wei Fan +Date: Mon Jun 20 00:13:29 2011 +0800 + + VS 2008 project files update + + Update RootNamespace as well for consistency sake. + + Thanks to Emmanuele for updating the VS9 projects earlier due to the + changes in the filenames of the test sources + + build/win32/vs9/array.vcproj | 2 +- + build/win32/vs9/boxed.vcproj | 2 +- + build/win32/vs9/builder.vcproj | 2 +- + build/win32/vs9/generator.vcproj | 2 +- + build/win32/vs9/gvariant.vcproj | 2 +- + build/win32/vs9/node.vcproj | 2 +- + build/win32/vs9/object.vcproj | 2 +- + build/win32/vs9/parser.vcproj | 2 +- + build/win32/vs9/path.vcproj | 2 +- + build/win32/vs9/reader.vcproj | 2 +- + 10 files changed, 10 insertions(+), 10 deletions(-) + +commit 49ecbb4e2449ad55623fb46a0072fd1fbaf837e1 +Author: Chun-wei Fan +Date: Mon Jun 20 00:04:30 2011 +0800 + + Update VS2010 project files + + -Reflect on the change of filenames of test sources, and updated dist + stuff accordingly. + -Clean up projects a little bit. + + build/win32/vs10/Makefile.am | 36 +++--- + build/win32/vs10/array-test.vcxproj | 172 ---------------------- + build/win32/vs10/array-test.vcxproj.filters | 14 -- + build/win32/vs10/array.vcxproj | 171 ++++++++++++++++++++++ + build/win32/vs10/array.vcxproj.filters | 14 ++ + build/win32/vs10/boxed.vcxproj | 1 - + build/win32/vs10/builder-test.vcxproj | 172 ---------------------- + build/win32/vs10/builder-test.vcxproj.filters | 14 -- + build/win32/vs10/builder.vcxproj | 171 ++++++++++++++++++++++ + build/win32/vs10/builder.vcxproj.filters | 14 ++ + build/win32/vs10/generator-test.vcxproj | 172 ---------------------- + build/win32/vs10/generator-test.vcxproj.filters | 14 -- + build/win32/vs10/generator.vcxproj | 171 ++++++++++++++++++++++ + build/win32/vs10/generator.vcxproj.filters | 14 ++ + build/win32/vs10/gvariant-test.vcxproj | 159 --------------------- + build/win32/vs10/gvariant-test.vcxproj.filters | 14 -- + build/win32/vs10/gvariant.vcxproj | 159 +++++++++++++++++++++ + build/win32/vs10/gvariant.vcxproj.filters | 14 ++ + build/win32/vs10/install.vcxproj | 1 - + build/win32/vs10/json-glib.props | 1 - + build/win32/vs10/json-glib.sln | 18 ++-- + build/win32/vs10/json-glib.vcxprojin | 1 - + build/win32/vs10/node-test.vcxproj | 172 ---------------------- + build/win32/vs10/node-test.vcxproj.filters | 14 -- + build/win32/vs10/node.vcxproj | 171 ++++++++++++++++++++++ + build/win32/vs10/node.vcxproj.filters | 14 ++ + build/win32/vs10/object-test.vcxproj | 172 ---------------------- + build/win32/vs10/object-test.vcxproj.filters | 13 -- + build/win32/vs10/object.vcxproj | 171 ++++++++++++++++++++++ + build/win32/vs10/object.vcxproj.filters | 13 ++ + build/win32/vs10/parser-test.vcxproj | 172 ---------------------- + build/win32/vs10/parser-test.vcxproj.filters | 14 -- + build/win32/vs10/parser.vcxproj | 171 ++++++++++++++++++++++ + build/win32/vs10/parser.vcxproj.filters | 14 ++ + build/win32/vs10/path-test.vcxproj | 159 --------------------- + build/win32/vs10/path-test.vcxproj.filters | 14 -- + build/win32/vs10/path.vcxproj | 159 +++++++++++++++++++++ + build/win32/vs10/path.vcxproj.filters | 14 ++ + build/win32/vs10/reader-test.vcxproj | 173 ----------------------- + build/win32/vs10/reader-test.vcxproj.filters | 14 -- + build/win32/vs10/reader.vcxproj | 172 ++++++++++++++++++++++ + build/win32/vs10/reader.vcxproj.filters | 14 ++ + build/win32/vs10/serialize-complex.vcxproj | 1 - + build/win32/vs10/serialize-full.vcxproj | 1 - + build/win32/vs10/serialize-simple.vcxproj | 1 - + 45 files changed, 1668 insertions(+), 1682 deletions(-) + +commit 54711b3e093e00457a355926516ba87c8171f17d +Author: Emmanuele Bassi +Date: Sun Jun 19 12:18:04 2011 +0100 + + build/vs9: Update after the changes in json-glib/tests + + build/win32/vs9/Makefile.am | 24 +++--- + build/win32/vs9/array-test.vcproj | 157 ------------------------------- + build/win32/vs9/array.vcproj | 157 +++++++++++++++++++++++++++++++ + build/win32/vs9/boxed.vcproj | 2 +- + build/win32/vs9/builder-test.vcproj | 157 ------------------------------- + build/win32/vs9/builder.vcproj | 157 +++++++++++++++++++++++++++++++ + build/win32/vs9/generator-test.vcproj | 157 ------------------------------- + build/win32/vs9/generator.vcproj | 157 +++++++++++++++++++++++++++++++ + build/win32/vs9/gvariant-test.vcproj | 147 ----------------------------- + build/win32/vs9/gvariant.vcproj | 147 +++++++++++++++++++++++++++++ + build/win32/vs9/json-glib.sln | 18 ++-- + build/win32/vs9/node-test.vcproj | 157 ------------------------------- + build/win32/vs9/node.vcproj | 157 +++++++++++++++++++++++++++++++ + build/win32/vs9/object-test.vcproj | 155 ------------------------------ + build/win32/vs9/object.vcproj | 155 ++++++++++++++++++++++++++++++ + build/win32/vs9/parser-test.vcproj | 166 --------------------------------- + build/win32/vs9/parser.vcproj | 166 +++++++++++++++++++++++++++++++++ + build/win32/vs9/path-test.vcproj | 147 ----------------------------- + build/win32/vs9/path.vcproj | 147 +++++++++++++++++++++++++++++ + build/win32/vs9/reader-test.vcproj | 158 ------------------------------- + build/win32/vs9/reader.vcproj | 158 +++++++++++++++++++++++++++++++ + 21 files changed, 1423 insertions(+), 1423 deletions(-) + +commit 78e896c64e8cbccf1b55291521f11a3f5093080a +Author: Emmanuele Bassi +Date: Sun Jun 19 12:08:03 2011 +0100 + + build: Rename test binaries + + It's pointless to add the '-test' suffix to files under the tests/ directory. + + json-glib/tests/Makefile.am | 18 +- + json-glib/tests/array-test.c | 122 ------ + json-glib/tests/array.c | 122 ++++++ + json-glib/tests/builder-test.c | 161 -------- + json-glib/tests/builder.c | 161 ++++++++ + json-glib/tests/generator-test.c | 330 ---------------- + json-glib/tests/generator.c | 330 ++++++++++++++++ + json-glib/tests/gvariant-test.c | 233 ----------- + json-glib/tests/gvariant.c | 233 +++++++++++ + json-glib/tests/node-test.c | 112 ------ + json-glib/tests/node.c | 112 ++++++ + json-glib/tests/object-test.c | 165 -------- + json-glib/tests/object.c | 165 ++++++++ + json-glib/tests/parser-test.c | 785 -------------------------------------- + json-glib/tests/parser.c | 785 ++++++++++++++++++++++++++++++++++++++ + json-glib/tests/path-test.c | 143 ------- + json-glib/tests/path.c | 143 +++++++ + json-glib/tests/reader-test.c | 134 ------- + json-glib/tests/reader.c | 134 +++++++ + 19 files changed, 2194 insertions(+), 2194 deletions(-) + +commit cef1f16cec529eeb03338f995a8b3d32b262df8b +Author: Emmanuele Bassi +Date: Sun Jun 19 12:00:01 2011 +0100 + + docs: Add index for symbols added in the 0.13 cycle + + doc/reference/json-glib-docs.xml | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +commit cc2543e101ca8fa41989bc8eb296f8d11b5b9cad +Author: Emmanuele Bassi +Date: Wed Jun 15 11:30:28 2011 +0100 + + build: Add tar-ustar and silent-rules automake options + + The tar-ustar format allows longer file names and other niceties — + though a format ratified in a standard from 1988 is still judged as + "possibly not widespread enough". I weep for the future of humanity + and software engineering. + + The silent-rules option is just added to avoid using AM_SILENT_RULES() + on a separate line. + + configure.ac | 3 +-- + 1 files changed, 1 insertions(+), 2 deletions(-) + +commit c106aa5b0fc0ca408328b95ba721eef6a9d560d2 +Author: Emmanuele Bassi +Date: Wed Jun 15 11:12:38 2011 +0100 + + Post-release version bump to 0.13.5 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 02b3ee77a35624a3d6e478ae3700100cb24bf18f +Author: Emmanuele Bassi +Date: Wed Jun 15 11:08:37 2011 +0100 + + Release JSON-GLib 0.13.4 + + NEWS | 6 ++++++ + configure.ac | 2 +- + 2 files changed, 7 insertions(+), 1 deletions(-) + +commit f37878ec1759283f78a6d580c1f78e9039717862 +Author: Emmanuele Bassi +Date: Wed Jun 15 11:08:12 2011 +0100 + + docs: Fix API reference missing symbols + + doc/reference/json-glib-sections.txt | 4 ++++ + json-glib/json-gobject.h | 6 ++++++ + 2 files changed, 10 insertions(+), 0 deletions(-) + +commit c994a9bfdc13aba1b7c5fd019853c68fb38148a6 +Author: Emmanuele Bassi +Date: Thu Jun 9 17:23:04 2011 +0100 + + Remove G_CONST_RETURN usage + + See GLib bug: + + https://bugzilla.gnome.org/show_bug.cgi?id=644611 + + The macro is going to be deprecated soon. + + json-glib/json-array.c | 2 +- + json-glib/json-node.c | 6 +++--- + json-glib/json-object.c | 2 +- + json-glib/json-reader.c | 6 +++--- + json-glib/json-reader.h | 6 +++--- + json-glib/json-types-private.h | 2 +- + json-glib/json-types.h | 8 ++++---- + 7 files changed, 16 insertions(+), 16 deletions(-) + +commit 74bb5d61a737cceffd04c8d9ae8d5db390eda5a2 +Author: Emmanuele Bassi +Date: Fri Jun 3 11:46:05 2011 +0100 + + Use the new atomic functions for refcounting + + The newly added g_atomic_int_dec_and_test() simplified the logic for + unreffing Object and Array. + + json-glib/json-array.c | 9 ++------- + json-glib/json-object.c | 9 ++------- + 2 files changed, 4 insertions(+), 14 deletions(-) + +commit 4f83868e3a18adac809aff111c5df5ff7af8fedf +Author: Emmanuele Bassi +Date: Fri Jun 3 11:18:27 2011 +0100 + + build: Remove deprecation disabling for GLib + + json-glib/Makefile.am | 1 - + 1 files changed, 0 insertions(+), 1 deletions(-) + +commit 65a95198a4a1bca7b418af1639c4bb24b09947c5 +Author: Chun-wei Fan +Date: Fri Jun 3 15:32:55 2011 +0800 + + Visual C++ support: projects for JSONPath test + + Added projects to build test program for JSONPath (path-test.c) + + build/win32/vs10/Makefile.am | 2 + + build/win32/vs10/install.vcxproj | 8 ++ + build/win32/vs10/json-glib.sln | 26 +++-- + build/win32/vs10/path-test.vcxproj | 159 ++++++++++++++++++++++++++++ + build/win32/vs10/path-test.vcxproj.filters | 14 +++ + build/win32/vs9/Makefile.am | 1 + + build/win32/vs9/json-glib.sln | 30 ++++-- + build/win32/vs9/path-test.vcproj | 147 +++++++++++++++++++++++++ + 8 files changed, 371 insertions(+), 16 deletions(-) + +commit 16bc9cccd32f4e1c7e9fc0da128aef969d0563b0 +Author: Chun-wei Fan +Date: Fri Jun 3 15:01:16 2011 +0800 + + Update config.h.win32.in + + Added definition of GETTEXT_PACKAGE due to the addition of the i18n + machinery (commit 67edce08670ce1da4956f87948985434ac5ae2ca) + + build/win32/config.h.win32.in | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit d93260c477930140f2ecf2927bd9f2a2d7f4f905 +Author: Chun-wei Fan +Date: Fri Jun 3 14:47:42 2011 +0800 + + Update VS property sheets + + -Add json-path.h to the list of headers to be copied in the "install" stage + + build/win32/vs10/json-glib.props | 2 ++ + build/win32/vs9/json-glib.vsprops | 1 + + 2 files changed, 3 insertions(+), 0 deletions(-) + +commit c3b367ca8bac245712f8390acab1b38a314972a9 +Author: Emmanuele Bassi +Date: Wed Jun 1 17:10:17 2011 +0100 + + serializable: Fix introspection annotations + + json-glib/json-serializable.c | 25 +++++++++++++++++++++++++ + 1 files changed, 25 insertions(+), 0 deletions(-) + +commit aee5a7fbb70e5cb714bcfef79a96c000f62e80db +Author: Emmanuele Bassi +Date: Wed Jun 1 17:01:02 2011 +0100 + + Remove unused files + + - AUTHORS + - MAINTAINER + replaced by json-glib.doap + + - ChangeLog + empty, and not necessary with the foreign automake option + + AUTHORS | 1 - + ChangeLog | 6 ------ + MAINTAINERS | 3 --- + configure.ac | 5 +++-- + 4 files changed, 3 insertions(+), 12 deletions(-) + +commit 1d4cdb76b6f97c723dd6e4c634bacc52d3199999 +Author: Emmanuele Bassi +Date: Wed Jun 1 13:35:16 2011 +0100 + + Update the ignore file for the po directory + + po/.gitignore | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 8863766ca4792383dad0e48550f8fb79d4c104e4 +Author: Emmanuele Bassi +Date: Wed Jun 1 13:21:32 2011 +0100 + + Mark GError messages for translations + + These errors might find their way into a UI. + + json-glib/json-gobject.c | 4 ++-- + json-glib/json-gvariant.c | 40 ++++++++++++++++++---------------------- + json-glib/json-parser.c | 5 ++++- + json-glib/json-path.c | 13 +++++++------ + json-glib/json-reader.c | 20 ++++++++++---------- + 5 files changed, 41 insertions(+), 41 deletions(-) + +commit 67edce08670ce1da4956f87948985434ac5ae2ca +Author: Emmanuele Bassi +Date: Wed Jun 1 13:09:01 2011 +0100 + + Add i18n machinery + + We need to translate the GError messages. + + .gitignore | 2 ++ + Makefile.am | 2 +- + build/autotools/Makefile.am | 1 + + build/autotools/as-linguas.m4 | 24 ++++++++++++++++++++++++ + configure.ac | 12 ++++++++++++ + json-glib/json-gobject.c | 2 ++ + json-glib/json-gvariant.c | 6 ++++++ + json-glib/json-parser.c | 2 ++ + json-glib/json-path.c | 4 +++- + json-glib/json-reader.c | 2 ++ + po/.gitignore | 13 +++++++++++++ + po/Makevars | 7 +++++++ + po/POTFILES.in | 6 ++++++ + 13 files changed, 81 insertions(+), 2 deletions(-) + +commit e3b5883d4435da7fe677eb9a7c74d2f83980f5da +Author: Emmanuele Bassi +Date: Wed Jun 1 12:49:28 2011 +0100 + + gobject: Do not serialize default values + + If a property is set to its default value then we can skip its + serialization, to keep the number of JSON object members down. + + json-glib/json-gobject.c | 7 +++++++ + 1 files changed, 7 insertions(+), 0 deletions(-) + +commit 8c424cc133575282371bff8d17295662267049e9 +Author: Emmanuele Bassi +Date: Wed Jun 1 12:48:20 2011 +0100 + + tests/serialize-complex: Modify the instance + + Do not test the defaults: create the GObject instance with different + values than the default ones from the GParamSpec. + + json-glib/tests/serialize-complex.c | 22 +++++++++++----------- + 1 files changed, 11 insertions(+), 11 deletions(-) + +commit 96b1e6b50f355c04e794ad7366bd33b9c3d1f81b +Author: Emmanuele Bassi +Date: Wed Jun 1 12:35:58 2011 +0100 + + serializable: Allow introspecting properties + + This allows a Serializable implementation to override the property list, + and the setter and the getter function. + + json-glib/json-gobject.c | 43 +++++++++++++++++----- + json-glib/json-gobject.h | 22 +++++++++++ + json-glib/json-serializable.c | 81 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 137 insertions(+), 9 deletions(-) + +commit c85fc93f6235b8b9d584585215b2fa860f07f2c3 +Author: Emmanuele Bassi +Date: Wed Jun 1 12:36:08 2011 +0100 + + Revert "path: Add some more validation points" + + This reverts commit e8fa85705e48d03742eb351addbad53be4d8e60b. + + The validation broke the test suite; it'll need some more work. + + json-glib/json-path.c | 77 +++++++++++++----------------------------------- + 1 files changed, 21 insertions(+), 56 deletions(-) + +commit e8fa85705e48d03742eb351addbad53be4d8e60b +Author: Emmanuele Bassi +Date: Wed Jun 1 07:55:17 2011 +0100 + + path: Add some more validation points + + Especially for the slice syntax. + + json-glib/json-path.c | 77 +++++++++++++++++++++++++++++++++++------------- + 1 files changed, 56 insertions(+), 21 deletions(-) + +commit 295c469329d040b4d311e9c295bec908856190d6 +Author: Emmanuele Bassi +Date: Wed Jun 1 07:54:48 2011 +0100 + + symbols: Add JsonPath public entry points + + json-glib/json-glib.symbols | 6 ++++++ + 1 files changed, 6 insertions(+), 0 deletions(-) + +commit 257209ab47ebcbf36006dd3aa3fcee5545381c6f +Author: Emmanuele Bassi +Date: Wed Jun 1 07:54:26 2011 +0100 + + docs: Document JsonPath and add it to the API reference + + doc/reference/json-glib-docs.xml | 3 +- + doc/reference/json-glib-sections.txt | 20 ++++ + doc/reference/json-glib.types | 1 + + json-glib/json-path.c | 174 +++++++++++++++++++++++++++++++++- + 4 files changed, 195 insertions(+), 3 deletions(-) + +commit ac89bb7fc7491019f8baa687a785eeb93af7213e +Merge: b5bd477 4ea8cd4 +Author: Emmanuele Bassi +Date: Tue May 31 23:16:10 2011 +0100 + + Merge branch 'wip/json-path' + + * wip/json-path: + Add initial JSONPath implementation + +commit 4ea8cd43986d5888fb8e809a198d6b0331b12480 +Author: Emmanuele Bassi +Date: Sat May 28 14:36:43 2011 +0100 + + Add initial JSONPath implementation + + JSONPath is a JSON query syntax similar to what XPath does for XML; + using JSONPath it's possible to address a specific node (or set of + nodes) inside a JSON document. + + The JsonPath class is a simple implementation of part of the JSONPath + proposal, as formalised by Stefan Gössner here: + + http://goessner.net/articles/JsonPath/ + + The covered operators are: + + • root, or '$'; + • child, both using the dot-notation and the bracket notation; + • recursive descent, or '..'; + • subscript, or '[]'; + • set, or '[,]'; + • slice, or '[start:end:step]'. + + The only missing operators are the filter, or '?()' and the script, + or '()', because implementing a JavaScript interpreter inside JSON-GLib + is not one of my greatest aspirations. It should be possible, though, + to parse and evaluate simple arithmetic conditions, in the future. + + The JsonPath methods are pretty straightforward: a JsonPath instance + should be created and used to compile an expression; the compilation + might result in a syntax error or not. Then, the JsonPath instance can + be used to match any JSON tree. Like the other JSONPath implementations, + JsonPath returns a JSON array of matching nodes. + + A simple, one-off static method called json_path_query() is also + provided; the method wraps the JsonPath creation, the expression + compilation, and the matching, as well as disposing the JsonPath + instance once done. + + For the time being, only positive testing is provided; negative testing + for the expression compilation will follow. + + json-glib/Makefile.am | 2 + + json-glib/json-debug.c | 3 +- + json-glib/json-debug.h | 3 +- + json-glib/json-glib.h | 1 + + json-glib/json-path.c | 856 +++++++++++++++++++++++++++++++++++++++++++ + json-glib/json-path.h | 97 +++++ + json-glib/tests/Makefile.am | 1 + + json-glib/tests/path-test.c | 143 +++++++ + 8 files changed, 1104 insertions(+), 2 deletions(-) + +commit b5bd47727c960990ba98902ee2d7099491a4178a +Author: Chun-wei Fan +Date: Mon May 30 14:26:54 2011 +0800 + + Add/update Visual C++ projects for GVariant API + + -Added/dist VS 2008/2010 projects to compile the gvariant-test test program + (yes, the gvariant-test program passes in the 32-bit and 64-bit modes) + -Updated property sheet to "install" the "new" json-gvariant.h + + build/win32/vs10/Makefile.am | 2 + + build/win32/vs10/gvariant-test.vcxproj | 159 ++++++++++++++++++++++++ + build/win32/vs10/gvariant-test.vcxproj.filters | 14 ++ + build/win32/vs10/json-glib.props | 2 + + build/win32/vs10/json-glib.sln | 10 ++ + build/win32/vs9/Makefile.am | 1 + + build/win32/vs9/gvariant-test.vcproj | 147 ++++++++++++++++++++++ + build/win32/vs9/json-glib.sln | 14 ++ + build/win32/vs9/json-glib.vsprops | 1 + + 9 files changed, 350 insertions(+), 0 deletions(-) + +commit 3a1d817fd39b4b4d2bdbc93257be535c5b29780f +Author: Chun-wei Fan +Date: Mon May 30 13:07:53 2011 +0800 + + Update json-glib.symbols + + Added the JSON-GLib gvariant-related APIs. + + json-glib/json-glib.symbols | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +commit 1b7f20fe88a6647e61964db063eba071114a6f66 +Author: Chun-wei Fan +Date: Mon May 30 12:53:47 2011 +0800 + + Update $(srcroot)/Makefile.am + + Due to changes to this file in commit + 4e41d26586991d2977c846fa4871b6df39fe4106, change the order of SUBDIRS + so that "make dist" will properly run. + + This has been checked with "make distcheck" on Fedora 15 + + Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 54172e25e8b62075f0aef98d21e17619466ff30f +Author: Chun-wei Fan +Date: Mon May 30 11:55:06 2011 +0800 + + Update Visual C++ Project Files + + Update the dist part too... + + build/win32/vs10/Makefile.am | 16 ++++++++-------- + build/win32/vs9/Makefile.am | 8 ++++---- + 2 files changed, 12 insertions(+), 12 deletions(-) + +commit 0715050a5a76f187f66f14288a8f3444a4eb49e4 +Author: Chun-wei Fan +Date: Mon May 30 11:53:04 2011 +0800 + + Update VS 2008/2010 Project Files + + Due to the changes in the location/names of some test programs in + commit 4e41d26586991d2977c846fa4871b6df39fe4106, update the VS + project files... + + build/win32/vs10/boxed.vcxproj | 172 ++++++++++++++++++++ + build/win32/vs10/boxed.vcxproj.filters | 14 ++ + build/win32/vs10/install.vcxproj | 8 +- + build/win32/vs10/json-glib.sln | 8 +- + build/win32/vs10/serialize-complex.vcxproj | 171 +++++++++++++++++++ + build/win32/vs10/serialize-complex.vcxproj.filters | 14 ++ + build/win32/vs10/serialize-full.vcxproj | 172 ++++++++++++++++++++ + build/win32/vs10/serialize-full.vcxproj.filters | 14 ++ + build/win32/vs10/serialize-simple.vcxproj | 171 +++++++++++++++++++ + build/win32/vs10/serialize-simple.vcxproj.filters | 14 ++ + build/win32/vs10/test-serialize-boxed.vcxproj | 172 -------------------- + .../vs10/test-serialize-boxed.vcxproj.filters | 14 -- + build/win32/vs10/test-serialize-complex.vcxproj | 171 ------------------- + .../vs10/test-serialize-complex.vcxproj.filters | 14 -- + build/win32/vs10/test-serialize-full.vcxproj | 172 -------------------- + .../win32/vs10/test-serialize-full.vcxproj.filters | 14 -- + build/win32/vs10/test-serialize-simple.vcxproj | 171 ------------------- + .../vs10/test-serialize-simple.vcxproj.filters | 14 -- + build/win32/vs9/boxed.vcproj | 157 ++++++++++++++++++ + build/win32/vs9/json-glib.sln | 8 +- + build/win32/vs9/serialize-complex.vcproj | 156 ++++++++++++++++++ + build/win32/vs9/serialize-full.vcproj | 157 ++++++++++++++++++ + build/win32/vs9/serialize-simple.vcproj | 156 ++++++++++++++++++ + build/win32/vs9/test-serialize-boxed.vcproj | 157 ------------------ + build/win32/vs9/test-serialize-complex.vcproj | 156 ------------------ + build/win32/vs9/test-serialize-full.vcproj | 157 ------------------ + build/win32/vs9/test-serialize-simple.vcproj | 156 ------------------ + 27 files changed, 1380 insertions(+), 1380 deletions(-) + +commit 8b778252358ddb28936c6c9192a84f76368ca122 +Author: Emmanuele Bassi +Date: Sat May 28 11:38:18 2011 +0100 + + tests: Fix up the JsonBuilder test + + Clean up some odd code, and add a unit for the empty object case outlined + in bug 651271. + + json-glib/tests/builder-test.c | 52 +++++++++++++++++++++++++++++++++++---- + 1 files changed, 46 insertions(+), 6 deletions(-) + +commit d784cc6825607c517d7565049108d1f908a1d71b +Author: Emmanuele Bassi +Date: Sat May 28 11:36:55 2011 +0100 + + builder: Initialize state fields + + If we're calling g_free() on a field we better make sure that it's either + NULL or contains valid data. + + https://bugzilla.gnome.org/show_bug.cgi?id=651271 + + json-glib/json-builder.c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit e20a8b0faeb45d1ae2f1bd12d49d31d12e357b24 +Author: Alexandre Mazari +Date: Tue Mar 22 17:28:55 2011 +0100 + + gvariant: Fix introspection annotation + + The 'signature' arguments of the json_gvariant_deserialize functions are + optional. + + Signed-off-by: Emmanuele Bassi + + json-glib/json-gvariant.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 4e41d26586991d2977c846fa4871b6df39fe4106 +Author: Emmanuele Bassi +Date: Sat May 28 11:05:57 2011 +0100 + + tests: Move all tests under json-glib + + The test framework should live under the json-glib directory, and not be + spread across the project. + + Makefile.am | 6 - + configure.ac | 2 - + json-glib/tests/Makefile.am | 53 ++--- + json-glib/tests/boxed.c | 264 +++++++++++++++++++++++ + json-glib/tests/serialize-complex.c | 293 +++++++++++++++++++++++++ + json-glib/tests/serialize-full.c | 402 +++++++++++++++++++++++++++++++++++ + json-glib/tests/serialize-simple.c | 166 ++++++++++++++ + tests/Makefile.am | 34 --- + tests/test-serialize-boxed.c | 264 ----------------------- + tests/test-serialize-complex.c | 293 ------------------------- + tests/test-serialize-full.c | 402 ----------------------------------- + tests/test-serialize-simple.c | 166 -------------- + 12 files changed, 1142 insertions(+), 1203 deletions(-) + +commit 8d98b99b945423085232d358492e97399c9fd071 +Author: Chun-wei Fan +Date: Mon May 23 09:39:20 2011 +0800 + + Update Visual Studio property sheets + + -Make DLL/LIB versioning more like the autotools-compiled versions + -"Install" the headers in a more consistent way like the autotools versions + + build/win32/vs10/json-glib.props | 26 +++++++++++++------------- + build/win32/vs9/json-glib.vsprops | 32 ++++++++++++++++---------------- + 2 files changed, 29 insertions(+), 29 deletions(-) + +commit 15221448b5998a071b72299c666044dd65ea97e3 +Author: Emmanuele Bassi +Date: Thu May 19 08:32:03 2011 +0100 + + Add ignore file under build/win32 + + build/win32/.gitignore | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +commit f4651885961bf3ccc8d475cdc86af051fe1101f6 +Author: Emmanuele Bassi +Date: Wed May 18 13:44:07 2011 +0100 + + doap: Add the download-page resource + + json-glib.doap | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 8ef7da62a7575ebb04c69b9d98fd24efb840ab3b +Author: Emmanuele Bassi +Date: Wed May 18 13:39:38 2011 +0100 + + Post-release version bump to 0.13.3 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit aa793a4fbcc4d44d367ba84d3782823885fec045 +Author: Emmanuele Bassi +Date: Wed May 18 13:37:38 2011 +0100 + + Release JSON-GLib 0.13.2 (snapshot) + + NEWS | 12 ++++++++++++ + configure.ac | 2 +- + 2 files changed, 13 insertions(+), 1 deletions(-) + +commit 595bdd9cc160f10edefb9e54f45cb4727f39ac24 +Merge: 46a8e57 d3db7ac +Author: Chun-wei Fan +Date: Wed May 4 01:47:14 2011 +0800 + + Merge branch 'master' into msvc-patch + +commit d3db7acc1f53491b6dd9637991a1eaf8bb5c5002 +Author: Tristan Van Berkom +Date: Tue May 3 00:20:12 2011 +0900 + + Fixed json_deserialize_pspec() to handle null nodes. + + This fixes deserialization to match serialization (bug 648539) + + json-glib/json-gobject.c | 14 +++++++++++++- + 1 files changed, 13 insertions(+), 1 deletions(-) + +commit 9824a32a803bd194208d460068ce5ba91d776686 +Author: Tristan Van Berkom +Date: Sat Apr 23 18:15:37 2011 +0900 + + Warn from json_gobject_deserialize() if a listed property cannot be deserialized. + + json-glib/json-gobject.c | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit 254e8e969968e7ed5f594238a980b20c6fabc46e +Author: Bastien Nocera +Date: Thu Apr 21 02:29:53 2011 +0100 + + reader: Fix bug in example usage + + json-glib/json-reader.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 46a8e576f2fae9563219fcd72a9866826e684559 +Author: Chun-wei Fan +Date: Wed Apr 13 00:34:05 2011 +0800 + + VS 2010 Project Files (automation support included) + + These are the VS 2010 project files to build JSON-GLib + and its test programs. The project for building the main JSON-GLib + DLL has its source files input into it by the preprocessor during + "make dist", like the VS2008 project files, simplifying its maintenance. + + build/win32/vs10/array-test.vcxproj | 172 +++++++++++++++++ + build/win32/vs10/array-test.vcxproj.filters | 14 ++ + build/win32/vs10/builder-test.vcxproj | 172 +++++++++++++++++ + build/win32/vs10/builder-test.vcxproj.filters | 14 ++ + build/win32/vs10/generator-test.vcxproj | 172 +++++++++++++++++ + build/win32/vs10/generator-test.vcxproj.filters | 14 ++ + build/win32/vs10/install.vcxproj | 149 +++++++++++++++ + build/win32/vs10/json-glib.props | 123 ++++++++++++ + build/win32/vs10/json-glib.sln | 146 +++++++++++++++ + build/win32/vs10/json-glib.vcxproj.filtersin | 20 ++ + build/win32/vs10/json-glib.vcxprojin | 196 ++++++++++++++++++++ + build/win32/vs10/node-test.vcxproj | 172 +++++++++++++++++ + build/win32/vs10/node-test.vcxproj.filters | 14 ++ + build/win32/vs10/object-test.vcxproj | 172 +++++++++++++++++ + build/win32/vs10/object-test.vcxproj.filters | 13 ++ + build/win32/vs10/parser-test.vcxproj | 172 +++++++++++++++++ + build/win32/vs10/parser-test.vcxproj.filters | 14 ++ + build/win32/vs10/reader-test.vcxproj | 173 +++++++++++++++++ + build/win32/vs10/reader-test.vcxproj.filters | 14 ++ + build/win32/vs10/test-serialize-boxed.vcxproj | 172 +++++++++++++++++ + .../vs10/test-serialize-boxed.vcxproj.filters | 14 ++ + build/win32/vs10/test-serialize-complex.vcxproj | 171 +++++++++++++++++ + .../vs10/test-serialize-complex.vcxproj.filters | 14 ++ + build/win32/vs10/test-serialize-full.vcxproj | 172 +++++++++++++++++ + .../win32/vs10/test-serialize-full.vcxproj.filters | 14 ++ + build/win32/vs10/test-serialize-simple.vcxproj | 171 +++++++++++++++++ + .../vs10/test-serialize-simple.vcxproj.filters | 14 ++ + 27 files changed, 2678 insertions(+), 0 deletions(-) + +commit 6c74292e66be25840612d7ee6871f1668010eada +Author: Chun-wei Fan +Date: Wed Apr 13 00:30:38 2011 +0800 + + Updates to MSVC pre-configured headers + + Add automation support for config.h.win32(.in), so config.h.win32 + will be distributed with the correct version info during "make dist" + + Also eliminate json-version.h.win32 as it is no longer needed (distribute + json-version.h during "make dist") + + build/win32/config.h.win32 | 61 ----------------------- + build/win32/config.h.win32.in | 61 +++++++++++++++++++++++ + build/win32/json-version.h.win32 | 100 -------------------------------------- + 3 files changed, 61 insertions(+), 161 deletions(-) + +commit a7bdf87ec150ac7a88a850190fa79359197e552e +Author: Chun-wei Fan +Date: Wed Apr 13 00:26:35 2011 +0800 + + MSVC Support with automation added (autotools part) + + This updates the autotools files so that the project to compile the main + JSON-GLib DLL will have its source files filed into the project during + "make dist". Plus, it enables the creation/distribution of json-version.h + and config.h.win32 with the correct version info during "make dist". + + These changes will simplify the maintenance of the VS project files, + and support for VS2010 is also added here, with similar automation support, + too. + + Makefile.am | 2 +- + build/win32/Makefile.am | 4 ++-- + build/win32/vs10/Makefile.am | 33 +++++++++++++++++++++++++++++++++ + build/win32/vs9/Makefile.am | 1 + + configure.ac | 2 ++ + json-glib/Makefile.am | 39 ++++++++++++++++++++++++++++++++++++++- + 6 files changed, 77 insertions(+), 4 deletions(-) + +commit 20fe28cfaa605e11c47fe53232b5dccfa47ddf8b +Author: Chun-wei Fan +Date: Wed Apr 13 00:02:48 2011 +0800 + + Update VS 2008 Project Files + + Add automation support for the VS 2008 project files + These are the updated VS 2008 Project files and property sheets + themselves. x64 compiling support is also added + + build/win32/vs9/array-test.vcproj | 132 ++++------- + build/win32/vs9/builder-test.vcproj | 134 ++++------- + build/win32/vs9/generator-test.vcproj | 134 ++++------- + build/win32/vs9/install.vcproj | 44 +++-- + build/win32/vs9/json-glib.sln | 115 +++++++++- + build/win32/vs9/json-glib.vcproj | 317 ------------------------- + build/win32/vs9/json-glib.vcprojin | 204 ++++++++++++++++ + build/win32/vs9/json-glib.vsprops | 20 +- + build/win32/vs9/node-test.vcproj | 134 ++++------- + build/win32/vs9/object-test.vcproj | 134 ++++------- + build/win32/vs9/parser-test.vcproj | 125 ++++------ + build/win32/vs9/reader-test.vcproj | 134 ++++------- + build/win32/vs9/test-serialize-boxed.vcproj | 134 ++++------- + build/win32/vs9/test-serialize-complex.vcproj | 134 ++++------- + build/win32/vs9/test-serialize-full.vcproj | 134 ++++------- + build/win32/vs9/test-serialize-simple.vcproj | 133 ++++------- + 16 files changed, 917 insertions(+), 1245 deletions(-) + +commit 61da54311018f7afedb5d1e8cbd88ae7a284714f +Author: Emmanuele Bassi +Date: Tue Apr 12 08:53:26 2011 +0100 + + doap: Fix the URI for Bugzilla + + JSON-GLib uses the GNOME Bugzilla instance. + + json-glib.doap | 19 +------------------ + 1 files changed, 1 insertions(+), 18 deletions(-) + +commit 0ff8f784848ad34c37bd60ad92791c6f76944803 +Author: Emmanuele Bassi +Date: Tue Apr 12 08:41:20 2011 +0100 + + build: List GIO in the json-glib pkgconfig file + + Building against JSON-GLib requires GIO. + + json-glib.pc.in | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 8b060cd9144990aae6531982bad66050fafcb658 +Author: Emmanuele Bassi +Date: Fri Apr 8 15:17:19 2011 +0100 + + object: Use g_list_find_custom() + + Instead of manual iteration, let's use the function GList provides us. + + json-glib/json-object.c | 13 +++---------- + 1 files changed, 3 insertions(+), 10 deletions(-) + +commit 2158cecee6fe1da8786afe2fad94d1098f879786 +Author: Emmanuele Bassi +Date: Wed Mar 23 11:35:28 2011 +0000 + + build: Use AC_CANONICAL_HOST + + Since we check for the contents of the $host variable to detect + compilation on/for win32, we should also be using the canonicalization + facilities from autoconf. + + configure.ac | 20 +++++++++++--------- + 1 files changed, 11 insertions(+), 9 deletions(-) + +commit 992d966572e3e498031578558ac307d0f4909246 +Author: Rob Taylor +Date: Tue Mar 15 09:09:11 2011 +0000 + + gvariant: Pass NULL down the chain for *signature + + Some small internal change from passing the signature to a pointer to + the signature down the call chain caused all the checks for + signature==NULL to fail. + + Signed-off-by: Emmanuele Bassi + + json-glib/json-gvariant.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit af70f34b3fc46a4c464418ebd6a16d9a1f18af8a +Author: Eduardo Lima Mitev +Date: Thu Jan 27 16:58:48 2011 +0100 + + generator: Removes blank spaces from generated JSON when not pretty-print + + https://bugzilla.gnome.org/show_bug.cgi?id=640729 + + json-glib/json-generator.c | 35 +++++++++++------ + json-glib/tests/builder-test.c | 6 +- + json-glib/tests/generator-test.c | 34 ++++++++-------- + json-glib/tests/gvariant-test.c | 78 +++++++++++++++++++------------------- + 4 files changed, 82 insertions(+), 71 deletions(-) + +commit 8bb22d7de00f15ef44cad502d757639d8a4e8973 +Author: Emmanuele Bassi +Date: Tue Feb 15 16:28:07 2011 +0000 + + Fix introspection annotations + + json-glib/json-node.c | 7 ++++--- + json-glib/json-object.c | 4 ++-- + 2 files changed, 6 insertions(+), 5 deletions(-) + +commit 217127545b07f93ac27e3f137d748d61a5e199da +Author: Emmanuele Bassi +Date: Tue Feb 15 16:16:46 2011 +0000 + + tests/object: Add a set_member() unit + + Verify that setting an object member multiple times does not lead to + random values. + + json-glib/tests/object-test.c | 29 +++++++++++++++++++++++++++++ + 1 files changed, 29 insertions(+), 0 deletions(-) + +commit 1a633159a593c962233a5ef4660e31e60eed96d9 +Author: Emmanuele Bassi +Date: Tue Feb 15 16:12:38 2011 +0000 + + object: Replace the name pointer in the members list + + When calling g_hash_table_replace() we also free the string holding the + member name. This means that the const gchar* pointer we store inside + the list of ordered member names now points to garbage - so if somebody + tries to iterate over the members list it will get random values instead + of a valid C string. + + Since we guaranteed insertion order, if we replace the contents of a + JsonObject member we need to find the right pointer and replace it: just + removing and prepending won't do. + + https://bugzilla.gnome.org/show_bug.cgi?id=642383 + + json-glib/json-object.c | 21 +++++++++++++++++++++ + 1 files changed, 21 insertions(+), 0 deletions(-) + +commit a125a724894a08a8d8053fdd2db92d0ad8e2dfd4 +Author: Emmanuele Bassi +Date: Sun Feb 6 23:10:54 2011 +0000 + + reader: Plug a leak + + Free the current_member string, and since we're destroying data we own + let's do it inside the finalize implementation instead of the dispose + one. + + json-glib/json-reader.c | 15 ++++++--------- + 1 files changed, 6 insertions(+), 9 deletions(-) + +commit 2c5f4563c54fa273ffd8dbe02b60a12c4b54b977 +Author: Emmanuele Bassi +Date: Sun Feb 6 12:22:49 2011 +0000 + + reader: Add accessor for the member name + + This should allow easy access to the member name during iteration. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-glib.symbols | 1 + + json-glib/json-reader.c | 32 ++++++++++++++++++++++++++++++++ + json-glib/json-reader.h | 1 + + json-glib/tests/reader-test.c | 1 + + 5 files changed, 36 insertions(+), 0 deletions(-) + +commit b2880f5a4dad07ff96a9b6578ffc5d677f75eb94 +Author: Emmanuele Bassi +Date: Sun Feb 6 11:34:22 2011 +0000 + + reader: Allow using read_element() on objects + + If we assume that a JSON object is just an array with a named mapping + then the JsonReader API should be able to descend into objects using the + same API used for arrays. + + This obviously is less useful than it sounds if we take a very strict + interpretation of JSON objects as unordered string-to-value mappings; as + the ordering is not guaranteed to be stable, parsers would be fairly + weak against various JSON definitions. + + If the JSON format parsed is guaranteed to be stable then an integer + offset might be an easy (albeit slightly less performant) way to access + data. + + json-glib/json-reader.c | 67 +++++++++++++++++++++++++++++++---------- + json-glib/tests/reader-test.c | 6 ++++ + 2 files changed, 57 insertions(+), 16 deletions(-) + +commit 74ed11db6da9839ce17b448d6d17103ea0734dda +Author: Emmanuele Bassi +Date: Fri Feb 4 19:18:22 2011 +0000 + + reader: Add list_members() method + + Allow retrieving a list of member names from the current cursor position + of the JsonReader. It's useful if you're trying to inspect a JSON tree. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-glib.symbols | 1 + + json-glib/json-reader.c | 46 ++++++++++++++++++++++++++++++++++ + json-glib/json-reader.h | 1 + + json-glib/tests/reader-test.c | 19 ++++++++++++++ + 5 files changed, 68 insertions(+), 0 deletions(-) + +commit eb14f92019bde75947a98f5578167af6b6a17974 +Author: Emmanuele Bassi +Date: Wed Jan 26 16:58:56 2011 +0000 + + Coding style fixes and compiler warnings removal + + G_VARIANT_CLASS_DICTIONARY is a define; GCC complais loudly when trying + to use a switch() on an enumeration type with case values not from the + enumeration. + + Plus: coding style in JSON-GLib is mostly the same as GTK+ and Clutter, + so we should adhere to it. + + json-glib/json-gvariant.c | 67 ++++++++++++++++++++++++--------------------- + 1 files changed, 36 insertions(+), 31 deletions(-) + +commit 212b243c07721242da3dc2c0e6dfe979f73ee5c6 +Author: Eduardo Lima Mitev +Date: Tue Nov 9 16:45:30 2010 +0100 + + gvariant: Adds JSON GVariant integration API, with docs and tests + + https://bugzilla.gnome.org/show_bug.cgi?id=632940 + + doc/reference/json-glib-docs.xml | 1 + + doc/reference/json-glib-sections.txt | 10 + + json-glib/Makefile.am | 2 + + json-glib/json-glib.h | 2 + + json-glib/json-gvariant.c | 1296 ++++++++++++++++++++++++++++++++++ + json-glib/json-gvariant.h | 46 ++ + json-glib/tests/Makefile.am | 4 + + json-glib/tests/gvariant-test.c | 233 ++++++ + 8 files changed, 1594 insertions(+), 0 deletions(-) + +commit 822b6be848f3ad53ad113f0ce317b01f1a91a54f +Author: Emmanuele Bassi +Date: Sat Jan 15 18:36:34 2011 +0000 + + build: Do not dist a gzip tarball + + Use only bzip2, since it gives better compression ratios; the + installation script on gnome.org will create a gzipped tarball by itself + anyway. + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 7c07a6549df1ae1b4826bf40f70c62768ce7e35b +Author: Emmanuele Bassi +Date: Mon Jan 10 11:56:32 2011 +0000 + + build: Fix previous commit + + configure.ac | 7 +++++++ + 1 files changed, 7 insertions(+), 0 deletions(-) + +commit bb67e146c9e883185d264e693cc4a07d1a60d191 +Author: Fan, Chun-wei +Date: Mon Jan 10 11:49:16 2011 +0000 + + build: Add Visual C++ 2008 project files + + Integrate the files inside the build, though it's still not fully + automated. + + https://bugzilla.gnome.org/show_bug.cgi?id=635484 + + Signed-off-by: Emmanuele Bassi + + build/Makefile.am | 2 +- + build/win32/Makefile.am | 3 + + build/win32/config.h.win32 | 61 +++++ + build/win32/json-version.h.win32 | 100 ++++++++ + build/win32/vs9/Makefile.am | 19 ++ + build/win32/vs9/array-test.vcproj | 189 +++++++++++++++ + build/win32/vs9/builder-test.vcproj | 189 +++++++++++++++ + build/win32/vs9/generator-test.vcproj | 189 +++++++++++++++ + build/win32/vs9/install.vcproj | 71 ++++++ + build/win32/vs9/json-glib.sln | 80 +++++++ + build/win32/vs9/json-glib.vcproj | 317 +++++++++++++++++++++++++ + build/win32/vs9/json-glib.vsprops | 93 +++++++ + build/win32/vs9/node-test.vcproj | 189 +++++++++++++++ + build/win32/vs9/object-test.vcproj | 187 +++++++++++++++ + build/win32/vs9/parser-test.vcproj | 189 +++++++++++++++ + build/win32/vs9/reader-test.vcproj | 190 +++++++++++++++ + build/win32/vs9/test-serialize-boxed.vcproj | 189 +++++++++++++++ + build/win32/vs9/test-serialize-complex.vcproj | 188 +++++++++++++++ + build/win32/vs9/test-serialize-full.vcproj | 189 +++++++++++++++ + build/win32/vs9/test-serialize-simple.vcproj | 189 +++++++++++++++ + 20 files changed, 2822 insertions(+), 1 deletions(-) + +commit c7cd46b8320157e223009aa2a7204461c7d301d5 +Author: Emmanuele Bassi +Date: Mon Jan 10 11:48:26 2011 +0000 + + build: Add .symbols file for win32 builds + + json-glib/Makefile.am | 2 + + json-glib/json-glib.symbols | 163 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 165 insertions(+), 0 deletions(-) + +commit 4549af3210461a07dc84a30433a0a23764618e4c +Author: Evan Nemerson +Date: Sat Nov 20 16:25:41 2010 -0800 + + Add C include information to GIR + + Bug #635398 + + json-glib/Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit f89a50e24d2088061eb7d975db7e6f39beaf853d +Author: Emmanuele Bassi +Date: Mon Jan 10 11:16:17 2011 +0000 + + object: Do some more validation in set_member() + + Check if we're setting the same node, to avoid a needless replace. + + json-glib/json-object.c | 10 ++++++++++ + 1 files changed, 10 insertions(+), 0 deletions(-) + +commit e828cba563f2584e848994935478a7ffd21728df +Author: Luca Bruno +Date: Thu Jan 6 17:44:46 2011 +0100 + + Add missing introspection annotations. + + https://bugzilla.gnome.org/show_bug.cgi?id=638932 + + json-glib/json-array.c | 8 ++++---- + json-glib/json-node.c | 8 ++++---- + json-glib/json-object.c | 8 ++++---- + json-glib/json-parser.c | 2 +- + json-glib/json-serializable.c | 2 +- + 5 files changed, 14 insertions(+), 14 deletions(-) + +commit 02dd77efaa60201f74349c969dc005c8bb092057 +Author: Emmanuele Bassi +Date: Mon Jan 10 10:45:39 2011 +0000 + + generator: Use g_ascii_dtostr() to avoid losing precision + + The nice format escape for g_ascii_formatd() is not really suited for a + serialization format, as it obviously loses precision. + + https://bugzilla.gnome.org/show_bug.cgi?id=637244 + + json-glib/json-generator.c | 7 ++++--- + 1 files changed, 4 insertions(+), 3 deletions(-) + +commit 3dbab961e675d0684df1121906ae3588d09653e6 +Author: Emmanuele Bassi +Date: Mon Jan 10 10:43:58 2011 +0000 + + builder-test: Do not compare serialized double values + + Serialization of floating point values might lead to odd formats, + especially when dealing with IEEE machine-neutral values. + + json-glib/tests/builder-test.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 567d7fb4dff83c9e37e64ef8611f0231fa181c34 +Author: Emmanuele Bassi +Date: Tue Dec 14 16:35:24 2010 +0000 + + reader: Use GObject ≥ 2.26 API + + Use modern API to deal with properties installation and notification. + + json-glib/json-reader.c | 35 +++++++++++++++-------------------- + 1 files changed, 15 insertions(+), 20 deletions(-) + +commit 9aed6f5a455109186ae56ec1c3e558505644d57f +Author: Emmanuele Bassi +Date: Tue Dec 14 16:26:10 2010 +0000 + + generator: Clean up and add accessors + + Instead of asking everyone to use g_object_set() to set up a + JsonGenerator the class should provide a decent API for its properties. + + While we're at it, we should also use modern API for installing and + notifying of property changes. + + doc/reference/json-glib-sections.txt | 11 ++- + json-glib/json-generator.c | 256 ++++++++++++++++++++++++++++------ + json-glib/json-generator.h | 35 +++-- + 3 files changed, 246 insertions(+), 56 deletions(-) + +commit de991a0c6dc4d0b3c366248e20ba6853bd2c3e92 +Author: Emmanuele Bassi +Date: Tue Dec 14 11:39:04 2010 +0000 + + generator: Verify that the decimal separator is locale-independent + + There shouldn't be a requirement for this, since we're using the GLib + function explicitely for this reason, but it's always good to have a + comprehensive test suite. + + json-glib/tests/generator-test.c | 58 ++++++++++++++++++++++++++++++++++++++ + 1 files changed, 58 insertions(+), 0 deletions(-) + +commit 4add0e081b8f58dec1cd68152d846be87942091e +Author: Emmanuele Bassi +Date: Tue Dec 14 11:37:25 2010 +0000 + + generator: Implement dumping bare values + + JsonGenerator is not able to generate strings for bare values, something + that completely went under the radar for all this time. + + json-glib/json-generator.c | 15 ++++++++++----- + 1 files changed, 10 insertions(+), 5 deletions(-) + +commit 20a16d5b9ecabe68ee18655b2ff3bdb17136c6f1 +Author: Emmanuele Bassi +Date: Tue Oct 19 09:59:38 2010 +0100 + + build: Resync the GTest rules with upstream + + Upstream GLib fixed the test rules to avoid repeating the test suite + three times. + + build/autotools/Makefile.am.gtest | 21 ++++++++++++--------- + 1 files changed, 12 insertions(+), 9 deletions(-) + +commit dc262a09572328342164ebb23ad698b5cb3cd8ff +Author: Emmanuele Bassi +Date: Tue Oct 19 09:49:20 2010 +0100 + + Use G_DEFINE_INTERFACE() + + json-glib/json-serializable.c | 35 +++++++---------------------------- + 1 files changed, 7 insertions(+), 28 deletions(-) + +commit 80665415cd945f3dafed6925c9975f36b916a80f +Author: Emmanuele Bassi +Date: Tue Oct 19 09:49:05 2010 +0100 + + Use G_DEFINE_BOXED_TYPE() + + json-glib/json-array.c | 13 +------------ + json-glib/json-node.c | 13 +------------ + json-glib/json-object.c | 13 +------------ + 3 files changed, 3 insertions(+), 36 deletions(-) + +commit 51e2eef48fabbee554193ee9eadfb8696ebb3ac7 +Author: Emmanuele Bassi +Date: Tue Oct 19 09:48:23 2010 +0100 + + build: Depend on GLib ≥ 2.26 + + README | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit d28db01f5d12497ec96d5e507af4003b076dd154 +Author: Emmanuele Bassi +Date: Wed Sep 29 17:25:18 2010 +0100 + + build: Use -no-undefined when compiling on win32 + + configure.ac | 13 +++++++++++++ + 1 files changed, 13 insertions(+), 0 deletions(-) + +commit 5332217481b3e5fee73710bb50886fbc52a632a4 +Author: Emmanuele Bassi +Date: Sat Sep 25 12:02:36 2010 +0100 + + Post-branch version bump to 0.13.1 + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 6b4d49b8024f672d62a7059c083455ad31f7d0eb +Author: Emmanuele Bassi +Date: Sat Sep 25 11:59:23 2010 +0100 + + Post-release version bump to 0.12.1 + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 661a7497cc88aebdee32bf31c219d457ca143590 +Author: Emmanuele Bassi +Date: Sat Sep 25 11:28:42 2010 +0100 + + Release JSON-GLib 0.12.0 + + NEWS | 1 + + README | 6 +++--- + configure.ac | 4 ++-- + 3 files changed, 6 insertions(+), 5 deletions(-) + +commit 9b6acd68e156a45cc845bdebd99c174e82d0677c +Author: Emmanuele Bassi +Date: Sat Sep 25 11:50:38 2010 +0100 + + gobject: Fix deserialization of construct-only properties + + Commit 2d7550948dfb2e5907b851bc2c4bd296a7526086 broke the construct-only + properties; we now only check for the G_PARAM_CONSTRUCT_ONLY flag, and + pass construct-only properties to g_object_newv(); all the properties + flagged as G_PARAM_CONSTRUCT gets passed with the rest of the properties + after that. + + json-glib/json-gobject.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit d480f2e77e3dc0d00fc617686b306f96353b7177 +Author: Emmanuele Bassi +Date: Sat Sep 25 11:49:41 2010 +0100 + + test-serialize-full: Remove the dummy deserialize implementation + + If you don't override JsonSerializable, you get the default behaviour + anyway. + + tests/test-serialize-full.c | 15 +-------------- + 1 files changed, 1 insertions(+), 14 deletions(-) + +commit ff5dd56e4f864d0c015dcd66fa852f9cf7cf90dd +Author: Emmanuele Bassi +Date: Sat Sep 25 11:49:19 2010 +0100 + + debug: Add debug notes in the GObject code + + json-glib/json-gobject.c | 14 ++++++++++++-- + json-glib/json-serializable.c | 3 +++ + 2 files changed, 15 insertions(+), 2 deletions(-) + +commit 7707b18f42b3a19dd780ba73ce1dabb6b8ef3c95 +Author: Emmanuele Bassi +Date: Sat Sep 25 11:48:25 2010 +0100 + + debug: Add debug flags for GObject-related code + + json-glib/json-debug.c | 3 ++- + json-glib/json-debug.h | 3 ++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +commit c6bf7ecb0a836b5b43f203cb22be577bd54b9e9a +Author: Emmanuele Bassi +Date: Sat Sep 25 11:23:43 2010 +0100 + + introspection: Update to 0.9.5 + + Add --warn-all for the scanner flags. + + Also, undefine JSON_DISABLE_DEPRECATED, to introspect deprecated API. + + configure.ac | 2 +- + json-glib/Makefile.am | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +commit 617286e4d38a93e41e1e1d974d01c1d73fd2ff66 +Author: Emmanuele Bassi +Date: Sat Sep 25 11:23:10 2010 +0100 + + Add introspection annotations + + json-glib/json-array.c | 4 ++-- + json-glib/json-gboxed.c | 14 +++++++------- + json-glib/json-gobject.c | 4 ++-- + json-glib/json-object.c | 4 ++-- + 4 files changed, 13 insertions(+), 13 deletions(-) + +commit 19b0b873c07f918e06a8610f27a439334334a83d +Author: Colin Walters +Date: Thu Sep 9 10:24:53 2010 -0400 + + introspection: Export json-glib-1.0 pkg-config file + + json-glib/Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit d05e919208a47c18d001ac08900b4d2837efd4b4 +Author: Luca Bruno +Date: Sun Aug 15 20:30:21 2010 +0200 + + docs: Add transfer none annotation to JsonBuilder return values. + + json-glib/json-builder.c | 22 +++++++++++----------- + 1 files changed, 11 insertions(+), 11 deletions(-) + +commit 2c3062cbd79826b184a6f613c519e69f3ddc1ec6 +Author: Emmanuele Bassi +Date: Sun Aug 15 18:46:02 2010 +0100 + + builder: Add Since: annotations + + json-glib/json-builder.h | 50 +++++++++++++++++++++++++-------------------- + 1 files changed, 28 insertions(+), 22 deletions(-) + +commit 2d7550948dfb2e5907b851bc2c4bd296a7526086 +Author: Emmanuele Bassi +Date: Sun Aug 15 18:44:13 2010 +0100 + + gobject: Use construct and construct-only properties + + Right now, we're checking twice for G_PARAM_CONSTRUCT_ONLY, but what we + really want is to check for both G_PARAM_CONSTRUCT and + G_PARAM_CONSTRUCT_ONLY properties when creating a new instance from a + JSON definition. + + json-glib/json-gobject.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit be48afe5801a541996bb64437c278088e70258f6 +Author: Emmanuele Bassi +Date: Sun Aug 15 18:43:49 2010 +0100 + + build: Use maintainer-clean for removing ignore files + + build/autotools/Makefile.am.gitignore | 11 ++++++++--- + 1 files changed, 8 insertions(+), 3 deletions(-) + +commit 5732cadfab58435ded4e88b0733c1a46aa0dcd2d +Author: Emmanuele Bassi +Date: Sat Aug 14 12:55:18 2010 +0100 + + build: Automate ignoring test binaries + + Use noinst_PROGRAMS to generate the list of test binaries to ignore + directly in the test directories. + + .gitignore | 11 ----------- + build/autotools/Makefile.am | 3 ++- + build/autotools/Makefile.am.gitignore | 19 +++++++++++++++++++ + json-glib/tests/Makefile.am | 4 ++++ + tests/Makefile.am | 4 ++++ + 5 files changed, 29 insertions(+), 12 deletions(-) + +commit 8b54bed521b609c373a48d27c880cc70272b6313 +Author: Emmanuele Bassi +Date: Sat Aug 14 12:26:29 2010 +0100 + + docs: Add 0.12 symbols index to the API reference + + doc/reference/json-glib-docs.xml | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +commit 9ed8d5e7fe96b9dcf82ddbba7de2db40d415cbaf +Author: Emmanuele Bassi +Date: Sat Aug 14 12:24:59 2010 +0100 + + reader: Add :root constructor property + + A JsonReader is useless without a root JsonNode to start walking the + tree from. + + json-glib/json-reader.c | 96 +++++++++++++++++++++++++++++++++++++--- + json-glib/json-reader.h | 2 +- + json-glib/tests/reader-test.c | 4 +- + 3 files changed, 91 insertions(+), 11 deletions(-) + +commit eeeae2194223abe2515e18edec1bdbbb6a7c793d +Author: Emmanuele Bassi +Date: Sat Aug 14 09:40:44 2010 +0100 + + docs: Add the RFC draft for JSON schema + + doc/draft-zyp-json-schema-02.txt | 1345 ++++++++++++++++++++++++++++++++++++++ + 1 files changed, 1345 insertions(+), 0 deletions(-) + +commit 0281176789b38973b1723f752070cb0e7340055d +Author: Emmanuele Bassi +Date: Thu Aug 12 16:09:45 2010 +0100 + + reader: Do not wrap JsonParser + + Since JsonParser has far more methods for parsing a JSON stream we + should just make JsonReader an API for reading an already parsed JSON + tree - in the same way that JsonBuilder does not generate the + stringified version of the JSON tree it builds. + + doc/reference/json-glib-sections.txt | 2 +- + json-glib/json-reader.c | 90 +++++++++++----------------------- + json-glib/json-reader.h | 6 +-- + json-glib/tests/reader-test.c | 11 +++- + 4 files changed, 41 insertions(+), 68 deletions(-) + +commit 149d2f3925ca798886f2137ae73488f7e2e6386b +Author: Emmanuele Bassi +Date: Thu Aug 12 15:55:54 2010 +0100 + + reader: Mirror the JsonBuilder API value accessors + + We should strive to make JsonBuilder and JsonReader similar in API. + + doc/reference/json-glib-sections.txt | 10 +++++----- + json-glib/json-reader.c | 34 +++++++++++++++++----------------- + json-glib/json-reader.h | 10 +++++----- + json-glib/tests/reader-test.c | 8 ++++---- + 4 files changed, 31 insertions(+), 31 deletions(-) + +commit b385ca3ae47e68cdf48f3641385b9fe5ac8429bc +Author: Emmanuele Bassi +Date: Thu Aug 12 15:35:17 2010 +0100 + + docs: Update NEWS + + NEWS | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 23866cb9935e9b9f7c88b26cdfc13ff95edfbe00 +Author: Emmanuele Bassi +Date: Thu Aug 12 15:34:09 2010 +0100 + + docs: Add JsonReader to the API reference + + doc/reference/json-glib-docs.xml | 1 + + doc/reference/json-glib-sections.txt | 41 ++++++++++++++++++++++++++++++++++ + doc/reference/json-glib.types | 2 + + 3 files changed, 44 insertions(+), 0 deletions(-) + +commit 10e5a1d38113b7b6e7c71da76ff11219baf1022d +Author: Emmanuele Bassi +Date: Thu Aug 12 15:29:41 2010 +0100 + + Add JsonReader + + JsonReader is a simple, cursor-based API for parsing a JSON DOM. It is + similar, in spirit, to the XmlReader API provided by various platforms + and XML parsing libraries. + + .gitignore | 1 + + json-glib/Makefile.am | 2 + + json-glib/json-glib.h | 3 + + json-glib/json-reader.c | 779 +++++++++++++++++++++++++++++++++++++++++ + json-glib/json-reader.h | 142 ++++++++ + json-glib/tests/Makefile.am | 10 +- + json-glib/tests/reader-test.c | 101 ++++++ + 7 files changed, 1035 insertions(+), 3 deletions(-) + +commit c3215ba1d46f7965fb58272da069bec389a174df +Author: Diego Escalante Urrelo +Date: Wed Aug 11 14:42:25 2010 -0500 + + json-glib/tests: explicitely link against glib + + Tests fail to link otherwise. + + Bug #626669 + + json-glib/tests/Makefile.am | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +commit 7f85693922d1e2614bcce9219d2c6521580dd360 +Author: Emmanuele Bassi +Date: Mon Aug 2 18:04:21 2010 +0100 + + build: Remove all stray mentions of Shave + + We depend on automake 1.11, now. + + .gitignore | 3 --- + autogen.sh | 15 +++------------ + build/autotools/Makefile.am | 2 -- + configure.ac | 1 - + 4 files changed, 3 insertions(+), 18 deletions(-) + +commit 26efdb4cc25cffa857e4ce36f0ad7ee6efe00634 +Author: Emmanuele Bassi +Date: Mon Aug 2 17:49:32 2010 +0100 + + Post-release version bump to 0.11.3 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit ff7a8ff73ee214d385c64501c8b5dc682ccad1cf +Author: Emmanuele Bassi +Date: Mon Aug 2 17:40:29 2010 +0100 + + Release 0.11.2 (0.12.0-rc1) + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 8a91b052fba48ae6f4a61115e2fa6839e7a21509 +Author: Emmanuele Bassi +Date: Mon Aug 2 17:45:35 2010 +0100 + + build: Fix up the tests data path + + Since we load up a test file from a directory, we should be using a + path - otherwise this will break out-of-tree builds, e.g. when doing + a distcheck. + + json-glib/tests/Makefile.am | 2 +- + json-glib/tests/parser-test.c | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +commit 2e3eaabd12829187f5a9ddacef68319442362729 +Author: Emmanuele Bassi +Date: Mon Aug 2 17:42:09 2010 +0100 + + build: Fix introspection dependencies + + json-glib/Makefile.am | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit def6d5612fbb33f9b02df80294651f5a28a83ccc +Author: Emmanuele Bassi +Date: Mon Aug 2 17:15:36 2010 +0100 + + Update the NEWS file + + NEWS | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 385e6278477dfd35d591f8343deb1de2827ac2ff +Author: Emmanuele Bassi +Date: Mon Aug 2 17:15:22 2010 +0100 + + docs: Update the dependencies in the README file + + README | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 5e19ed2523441afd12c45a8b3f19d10fc9a37335 +Author: Emmanuele Bassi +Date: Mon Aug 2 17:14:52 2010 +0100 + + build: Revert the GLib version bump + + GIO landed in GLib 2.16, so we don't need the version bump to 2.20. + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit d9b2845572123ed79a6626d90d2d2298c9c3d5a7 +Author: Emmanuele Bassi +Date: Mon Aug 2 17:07:36 2010 +0100 + + generator: Add an OutputStream-based method + + Currently, only synchronous API. + + The output handling is pretty trivial, unlike the input handling in + JsonParser; this is a really basic convenience API. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-generator.c | 37 ++++++++++++++++++++++++++++++++++ + json-glib/json-generator.h | 22 ++++++++++++------- + 3 files changed, 52 insertions(+), 8 deletions(-) + +commit c7c288b89175b1f9545d6ddff25609b7bb243041 +Author: Emmanuele Bassi +Date: Mon Aug 2 17:04:38 2010 +0100 + + Fix the include for json-types.h + + json-glib/json-parser.h | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit e6ea3a6bae044ec7874dfde3b50bb337f00bb3be +Author: Emmanuele Bassi +Date: Mon Aug 2 16:41:18 2010 +0100 + + Exercise the stream API in JsonParser + + json-glib/tests/Makefile.am | 2 + + json-glib/tests/parser-test.c | 78 ++++++++++++++++++++++++++++++++++++++ + json-glib/tests/stream-load.json | 1 + + 3 files changed, 81 insertions(+), 0 deletions(-) + +commit 8cac7f2b6987803e36ff6abe012cd3e017b5b960 +Author: Emmanuele Bassi +Date: Mon Aug 2 16:40:36 2010 +0100 + + docs: Add the new JsonParser stream API + + doc/reference/json-glib-sections.txt | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit 107e53b2daa27b99fb629dd1e2bf9bfd2729b3dd +Author: Emmanuele Bassi +Date: Mon Aug 2 16:39:04 2010 +0100 + + parser: Add loading from a GInputStream + + JsonParser should be able to use a GInputStream (both synchronously and + asynchronously) to retrieve the JSON data and parse it. + + json-glib/json-parser.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++ + json-glib/json-parser.h | 43 +++++--- + 2 files changed, 286 insertions(+), 15 deletions(-) + +commit b164bb5180749aaff385f5d61866875bfbf9c552 +Author: Emmanuele Bassi +Date: Mon Aug 2 16:38:01 2010 +0100 + + build: Use the GIO prefix for cross-references + + doc/reference/Makefile.am | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +commit 6d317b129169352111ab16a0df9aee8b9aa5284b +Author: Emmanuele Bassi +Date: Mon Aug 2 16:37:32 2010 +0100 + + build: Depend on GIO + + Bump up the dependencies to be able to use GIO. + + configure.ac | 8 ++++---- + 1 files changed, 4 insertions(+), 4 deletions(-) + +commit 84230dd03b5db34da5ccef0a4926e18c22124952 +Author: Emmanuele Bassi +Date: Wed Jul 21 23:50:09 2010 +0100 + + docs: Fix up the cgit URL + + README | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +commit 67ad0f21ca554edcd6633014b0bce7dd8d216191 +Author: Emmanuele Bassi +Date: Wed Jul 21 23:49:56 2010 +0100 + + Update NEWS for the next stable release + + NEWS | 10 ++++++++++ + 1 files changed, 10 insertions(+), 0 deletions(-) + +commit 465880e5ccaca086fd1b881bd7175658d627318c +Author: Colin Walters +Date: Tue Jul 6 10:49:27 2010 -0400 + + Make GIR depend on library + + Fixes parallel builds. + + json-glib/Makefile.am | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 66e5f619d55433985460e8c641beb98e85832451 +Author: Emmanuele Bassi +Date: Wed Jun 16 12:07:41 2010 +0100 + + docs: Add JsonBuilder to the API reference + + And silence gtk-doc-scanner by removing an undefined argument from + json_builder_add_null_value(). + + doc/reference/json-glib-docs.xml | 3 ++- + doc/reference/json-glib-sections.txt | 32 ++++++++++++++++++++++++++++++++ + doc/reference/json-glib.types | 2 ++ + json-glib/json-builder.c | 3 ++- + 4 files changed, 38 insertions(+), 2 deletions(-) + +commit 08f3073eeb0f7e2e0973abd23ba4ec978ad302d7 +Author: Luca Bruno +Date: Wed Jun 9 21:31:06 2010 +0200 + + builder: Add convenience API for building JSON trees. + + https://bugzilla.gnome.org/show_bug.cgi?id=621141 + + Signed-off-by: Emmanuele Bassi + + .gitignore | 1 + + json-glib/Makefile.am | 2 + + json-glib/json-builder.c | 682 ++++++++++++++++++++++++++++++++++++++++ + json-glib/json-builder.h | 100 ++++++ + json-glib/json-glib.h | 1 + + json-glib/tests/Makefile.am | 3 + + json-glib/tests/builder-test.c | 121 +++++++ + 7 files changed, 910 insertions(+), 0 deletions(-) + +commit 3f8c8f99126dc1a70d847eded13afd4b64395250 +Author: Emmanuele Bassi +Date: Wed Jun 16 11:54:55 2010 +0100 + + Allow NULL as a value for strings, arrays and objects + + We should not warn when asking for a string, array or object if the + contents were 'null'. + + json-glib/json-array.c | 42 ++++++++++++++++++++++++++++++-------- + json-glib/json-object.c | 45 ++++++++++++++++++++++++++++++++-------- + json-glib/tests/object-test.c | 4 +++ + 3 files changed, 73 insertions(+), 18 deletions(-) + +commit 471bcf28589e0929984c4e6a097ae273b017b3f0 +Author: Anssi Hannula +Date: Fri May 28 11:57:18 2010 +0100 + + Do not hardcode paths in pkgconfig file + + The pkgconfig file correctly sets $prefix to @prefix@, but the other + paths are hardcoded to ones relative to $prefix. + + prefix=@prefix@ + exec_prefix=${prefix} + libdir=${exec_prefix}/lib + includedir=${exec_prefix}/include + + https://bugzilla.gnome.org/show_bug.cgi?id=613282 + + Signed-off-by: Emmanuele Bassi + + json-glib.pc.in | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +commit 70b9235b50dd4257e0bf196e07f65807d65f8091 +Author: Emmanuele Bassi +Date: Fri May 28 11:50:01 2010 +0100 + + docs: Remove unused field annotation + + JsonNode is fully opaque, so there are no fields to annotate. + + json-glib/json-types.h | 1 - + 1 files changed, 0 insertions(+), 1 deletions(-) + +commit aaf08f5c1a08c8aaec2436653114996b98ee1f8c +Author: Emmanuele Bassi +Date: Fri May 28 11:35:05 2010 +0100 + + build: Clean up the build system + + • Use libtool m4 macros, and require libtool >= 2.2.6 + + • Ditch unused platform checks + + • Drop Shave and require automake >= 1.11 + + • Depend on gtk-doc >= 1.13 + + • Use gobject-introspection's Makefile rules, and update introspection.m4 + + README | 4 +- + build/autotools/Makefile.am | 3 - + build/autotools/Makefile.am.silent | 6 --- + build/autotools/introspection.m4 | 6 +++ + build/autotools/shave-libtool.in | 69 ------------------------------- + build/autotools/shave.in | 79 ------------------------------------ + build/autotools/shave.m4 | 77 ----------------------------------- + configure.ac | 44 +++++-------------- + json-glib/Makefile.am | 43 ++++++------------- + 9 files changed, 34 insertions(+), 297 deletions(-) + +commit 6231cf027a8f9c3b11324c1d49a350ba55353123 +Author: Emmanuele Bassi +Date: Wed May 26 08:43:10 2010 +0100 + + parser: Do not access GScanner:token + + The GScanner:token member is declared as GTokenType instead of being an + unsigned int. This means that comparing it to any other enumeration is + going to generate a warning in GCC >= 4.5. Unfortunately, extending the + GTokenType enumeration is the idiomatic way of handling new tokens. + + EPIC. FAIL. + + json-glib/json-parser.c | 9 ++++++--- + 1 files changed, 6 insertions(+), 3 deletions(-) + +commit fcd07918d3ed2b31b047900da9d2fed23dddf7da +Author: Eiichi Sato +Date: Sat Apr 10 01:52:10 2010 +0900 + + Support for surrogate pairs in json string. + + https://bugzilla.gnome.org/show_bug.cgi?id=615799 + + Signed-off-by: Emmanuele Bassi + + json-glib/json-scanner.c | 16 +++++++++++++++- + 1 files changed, 15 insertions(+), 1 deletions(-) + +commit 37a7931f91f8d79def3f4895bc349ba621a826a1 +Author: Emmanuele Bassi +Date: Wed Apr 14 23:19:58 2010 +0100 + + Ignore json-debug.h + + The json-debug.h header is private and not installed; hence, it should + be ignored by gtk-doc. + + doc/reference/Makefile.am | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +commit 28edd58ddfd719d107d89aa364dc208b0126c90f +Author: Emmanuele Bassi +Date: Wed Apr 14 23:19:28 2010 +0100 + + docs: Fix typo + + There is no such thing as a 'JsonArrary' type. + + json-glib/json-parser.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit d40045bd855bfcb73c10967d894a8e91f8675409 +Author: Emmanuele Bassi +Date: Wed Apr 14 23:17:28 2010 +0100 + + docs: Fix typo in JsonParserError annotation + + json-glib/json-parser.h | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit b55d139ed0368f368c6c9c8f8adfab4c91f1f508 +Author: Emmanuele Bassi +Date: Sat Apr 3 14:59:00 2010 +0100 + + parser: Do not increment the index variable + + When parsing an array with a JsonParser with the debugging notes + enabled, we get an erroneous increment of the idx variable - which is + then passed to the JsonParser::array-element signal. + + Thanks to: Michael Stapelberg + + json-glib/json-parser.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 24567790d30e314ee519c8f3756b3514c0c3fd31 +Author: Emmanuele Bassi +Date: Fri Mar 19 15:45:24 2010 +0000 + + parser: Add MISSING_COLON error + + We identify a missing ':' separator between an object member name and + its value, so it would be a good thing to actually have an error code + for that. + + json-glib/json-parser.c | 3 +-- + json-glib/json-parser.h | 2 ++ + json-glib/tests/parser-test.c | 3 ++- + 3 files changed, 5 insertions(+), 3 deletions(-) + +commit 08cec3b7c9202007a5abbf548f8d2f1b54d4d0b4 +Author: Emmanuele Bassi +Date: Fri Mar 19 15:40:48 2010 +0000 + + parser: Refactor the JsonParser logic + + The array and object parsing logic in JsonParser has clearly exploded + beyond control: a simple tightening of the JSON validation almost broke + the parser in two. It it is time to... + + + + REFACTOR THE CODE! + + + + This time, we should be following the JSON state machine and try to do + more prediction of the next state based on peeking the next token. + + The code is fairly cleaner, now; and, most of all, still passes the + validation test suite - which is, you know... nice. + + json-glib/json-parser.c | 480 +++++++++++++++++++---------------------------- + 1 files changed, 196 insertions(+), 284 deletions(-) + +commit 9ce82f8052290f3956c3d80c8739c214da314d42 +Author: Emmanuele Bassi +Date: Fri Mar 19 11:04:00 2010 +0000 + + parser: Return specific error codes + + The JsonScanner error reporting mechanism, which is basically + GScanner's, sucks beyond belief. In order to report an error code we + need to store it inside the JsonParser private structure and then use it + when creating the GError inside the error handler. + + This, frankly, is quite stupid. + + json-glib/json-parser.c | 49 ++++++++++++++++++++++++++++++++-------- + json-glib/json-parser.h | 8 ++++++ + json-glib/tests/parser-test.c | 18 +++++++------- + 3 files changed, 56 insertions(+), 19 deletions(-) + +commit 9d7c58b4fb5edeb8e9413acc9d509447e13962e5 +Author: Emmanuele Bassi +Date: Thu Mar 18 22:41:14 2010 +0000 + + JSON-GLib bug tracking system has been moved + + We are now using bugzilla.gnome.org, since we're already hosted on + git.gnome.org. + + README | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 449656c68a3d6e0d8b96c36414b475e78b9cc272 +Author: Emmanuele Bassi +Date: Thu Mar 18 17:11:11 2010 +0000 + + parser-test: Add a case for double parsing in JsonObject + + json-glib/tests/parser-test.c | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +commit 54756457de3f01d1a487fc6b90c5bd9b5b50bcf3 +Author: Emmanuele Bassi +Date: Thu Mar 18 17:09:38 2010 +0000 + + parser: Re-use json_parse_value() + + The main switch inside json_parse_statement() is re-implementing the + bare value parsing that is also provided by json_parse_value(). We + should kill it off to avoid redundant code. + + json-glib/json-parser.c | 57 +--------------------------------------------- + 1 files changed, 2 insertions(+), 55 deletions(-) + +commit 3355987049560b4d31af22476a7c2b20c9d6665b +Author: Emmanuele Bassi +Date: Thu Mar 18 17:08:44 2010 +0000 + + parser: Add debug annotations for json_parse_value() + + Print out the values we are parsing, for debug purposes. + + json-glib/json-parser.c | 8 ++++++++ + 1 files changed, 8 insertions(+), 0 deletions(-) + +commit f622ee8d8ba54ddea6fbb7311a905ffab7842e8d +Author: Emmanuele Bassi +Date: Thu Mar 18 17:05:57 2010 +0000 + + generator: Use %g format for g_ascii_formatd() + + I should read the documentation for the functions I use: + + Converts a gdouble to a string, using the '.' as decimal point. To + format the number you pass in a printf()-style format string. Allowed + conversion specifiers are 'e', 'E', 'f', 'F', 'g' and 'G'. + -- from g_ascii_formatd() in GLib's API reference + + Epic reading fail. + + json-glib/json-generator.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 90d6e0b8c334b3fd7995c126f07dd61ba0a9e0fb +Author: Emmanuele Bassi +Date: Thu Mar 18 15:26:19 2010 +0000 + + parser-test: Add a complex nested object test + + The nested object test should use something that's really complex: an + object with a nested array and nested object definitions. + + json-glib/tests/parser-test.c | 21 ++++++++++++++++++++- + 1 files changed, 20 insertions(+), 1 deletions(-) + +commit f4c57ac4227c4edb8a4cecb784e871fbd5649ad1 +Author: Emmanuele Bassi +Date: Thu Mar 18 15:24:39 2010 +0000 + + parser: Clean up array and object parsing + + We are doing some of the work twice, especially when dealing with the + trailing commas detection and the unknown tokens after an array element + or an object member definition. + + json-glib/json-parser.c | 44 ++++++++++++++++++++++++-------------------- + 1 files changed, 24 insertions(+), 20 deletions(-) + +commit b3435c6a05ecee58c64dce669ce7e44f829afc98 +Author: Emmanuele Bassi +Date: Thu Mar 18 15:21:29 2010 +0000 + + Add debugging macros + + Similarly to what GTK+ and Clutter do, we can use macros that evaluate + to nothing if JSON_ENABLE_DEBUG is disabled; they evaluate to messages + when the JSON_DEBUG environment variable is set to a debug domain. + + json-glib/Makefile.am | 4 +++- + json-glib/json-debug.c | 37 +++++++++++++++++++++++++++++++++++++ + json-glib/json-debug.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 85 insertions(+), 1 deletions(-) + +commit 88ac0d5111eb528e4c396e4c169ceee4fb046e62 +Author: Emmanuele Bassi +Date: Fri Mar 5 19:23:49 2010 +0000 + + tests: Add another trailing comma invalid test + + json-glib/tests/parser-test.c | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +commit c22c1151e418af9506547804d4bc9fddb60bc0f1 +Author: Emmanuele Bassi +Date: Mon Mar 1 17:42:41 2010 +0000 + + tests: Add negative tests + + Verify that invalid JSON will trigger a parser error. + + json-glib/tests/parser-test.c | 53 +++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 53 insertions(+), 0 deletions(-) + +commit 26668fe238a52a7fd8374f409fc277aaa8efa826 +Author: Emmanuele Bassi +Date: Mon Mar 1 17:41:14 2010 +0000 + + parser: Improve strictness + + Apparently, some breakage crept in JsonParser which allowed invalid JSON + to actually pass. For instance: trailing and missing commas, invalid + barewords and wrong array and object closing braces. + + json-glib/json-parser.c | 117 +++++++++++++++++++++++++++++++---------------- + 1 files changed, 78 insertions(+), 39 deletions(-) + +commit 7d156366e9062349fbe58344712a055839449098 +Author: Emmanuele Bassi +Date: Sun Jan 24 19:09:28 2010 +0000 + + build: Configure Automake + + Add the following options: + + • no-define: we don't need PACKAGE and VERSION defined in config.h + • -Wno-portability: we require GNU make + • dist-bzip2: generate a bz2 tarball when distchecking + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit ea666891cb1e6fc9e527ce6c93a306bc97145f16 +Author: Emmanuele Bassi +Date: Sun Jan 24 18:58:28 2010 +0000 + + docs: Fix the GBoxed registration example + + The registration function for serialization and deserialization has been + replaced by two separate functions before releasing 0.10, but I forgot to + update the example in the documentation. + + json-glib/json-gboxed.c | 11 ++++++----- + 1 files changed, 6 insertions(+), 5 deletions(-) + +commit 4c15bf185dcd55ae5daf6b68d2b58d32e9ac9d5c +Author: Emmanuele Bassi +Date: Sun Jan 10 10:00:32 2010 +0000 + + build: Fix CFLAGS and LDFLAGS for the gcov target + + A copy and paste thinko duplicated the CFLAGS into the LDFLAGS, so we + need to fix that. The CFLAGS should also specify the optimization level + to 0 and turn on debugging notes, in case --enable-debug and + --enable-maintainer-flags are turned off. + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit c09c2dc1c54c83fea4ef93486081fe3fab35452c +Author: Emmanuele Bassi +Date: Sun Jan 10 09:59:14 2010 +0000 + + Initialize every member of JsonObject on construction + + We create JsonObject structures using g_slice_new(), so we need to + initialize every member of the structure ourselves. + + json-glib/json-object.c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit cdffa0eacb52479e04460d8436a08ccecce91edf +Author: Emmanuele Bassi +Date: Sat Jan 2 23:57:31 2010 +0000 + + build: Move compiler flags to AM_CFLAGS + + The AM_CPPFLAGS variable is for pre-processor flags. + + tests/Makefile.am | 4 +++- + 1 files changed, 3 insertions(+), 1 deletions(-) + +commit f20523412e737c2dfae92ba4b9bd86177fd018a0 +Author: Emmanuele Bassi +Date: Sat Jan 2 23:56:42 2010 +0000 + + build: Enable maintainer flags when compiling tests + + The tests should be compiled under the same flags as the rest of the + library. + + json-glib/tests/Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 7b72cad780fdeb42ac1d847210bd2022376f2875 +Author: Emmanuele Bassi +Date: Sat Jan 2 23:50:40 2010 +0000 + + tests: Clean up JsonGenerator test suite + + Use typed accessors instead of using GValues all around. + + json-glib/tests/generator-test.c | 141 ++++++------------------------------- + 1 files changed, 23 insertions(+), 118 deletions(-) + +commit 3c33b61738d74b732805b497accec830b1a05796 +Author: Emmanuele Bassi +Date: Fri Jan 1 18:23:16 2010 +0000 + + tests: Verify Array.get_elements() + + While verifying Array.foreach() we should also verify that the list we + are iterating on is the same returned by the get_elements() method. + + json-glib/tests/array-test.c | 22 +++++++++++++++++----- + 1 files changed, 17 insertions(+), 5 deletions(-) + +commit c8cc10985c3e7aac5ca1c03a7b443951929ed0cb +Author: Emmanuele Bassi +Date: Fri Jan 1 18:02:03 2010 +0000 + + build: Add gcov proxy rule + + The gcov rule lives in json-glib/Makefile.am, but it would be nice to + have a similar rule in the top source directory as well. + + Makefile.am | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit 060eddb098dd2b442f986a39aad2e7bf8c777a15 +Author: Emmanuele Bassi +Date: Tue Dec 29 23:35:00 2009 +0000 + + tests: Re-enable the nested object generator unit + + Use the JSON object example inside the RFC 4627 to verify that the + JsonGenerator creates the right output. This is now possible as we + garantee the order of a JsonObject members. + + json-glib/tests/generator-test.c | 102 +++++++++++++++++++++----------------- + 1 files changed, 56 insertions(+), 46 deletions(-) + +commit 37a9c9544447c4a8ac36d80dd58bd38f80b0aa3c +Author: Emmanuele Bassi +Date: Tue Dec 29 23:02:23 2009 +0000 + + Update the ignore file + + .gitignore | 5 +++-- + 1 files changed, 3 insertions(+), 2 deletions(-) + +commit e32a157f97d96293f6f4061e7d0008d90ff16258 +Author: Emmanuele Bassi +Date: Tue Dec 29 22:59:13 2009 +0000 + + tests: Move Parser and Generator tests + + Use the json-glib/tests directory for testing the data structures of + JSON-GLib: node, object, array, parser and generator. + + The tests/ directory should be used for complex test cases, like the + GObject and GBoxed integration - but the goal is to remove the top-level + tests/ directory altogether, since the conformance test suite should be + built along the json-glib/ directory. + + json-glib/tests/Makefile.am | 26 +- + json-glib/tests/generator-test.c | 357 +++++++++++++++++++++ + json-glib/tests/parser-test.c | 632 ++++++++++++++++++++++++++++++++++++++ + tests/Makefile.am | 8 - + tests/test-generator.c | 357 --------------------- + tests/test-parser.c | 632 -------------------------------------- + 6 files changed, 1006 insertions(+), 1006 deletions(-) + +commit 5b4186eb60e953ddbaadcd53b1996d28b06d4a97 +Author: Emmanuele Bassi +Date: Tue Dec 29 22:53:29 2009 +0000 + + Add GCOV coverage test support + + Use GCOV, a GCC extension, to perform a coverage test on JSON-GLib when + we perform a 'make check'. + + GCOV support builds JSON-GLib with compiler and linker flags that enable + coverage reports; then the check-local target will build the gcov file + for each source file, and the coverage report will be extracted from + those. + + .gitignore | 7 ++++--- + build/autotools/Makefile.am | 3 ++- + build/autotools/Makefile.am.gcov | 35 +++++++++++++++++++++++++++++++++++ + build/autotools/Makefile.am.gtest | 2 ++ + configure.ac | 25 +++++++++++++++++++++++-- + json-glib/Makefile.am | 9 ++++++--- + 6 files changed, 72 insertions(+), 9 deletions(-) + +commit 56f56ae22d981830b696c5861e158802d31240c1 +Author: Emmanuele Bassi +Date: Tue Dec 29 21:56:42 2009 +0000 + + Bump to 0.11 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit dae32677d140a3ba6a15e1198b53ea0b3e1716b0 +Author: Emmanuele Bassi +Date: Tue Dec 29 15:35:21 2009 +0000 + + Post-release version bump to 0.10.1 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 5aff66e8fae3e28c9cb6b74696170154eed303a7 +Author: Emmanuele Bassi +Date: Tue Dec 29 15:33:28 2009 +0000 + + Release 0.10.0 + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 7258a776ea90ea4b2dcc6f1e4e440bb09c581a12 +Author: Emmanuele Bassi +Date: Tue Dec 29 15:31:22 2009 +0000 + + build: Enable introspection when distchecking + + Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 80c5178127052e600146ca889500e263da4ebf10 +Author: Vincent Untz +Date: Sun Dec 27 02:09:44 2009 +0100 + + Initialize out variables before using them + + Here's a small patch for json-glib, to fix some gcc warnings breaking + the build with -Werror (gcc can't know if the variable will get + initialized or not). I didn't find a product for json-glib in bugzilla, + but I guess a mail will work ;-) + + Happy holidays :-) + + Signed-off-by: Emmanuele Bassi + + json-glib/json-gobject.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit e3a57eae397926ad23e216996eda164ed6c15c63 +Author: Emmanuele Bassi +Date: Sun Nov 29 12:50:58 2009 +0000 + + Post-release bump to 0.9.3 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 4477758b73a0712fdef5e7907f6b6f4e0878428e +Author: Emmanuele Bassi +Date: Sun Nov 29 12:34:41 2009 +0000 + + Release 0.9.2 (0.10.0-rc1) + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 654f99c6b0d771cc6b4c68790ab164b0f691307d +Author: Emmanuele Bassi +Date: Sun Nov 29 12:34:08 2009 +0000 + + Update NEWS + + NEWS | 15 +++++++++++++++ + 1 files changed, 15 insertions(+), 0 deletions(-) + +commit 307605046cf89ef922d1d1e7c7b95405f3ecfdfb +Author: Emmanuele Bassi +Date: Sun Nov 29 12:48:25 2009 +0000 + + docs: Fix annotation for json_gobject_to_data() + + json-glib/json-gobject.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit e7eb3ca4a9de1c7348dc82f528cf0858f52849dd +Author: Emmanuele Bassi +Date: Sun Nov 29 12:37:04 2009 +0000 + + docs: Add missing to/from_data methods + + doc/reference/json-glib-sections.txt | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 27bb2c060a7b2ecc4839719d39b2044efe4c3e7a +Author: Emmanuele Bassi +Date: Sun Nov 29 12:46:27 2009 +0000 + + docs: Add release notes to the README + + README | 12 ++++++++++-- + 1 files changed, 10 insertions(+), 2 deletions(-) + +commit 780d82310c47c3f07bcbaab2cefbea0d6decb899 +Author: Emmanuele Bassi +Date: Sun Nov 29 12:33:45 2009 +0000 + + docs: Update the README + + README | 27 ++++++++++++++++----------- + 1 files changed, 16 insertions(+), 11 deletions(-) + +commit d122f9b8c09ad7e82b1a70da5d65ebba2111e6cc +Author: Emmanuele Bassi +Date: Sun Nov 29 12:33:01 2009 +0000 + + build: Clean up the configure summary + + Make the output of the summary a little bit nicer. + + configure.ac | 27 ++++++++++++++++++++------- + 1 files changed, 20 insertions(+), 7 deletions(-) + +commit e4c4480036a82cc5177fc1f184c18019af0e805e +Author: Emmanuele Bassi +Date: Sun Nov 29 12:31:54 2009 +0000 + + build: Require G-I 0.6.4 + + We should require at least 0.6.4 for GObject-Introspection, since that + version allows us to pass the .la file to g-ir-scanner. + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 4445bdb04d5eebc145a1db82aa5b811e8d4ed048 +Author: Emmanuele Bassi +Date: Sat Nov 28 19:13:27 2009 +0000 + + docs: Fix argument name mismatch + + gtk-doc complains that the argument name in the header does not match + the one in the documentation annotation for the GBoxed deserialization + function registration. + + json-glib/json-gobject.h | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 793e827feaa9be3c4ffbe5c0425ac6bcc0c9e6d1 +Author: Emmanuele Bassi +Date: Sat Nov 28 19:06:36 2009 +0000 + + build: Clean up json-glib/Makefile.am rules + + json-glib/Makefile.am | 24 ++++-------------------- + 1 files changed, 4 insertions(+), 20 deletions(-) + +commit 97584658672fe4709fc7c3b24a44b477112322f7 +Author: Emmanuele Bassi +Date: Sat Nov 28 17:39:37 2009 +0000 + + build: Fix out-of-tree enum types file generation + + build/autotools/Makefile.am.enums | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 3cf919e9c7f3201305a1a63a3c270e422a37efed +Author: Emmanuele Bassi +Date: Mon Nov 23 22:20:58 2009 +0000 + + boxed: Split (de)serialization registration + + A GBoxed type defined as: + + struct Boxed { + int foo; + gboolean bar; + int baz; + }; + + Can be represented either by a JSON object: + + { + "foo" : 1, + "bar" : true, + "baz" : 3 + } + + Or by a JSON array: + + [ 1, true, 3 ] + + The current function for registering a serialization and a + deserialization pair does not allow registering more than one + deserialization function - which means that there can only be + one way to deserialize a GBoxed type into a specific JsonNode + type. + + To allow having more than one JsonNodeType associated to a + GBoxed type and a deserialization function we need to split out + the registration of the serialization and deserialization functions + into two distinct functions. + + doc/reference/json-glib-sections.txt | 3 +- + json-glib/json-gboxed.c | 115 ++++++++++++++++++++++------------ + json-glib/json-gobject.c | 4 +- + json-glib/json-gobject.h | 59 +++++++++--------- + tests/test-serialize-boxed.c | 7 +- + 5 files changed, 112 insertions(+), 76 deletions(-) + +commit 61d54cc9e2a3098e876e700a9248428f400a5368 +Author: Tristan Van Berkom +Date: Thu Nov 12 12:59:03 2009 +0000 + + serializable: Make Serializable in charge of parsing + + If a GObject class implements JsonSerializable and has overridden + the serialize_property() vfunc then the Serializable should be fully in + charge of serializing a property - that is: JSON-GLib should not try to + add a fallback in case the serialize_property() implementation returned + NULL. + + This is a change in semantics for JsonSerializable implementations. + + http://bugzilla.openedhand.com/show_bug.cgi?id=1859 + + Signed-off-by: Emmanuele Bassi + + json-glib/json-gobject.c | 7 +++++-- + 1 files changed, 5 insertions(+), 2 deletions(-) + +commit 63dc03da507a216c0764bc0e50fc62b2b77dc1b2 +Author: Emmanuele Bassi +Date: Thu Nov 12 12:38:29 2009 +0000 + + serializable: Add methods proxying default implementations + + If you want to use the default implementation of serialize_property() + and/or deserialize_property() from an object class implementing + JsonSerializable you currently have to peek the interface vtable and + then call the vfunc pointers. + + We can expose the default implementation through functions ourselves and + simplify the required code. + + doc/reference/json-glib-sections.txt | 4 ++ + json-glib/json-gobject.h | 28 +++++++--- + json-glib/json-serializable.c | 92 ++++++++++++++++++++++++++++++++++ + 3 files changed, 115 insertions(+), 9 deletions(-) + +commit 5f484d8c274a2b866f9a3d38eebe2baa1939b7ac +Author: Emmanuele Bassi +Date: Thu Nov 12 12:03:13 2009 +0000 + + gobject: Add deprecation annotations + + This makes it easier to detect when building without + JSON_DISABLE_DEPRECATED. + + json-glib/json-gobject.h | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 3c7811bf24d4b957da730949af6795b728db5bad +Author: Emmanuele Bassi +Date: Thu Nov 12 12:02:41 2009 +0000 + + build: Build without deprecations + + Make sure that JSON-GLib is built without using deprecated API. + + json-glib/Makefile.am | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 923311ee52b77f53c5c529e497ea2a24efaf23ea +Author: Emmanuele Bassi +Date: Thu Nov 12 12:01:04 2009 +0000 + + tests: Do not use deprecated API + + Tests should not be using API marked as deprecated. + + tests/Makefile.am | 2 +- + tests/test-serialize-boxed.c | 4 ++-- + tests/test-serialize-complex.c | 2 +- + tests/test-serialize-full.c | 2 +- + tests/test-serialize-simple.c | 2 +- + 5 files changed, 6 insertions(+), 6 deletions(-) + +commit e3ae84e743e1d1a1a0202e900f443e143cb77f45 +Author: Emmanuele Bassi +Date: Thu Nov 12 11:57:45 2009 +0000 + + tests: Verify Serializable::serialize_property() + + Verify that an object with a custom serialize_property() is effectively + what we expect it to be when it is parsed. + + tests/test-serialize-complex.c | 32 ++++++++++++++++++++++++++++++++ + 1 files changed, 32 insertions(+), 0 deletions(-) + +commit 7cebdd008a02e6cef0514f40327f94eba2a2088e +Author: Cornelius Hald +Date: Thu Nov 12 11:37:54 2009 +0000 + + Doubles are converted to strings containing commas + + Under some locales (e.g. de_DE) a double is converted to a string + containing a comma instead of a dot. That breaks the JSON syntax. + + Example: + Double: 0.34 is converted to 0,34 when using locale de_DE + + http://bugzilla.openedhand.com/show_bug.cgi?id=1826 + + Signed-off-by: Emmanuele Bassi + + json-glib/json-generator.c | 7 ++++++- + 1 files changed, 6 insertions(+), 1 deletions(-) + +commit 30d4efb775cb416212c00e3ececb0f0147739f40 +Author: Emmanuele Bassi +Date: Thu Nov 12 11:28:17 2009 +0000 + + Update Introspection annotations + + • Fix the transfer rules for JsonNode, JsonObject and JsonArray + getters. + + • Annotate the methods returning lists + + json-glib/json-array.c | 18 +++++++++--------- + json-glib/json-node.c | 15 ++++++++------- + json-glib/json-object.c | 25 +++++++++++++------------ + json-glib/json-parser.c | 5 +++-- + 4 files changed, 33 insertions(+), 30 deletions(-) + +commit 47cd2f678a8321faac0e2d00a3538181e7bc1cbf +Author: Emmanuele Bassi +Date: Thu Nov 12 11:27:36 2009 +0000 + + build: Add more flags to the maintainer cflags list + + configure.ac | 4 +++- + 1 files changed, 3 insertions(+), 1 deletions(-) + +commit ecc185d1c542f6ff41f84be026dc380f611a5d6e +Author: Emmanuele Bassi +Date: Thu Nov 12 10:09:17 2009 +0000 + + enum-types: Put back a missing static + + The GEnumValue array defining the enumeration type values must be + static, otherwise everything will crash when trying to retrieve the + enumeration data from the GEnumClass. + + json-glib/json-enum-types.c.in | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 84f5af58c23b174cc9708e81ce8ccbfffa6e68eb +Author: Emmanuele Bassi +Date: Fri Oct 30 10:46:32 2009 +0000 + + object: Return values list in insertion order + + Since we return the member names in insertion order, we should also + return the member values in the same order. + + This also allows us to get rid of the (yucky) internal copies of + g_hash_table_get_keys() and g_hash_table_get_values(), since we use + the hash table only for storage and lookup purposes. + + json-glib/json-object.c | 55 ++++++----------------------------------------- + 1 files changed, 7 insertions(+), 48 deletions(-) + +commit a25a1ded25e5d1f605cffd6da7a5e036151aa70c +Author: Mathias Hasselmann +Date: Thu Oct 29 14:01:04 2009 +0000 + + tests: Verify parsing bare values + + http://bugzilla.openedhand.com/show_bug.cgi?id=1856 + + Signed-off-by: Emmanuele Bassi + + tests/test-parser.c | 38 +++++++++++++++++++++++++++++++++----- + 1 files changed, 33 insertions(+), 5 deletions(-) + +commit 026ea0357fbe95e2acd43555e3b5d00e329c9740 +Author: Emmanuele Bassi +Date: Thu Oct 29 13:59:44 2009 +0000 + + parser: Advance when parsing bare values + + A missing get_next_token() prevents getting the contents of + the tokenizer in order to place them into the JsonNode. + + json-glib/json-parser.c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 578111be53065a2bbeda73865bfa4e178f185649 +Author: Emmanuele Bassi +Date: Wed Oct 28 16:54:34 2009 +0000 + + introspection: Fix the GIR generation + + The rule for creating the JSON-GLib GIR file should use the + json-glib.la shared object, to avoid using the installed + copy of JSON-GLib when compiling the typelib. + + The include file for JSON-GLib is json-glib/json-gobject.h as + well: json-glib/json-object.h does not exist. + + json-glib/Makefile.am | 11 ++++++++--- + 1 files changed, 8 insertions(+), 3 deletions(-) + +commit 8f8ce87730fc0bf102a707e84c4f6106b215cfab +Author: Emmanuele Bassi +Date: Wed Oct 28 16:23:39 2009 +0000 + + gobject: Use from/to data naming convention + + Be more GLib-like, and use + + __from_data() + __to_data() + + Instead of the homebrew "construct" and "serialize", when dealing + with string buffers. + + This means: + + • adding json_gobject_from_data() to deprecate + json_construct_gobject() + • adding json_gobject_to_data() to deprecate + json_serialize_gobject() + + The json_construct_gobject() function also contains a mistake: it + uses gsize with the special value of -1 meaning "slurp the whole + string", but gsize is an unsigned type. The newly added + json_gobject_from_data() correctly uses gssize instead. + + json-glib/json-gobject.c | 63 +++++++++++++++++++++++++++++++++++++++++++--- + json-glib/json-gobject.h | 10 +++++++ + 2 files changed, 69 insertions(+), 4 deletions(-) + +commit 00b4d200849e232cd904d23d3593d6f95252b483 +Author: Emmanuele Bassi +Date: Wed Oct 28 16:05:19 2009 +0000 + + gobject: Uniform JSON<->GObject mapping code + + Rename json_gobject_new() to json_gobject_deserialize(), and + json_gobject_dump() to json_gobject_serialize(); this maps the + JSON GBoxed API. + + Also for consistency, change the serialize() return value and + the deserialize() argument to be JsonNodes of type JSON_NODE_OBJECT. + + doc/reference/json-glib-sections.txt | 4 +- + json-glib/json-gobject.c | 82 ++++++++++++++++++++++------------ + json-glib/json-gobject.h | 10 ++-- + 3 files changed, 61 insertions(+), 35 deletions(-) + +commit fc0607c740b153acc96e4df12a12b042e08e831b +Author: Emmanuele Bassi +Date: Wed Oct 28 12:01:21 2009 +0000 + + docs: Add long descriptions + + The json-boxed and json-serializable sections are missing the + long description for the API reference. + + json-glib/json-gboxed.c | 49 +++++++++++++++++++++++++++++++++++++--- + json-glib/json-serializable.c | 5 +++- + 2 files changed, 49 insertions(+), 5 deletions(-) + +commit 498827110f3d635e545c7351732551676a06a1bf +Author: Emmanuele Bassi +Date: Wed Oct 28 10:07:39 2009 +0000 + + docs: Add the 0.10 symbols index + + doc/reference/json-glib-docs.xml | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +commit 0979bbceb654c5e4b5dadf1e6f7e85bb9df87ca5 +Author: Emmanuele Bassi +Date: Tue Oct 27 20:58:08 2009 +0000 + + docs: Split out GObject-related sections + + Like commit c176f70e593c9cfb4901cd9f27ce54b8aa7152f2 did for the + source code, the documentation should be split three-ways: + + • GObject-related API + • JsonSerializable + • GBoxed-related API + + doc/reference/Makefile.am | 15 +++++++-------- + doc/reference/json-glib-docs.xml | 2 ++ + doc/reference/json-glib-sections.txt | 33 +++++++++++++++++++++++++++------ + 3 files changed, 36 insertions(+), 14 deletions(-) + +commit 2f56ba9021ec4fe1574630404d6b24e4813cf1eb +Author: Emmanuele Bassi +Date: Tue Oct 27 20:57:52 2009 +0000 + + docs: Documentation fixes for gtk-doc + + json-glib/json-gboxed.c | 8 +++++--- + 1 files changed, 5 insertions(+), 3 deletions(-) + +commit c176f70e593c9cfb4901cd9f27ce54b8aa7152f2 +Author: Emmanuele Bassi +Date: Tue Oct 27 20:49:09 2009 +0000 + + build: Split out GBoxed and Serializable + + The json-gobject.c is getting pretty crowded; we should split out + the JsonBoxed API and the JsonSerialized implementation into their + separate source files. + + json-glib/Makefile.am | 4 + + json-glib/json-gboxed.c | 275 ++++++++++++++++++++++++++++ + json-glib/json-gobject-private.h | 39 ++++ + json-glib/json-gobject.c | 371 +------------------------------------- + json-glib/json-serializable.c | 154 ++++++++++++++++ + 5 files changed, 476 insertions(+), 367 deletions(-) + +commit 373fa3d9b73391b38620fbd9ce9b69f358e5f4c8 +Author: Emmanuele Bassi +Date: Tue Oct 27 18:10:19 2009 +0000 + + gobject: Make GObject<->JsonObject functions public + + The functions mapping a GObject to and from a JsonObject should + be public, as they can be used by parsers. + + json-glib/json-gobject.c | 30 ++++++++++++++++++++++++++++-- + json-glib/json-gobject.h | 16 ++++++++++------ + 2 files changed, 38 insertions(+), 8 deletions(-) + +commit 85f7a8e8206168d95b3ef9c02aa584f0fae5b37e +Author: Emmanuele Bassi +Date: Tue Oct 27 18:03:11 2009 +0000 + + gobject: Reuse the list data + + Since we ignore all members that don't have a corresponding + GParamSpec for the class we cannot use: + + members = g_list_prepend (members, pspec->name); + + Because pspec might also be NULL. We can reuse the GList iterator + data field, since that points to data internal to the JsonObject + we are iterating over. + + json-glib/json-gobject.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit ff986ee5b8df45255f4f5ab01be0bbad893bc55e +Author: Emmanuele Bassi +Date: Tue Oct 27 17:53:34 2009 +0000 + + gobject: Add experimental GBoxed<->JSON transformation + + Serializing and deserializing GBoxed types is fairly complicated + currently. If a GObject implements JsonSerializable it is possible + for the class to intercept the JsonNode, parse it manually and + then set the value to the property. + + This leaves a hole opened for: + + • manual (de)serialization of GBoxed types + • (de)serialization of GBoxed properties in classes not + implementing JsonSerializable + + In order to serialize and deserialize a GBoxed JSON-GLib should + provide a mechanism similar to the GValue transformation functions: + when registering the boxed type the developer should also be able + to register a serialization and a deserialization functions pair + matching the tuple: + + (GBoxed type, JSON type) + + The serialization function would be: + + JsonNode *(* JsonBoxedSerializeFunc) (gconstpointer boxed); + + And, conversely, the deserialization function would be: + + gpointer (* JsonBoxedDeserializeFunc) (JsonNode *node); + + Obviously, the whole machinery works only for GBoxed types that + register the serialization and deserialization functions. + + .gitignore | 68 ++++++----- + json-glib/json-gobject.c | 264 +++++++++++++++++++++++++++++++++++++++++- + json-glib/json-gobject.h | 37 ++++++ + tests/Makefile.am | 8 +- + tests/test-serialize-boxed.c | 263 +++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 602 insertions(+), 38 deletions(-) + +commit 7f6a73a0964b66b15e8b5a9858b9bc76b010f67b +Author: Emmanuele Bassi +Date: Tue Oct 27 17:43:38 2009 +0000 + + node: Add a private NodeType-to-string converter + + Useful for debugging and logging purposes. + + json-glib/json-node.c | 23 ++++++++++++++++++++++- + json-glib/json-types-private.h | 2 ++ + 2 files changed, 24 insertions(+), 1 deletions(-) + +commit 3f8990f7a4c362590f19e427aae2f68f27303fe6 +Author: Emmanuele Bassi +Date: Tue Oct 27 17:42:14 2009 +0000 + + Remove a stray ';' that broke deserializing GStrv + + The stray semicolon was preventing the GPtrArray from being + updated. + + json-glib/json-gobject.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 8e550ac4dbb5500a40c7adad88868ede40397db7 +Author: Emmanuele Bassi +Date: Tue Oct 27 17:41:38 2009 +0000 + + Fix compiler warnings (remove unused variables) + + json-glib/json-gobject.c | 3 +-- + 1 files changed, 1 insertions(+), 2 deletions(-) + +commit 3b994a52d9c34f67e4ac52aa4bb0c380789fbd60 +Author: Emmanuele Bassi +Date: Tue Oct 27 17:40:55 2009 +0000 + + build: Export MAINTAINER_CFLAGS + + Apparently, the MAINTAINER_CFLAGS were ignored. + + configure.ac | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 0810711a7fbb915ba9397a66e77babb30765d090 +Author: Emmanuele Bassi +Date: Tue Oct 27 17:40:23 2009 +0000 + + build: Use AM_PROG_CC_C_O + + Use the Automake macro to enable per-target compiler flags. + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit d664f886372afbe1d54e633240e7b7e06ebc45c8 +Author: Emmanuele Bassi +Date: Tue Oct 27 17:40:03 2009 +0000 + + Fix compiler warnings (remove unused variables) + + tests/test-generator.c | 4 ++-- + tests/test-serialize-full.c | 1 - + 2 files changed, 2 insertions(+), 3 deletions(-) + +commit 83dea3b3dd2281dca206e0873b5fed0a2a3d50de +Author: Emmanuele Bassi +Date: Tue Oct 27 14:01:24 2009 +0000 + + gobject: Support constructor properties + + The GObject deserialization code currently skips all the constructor + and constructor-only properties. In order to implement them we can + add a preliminary pass on the JSON object members and build a + GParameter array. + + As we don't have a GObject instance we cannot really use the + Serializable interface to provide custom parsing for complex data + structures, thus we fall back to the default deserialization code + path. + + json-glib/json-gobject.c | 98 ++++++++++++++++++++++++++++++++++++++----- + tests/test-serialize-full.c | 11 +++-- + 2 files changed, 93 insertions(+), 16 deletions(-) + +commit 2616938c7c042fced9be197205a535a8b420534e +Author: Emmanuele Bassi +Date: Tue Oct 27 11:57:38 2009 +0000 + + build: Clean up the build environment + + Some of the rules can be moved into their own files to be included + when needed, like: + + • silent rules (QUIET_*) + • glib-mkenums rules + • glib-genmarshal rules + + Also, the test suite rules should be moved from the top-level of + the project into the build/autotools directory and then included + only where it makes sense. + + This requires changing most of the build system to use the new + files layout. + + .gitignore | 4 +- + Makefile.am | 5 +- + Makefile.decl | 61 ------------------------- + build/Makefile.am | 12 ++++- + build/autotools/Makefile.am | 21 +++++++-- + build/autotools/Makefile.am.enums | 43 +++++++++++++++++ + build/autotools/Makefile.am.gtest | 61 +++++++++++++++++++++++++ + build/autotools/Makefile.am.marshal | 45 ++++++++++++++++++ + build/autotools/Makefile.am.silent | 17 +++++++ + doc/Makefile.am | 12 ++++- + doc/reference/Makefile.am | 5 +- + json-glib/Makefile.am | 86 +++++++++++------------------------ + json-glib/tests/Makefile.am | 12 +++-- + tests/Makefile.am | 15 +++---- + 14 files changed, 251 insertions(+), 148 deletions(-) + +commit 5406301b0e3bf74c0d7ae47a618c416d5c6dc29d +Author: Emmanuele Bassi +Date: Tue Oct 27 11:30:55 2009 +0000 + + gobject: Serialize properties holding a GObject + + Like we deserialize them, we can serialize GObject properties + defined using GParamSpecObject. + + json-glib/json-gobject.c | 14 ++++++++++++++ + 1 files changed, 14 insertions(+), 0 deletions(-) + +commit 3a2176ebf96b33716d1b50068ca44b1d3cd9b0c8 +Author: Emmanuele Bassi +Date: Tue Oct 27 11:29:32 2009 +0000 + + tests: Use properties to change values + + The values inside _init() should match the default values of the + properties as specified by the GParamSpec. If we want to verify + a non-default value we should specify the value when instantiating + the object. + + tests/test-serialize-simple.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 1ff48cdc5d773925bda1ddf8bc904a9ea6a5e643 +Author: Emmanuele Bassi +Date: Tue Oct 27 11:18:51 2009 +0000 + + gobject: Split GObject serialization code + + Like for the deserialization of a GObject into a JsonObject we + should split out the serialization of a GObject into a JsonObject + part of json_serialize_gobject() into its own private function. + + json-glib/json-gobject.c | 113 +++++++++++++++++++++++++--------------------- + 1 files changed, 61 insertions(+), 52 deletions(-) + +commit 27afed8dc89bf9562c3536f0a053d250e70eea4d +Author: Emmanuele Bassi +Date: Tue Oct 27 10:30:27 2009 +0000 + + gobject: Recurse in GParamSpecObject properties + + Use the newly added json_gobject_new() internal function to + recurse into properties defined using GParamSpecObject. + + The same rules used by json_construct_gobject() apply to the + properties storing a GObject - including JsonSerializable + support. + + The test case for serialization and deserialization of a + GObject has been updated to include a property holding a + GObject. + + json-glib/json-gobject.c | 14 +++++++++- + tests/test-serialize-full.c | 58 +++++++++++++++++++++++++++++++++++++----- + 2 files changed, 64 insertions(+), 8 deletions(-) + +commit 317447b52455c56b0123168ab127ce026d7d0c22 +Author: Emmanuele Bassi +Date: Tue Oct 27 10:20:42 2009 +0000 + + gobject: Split JSON to GObject code + + If we want to be able to parse a GParamSpecObject property + we need to use the same code as json_construct_gobject(), minus + the parsing. + + json-glib/json-gobject.c | 152 +++++++++++++++++++++++++--------------------- + 1 files changed, 83 insertions(+), 69 deletions(-) + +commit cba7db96581343e3cbd8e5eb067026efb8cac24e +Author: Emmanuele Bassi +Date: Mon Oct 26 22:36:01 2009 +0000 + + object: Guarantee insertion order + + When iterating over the members of a JsonObject, or when retrieving + the list of members, the insertion order should be preserved by the + JsonObject. This is simply implemented by keeping a mirror list of + the member names. + + Apparently, though JSON does not guarantee any ordering, it is somewhat + expected by JSON (and ECMAScript) users. + + json-glib/json-object.c | 67 +++++++++++++++++++++------------------- + json-glib/json-types-private.h | 3 ++ + tests/test-generator.c | 3 +- + 3 files changed, 40 insertions(+), 33 deletions(-) + +commit d5bebce0286aef5c4b4110c16c22a8ef3dc38405 +Author: Emmanuele Bassi +Date: Mon Sep 28 18:15:48 2009 +0100 + + [node] Add HOLDS macros for quick type checking + + JsonNode should mimick GValue's API and have macros for easy type checking + + doc/reference/json-glib-sections.txt | 7 ++++- + json-glib/json-types.h | 51 ++++++++++++++++++++++++++++++++++ + json-glib/tests/node-test.c | 2 +- + 3 files changed, 58 insertions(+), 2 deletions(-) + +commit 17fc731ed54b754285bac76c7ac23eac6b96bf24 +Author: Emmanuele Bassi +Date: Mon Sep 28 14:02:14 2009 +0100 + + [tests] Add a test case for Object members with empty strings + + Both the Object API and the Parser should not choke on members with + empty strings as their value. The Object should just have a member + associated with a JSON_NODE_VALUE node type and an empty string as + the contents. + + json-glib/tests/object-test.c | 13 +++++++++++++ + tests/test-parser.c | 1 + + 2 files changed, 14 insertions(+), 0 deletions(-) + +commit 5181bf24bf3cde743de590ab3ffa0471df9e4799 +Author: Emmanuele Bassi +Date: Tue Sep 22 15:42:34 2009 +0100 + + Post-branch bump to 0.9.1 + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 3f83767109979c660469b3b5862fbd04469a8011 +Author: Emmanuele Bassi +Date: Tue Sep 22 15:39:46 2009 +0100 + + [release] 0.8.0 + + NEWS | 2 ++ + configure.ac | 8 ++++++-- + 2 files changed, 8 insertions(+), 2 deletions(-) + +commit bd604338b25884edbd86cf531505259948484323 +Author: Emmanuele Bassi +Date: Tue Sep 22 15:34:18 2009 +0100 + + [parser] Return the right expected token + + When parsing a value embedded in a Json Object or Array we need to + return the right expected token so that the generated syntax error + will be correct. + + json-glib/json-parser.c | 16 ++++++++++++++-- + 1 files changed, 14 insertions(+), 2 deletions(-) + +commit f99cf3d3d038eff786f85409f3d04736e2068e74 +Author: Emmanuele Bassi +Date: Tue Sep 22 15:34:07 2009 +0100 + + [parser] Whitespace clean up + + json-glib/json-parser.c | 11 ++++++----- + 1 files changed, 6 insertions(+), 5 deletions(-) + +commit 6c20aae2b49a6d64db5e7d1ff3a82950ea9dc58a +Author: Emmanuele Bassi +Date: Mon Sep 7 22:30:11 2009 +0100 + + [build] Prefer automake-1.11 silent rules to shave + + If we have automake-1.11 installed then we should enable the + AM_SILENT_RULES machinery instead of Shave - even though Shave's + output is a lot cleaner. + + autogen.sh | 2 +- + configure.ac | 20 ++++++++++++++++---- + json-glib/Makefile.am | 6 +++++- + 3 files changed, 22 insertions(+), 6 deletions(-) + +commit 7b4bb80930f12a366d5e5a5eea5b398972cd4891 +Author: Emmanuele Bassi +Date: Sun Sep 6 17:31:41 2009 +0100 + + [node] Add JsonNode.set_parent() + + Add the setter for JsonNode:parent, to be used in JsonParser instead + of directly accessing the JsonNode structure. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-node.c | 18 ++++++++++++++++++ + json-glib/json-parser.c | 14 +++++++------- + json-glib/json-types.h | 2 ++ + tests/test-parser.c | 10 ++++++++++ + 5 files changed, 38 insertions(+), 7 deletions(-) + +commit c1b76a73e0b2c6e0c7afced10bd6079a0f5b0f5d +Author: Emmanuele Bassi +Date: Sun Sep 6 17:15:46 2009 +0100 + + [parser] Clean up value parsing + + The code that parses a value within an object and an array should + be moved to its own function to avoid duplication. + + json-glib/json-parser.c | 199 +++++++++++++++++++---------------------------- + tests/test-parser.c | 9 +- + 2 files changed, 86 insertions(+), 122 deletions(-) + +commit d3f005c27d9a8a46259205f2f8077fb01a6a3609 +Author: Emmanuele Bassi +Date: Sun Sep 6 14:44:18 2009 +0100 + + [docs] Use proper xi:include for indexes + + Instead of relying on gtk-doc we can do the proper inclusion of the + API indexes using XInclude, complete with fallbacks. + + Also, we should include the additional pages that gtk-doc generates + for us, like the annotations glossary and the object tree. + + doc/reference/json-glib-docs.xml | 17 +++++++++++++++++ + 1 files changed, 17 insertions(+), 0 deletions(-) + +commit 7442a3011a860f12cbd40b6687b699b0b648d6b7 +Author: Emmanuele Bassi +Date: Wed Sep 2 16:41:51 2009 +0100 + + [docs] Small documentation fixes + + Clean up some notes, and add introspection annotations where needed. + + json-glib/json-generator.c | 11 ++++++----- + json-glib/json-gobject.c | 24 ++++++++++++------------ + json-glib/json-parser.c | 3 ++- + 3 files changed, 20 insertions(+), 18 deletions(-) + +commit d7d1e702b1d67fa1ec830a46650a02367ce1dd29 +Author: Emmanuele Bassi +Date: Sun Aug 16 05:52:46 2009 +0100 + + [docs] Remove note about normalization of member names + + The normalization of member names inside JsonObject was removed by + commit 8a7e0f381dc7e49745680df92ebb428f18bf4832. + + json-glib/json-object.c | 6 +----- + 1 files changed, 1 insertions(+), 5 deletions(-) + +commit 9362ccc891c914dbcf1be6bd067b0b37ae688057 +Author: Emmanuele Bassi +Date: Thu Aug 13 15:10:27 2009 +0100 + + [docs] Generate new symbols index for 0.6 and 0.8 + + doc/reference/json-glib-docs.xml | 8 ++++++++ + 1 files changed, 8 insertions(+), 0 deletions(-) + +commit 087bfe83412dca8de1b2dd67c74f490e44ecb96a +Author: Emmanuele Bassi +Date: Thu Aug 13 15:10:07 2009 +0100 + + [docs] Remove the version number from the title + + doc/reference/json-glib-docs.xml | 11 ++++++++--- + 1 files changed, 8 insertions(+), 3 deletions(-) + +commit c7d31d55b50b23c6c1067cb3541f73dddc12127a +Author: Emmanuele Bassi +Date: Thu Aug 13 15:08:56 2009 +0100 + + [docs] Let gtk-doc build the correct index + + Use the --name-space command line argument for gtk-doc to detect the + right namespace of JSON-GLib, and create a useful index. + + doc/reference/Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit be07ba7b983b34231ad2f0fd7fe60ae47b14ce7e +Author: Emmanuele Bassi +Date: Thu Aug 13 14:51:42 2009 +0100 + + [docs] Rename main file to json-glib-docs.xml + + doc/reference/Makefile.am | 36 +++++---- + doc/reference/json-glib-docs.sgml | 163 ------------------------------------- + doc/reference/json-glib-docs.xml | 163 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 182 insertions(+), 180 deletions(-) + +commit 9661ff342b439b5b398f7bf9ba931d6139d5143d +Author: Emmanuele Bassi +Date: Wed Aug 12 16:09:33 2009 +0100 + + Post-release version bump to 0.7.7 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit d5922b42604c09ba7ebcb0adc1566d0a33a99808 +Author: Emmanuele Bassi +Date: Wed Aug 12 16:06:40 2009 +0100 + + [release] 0.7.6 (brown paper bag) + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 9a647104ca77d4c4272845fed4bfae028098afd3 +Author: Emmanuele Bassi +Date: Wed Aug 12 15:56:00 2009 +0100 + + Actually use the int64 support in the Scanner + + We switched everything to 64 bit integers but then I forgot to + enable the support for actually making the tokenizer store the + parsed integers into a 64 bit value. + + Bad Emmanuele, no cookie for you. + + json-glib/json-parser.c | 12 ++++++------ + json-glib/json-scanner.c | 1 + + 2 files changed, 7 insertions(+), 6 deletions(-) + +commit f3e0618ee1d8aa90d0ba22e9abe5c7d6b849e0ea +Author: Emmanuele Bassi +Date: Wed Aug 12 14:08:35 2009 +0100 + + Post-release version bump to 0.7.5 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit c1c691fc6b58a990ee9bb17504189064051d5a12 +Author: Emmanuele Bassi +Date: Wed Aug 12 13:56:08 2009 +0100 + + [release] 0.7.4 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 12bc49ea08c50da9a7ed53c475fe873421432ebe +Author: Emmanuele Bassi +Date: Wed Aug 12 14:01:49 2009 +0100 + + Update the NEWS file + + NEWS | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 75b888c26eebf7784c5af045a8c6af353e56b2e9 +Author: Emmanuele Bassi +Date: Wed Aug 12 13:59:52 2009 +0100 + + [build] Use our libtool for the typelib + + When compiling the GIR into a .typelib file we can use our own + libtool script; shave will proxy it to the right place. + + json-glib/Makefile.am | 20 +++++++++++--------- + 1 files changed, 11 insertions(+), 9 deletions(-) + +commit d84c0f367b06e094ff693d60a724b9f141c33ca9 +Author: Emmanuele Bassi +Date: Wed Aug 12 13:48:17 2009 +0100 + + Disallow single header file inclusion + + The correct header file for JSON-GLib is, and has always been, + json-glib.h. Anything else was not supported, as we've been + moving around stuff for a while, now. + + This commit enforces the single include file, using the same + policy enacted by other libraries, like: GLib, GTK+ and Clutter. + + json-glib/json-enum-types.h.in | 4 ++++ + json-glib/json-generator.h | 4 ++++ + json-glib/json-glib.h | 6 ++++++ + json-glib/json-parser.h | 4 ++++ + json-glib/json-types.h | 4 ++++ + json-glib/json-version.h.in | 4 ++++ + 6 files changed, 26 insertions(+), 0 deletions(-) + +commit 3adba015e1c1f9edc7b7b2c4364d65f813216225 +Author: Emmanuele Bassi +Date: Wed Aug 12 13:46:46 2009 +0100 + + [introspection] Use json-gobject.h + + The json-gobject.h header file includes json-glib.h and all the + GObject-related serialization and deserialization API; we assume + that if you want introspection you should be using this header + and not the plain json-glib.h. + + json-glib/Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit a9386e20bf8e16e17a89dda24781c63bf495aa97 +Author: Emmanuele Bassi +Date: Wed Aug 12 13:46:05 2009 +0100 + + [tests] Include json-glib.h + + Do not include json-types.h, use the correct global include. + + json-glib/tests/array-test.c | 2 +- + json-glib/tests/node-test.c | 2 +- + json-glib/tests/object-test.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +commit 8a7e0f381dc7e49745680df92ebb428f18bf4832 +Author: Emmanuele Bassi +Date: Wed Aug 12 12:29:34 2009 +0100 + + Do not sanitize the object member's name + + JsonObject sanitizes the name of the member to replace all + characters defined by G_STR_DELIMITERS with '_'. This is + absolutely brain damaged, since a member name can be any + valid JSON string. + + Obviously, if a member name maps to a GObject property is + entirely up to the GObject code to decide whether to sanitize + the member name or not. + + json-glib/json-object.c | 35 ++++++----------------------------- + 1 files changed, 6 insertions(+), 29 deletions(-) + +commit ad638149c2cc1a17c0d2ad1482d932c8940c64e0 +Author: Emmanuele Bassi +Date: Wed Aug 12 12:22:44 2009 +0100 + + Update NEWS + + NEWS | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit d87b18675ac02f42be23bf4070134690b8b9934b +Author: Emmanuele Bassi +Date: Wed Aug 12 12:13:11 2009 +0100 + + Auto-promote integer types to G_TYPE_INT64 + + The JSON RFC does not specify the size of the integer type, thus + implicitly falling back to machine-size. + + This would all be fine and dandy if some demented Web Developer (and + I use the term "developer" *very much* loosely) did not decide to + use integers to store unique identifiers for objects; obviously, you + can't have more than 2^32-1 status messages in a database with + millions of users who update their status multiple times per day. + Right, Twitter? + + Anyway, some languages do a type auto-promotion from Integer to + Long, thus pushing the limit of allowed positive values -- until the + next integer overflow, that is. C, and GLib, do not do that + transparently for us so we need to: + + - always use gint64 when parsing a JSON data stream using + JsonScanner + - move all the Node, Object and Array APIs to gint64 + - auto-promote G_TYPE_INT to G_TYPE_INT64 when setting + a GValue manually + - auto-promote and auto-demote G_TYPE_INT properties when + (de)serializing GObjects. + + The GLib types used internally by JSON-GLib are, thus: + + integer -> G_TYPE_INT64 + boolean -> G_TYPE_BOOLEAN + float -> G_TYPE_DOUBLE + string -> G_TYPE_STRING + + json-glib/json-array.c | 4 +- + json-glib/json-generator.c | 4 +- + json-glib/json-gobject.c | 58 ++++++++++++++++++++++++++++++--------- + json-glib/json-node.c | 61 +++++++++++++++++++++++++++++++++-------- + json-glib/json-object.c | 4 +- + json-glib/json-types.h | 12 ++++---- + json-glib/tests/array-test.c | 2 +- + json-glib/tests/node-test.c | 14 +++++----- + json-glib/tests/object-test.c | 2 +- + tests/test-generator.c | 4 +- + tests/test-parser.c | 4 +- + tests/test-serialize-full.c | 9 +----- + 12 files changed, 121 insertions(+), 57 deletions(-) + +commit 7411cadc0fdd9ffc2bd7004c9980913ac857a495 +Author: Emmanuele Bassi +Date: Sun Jun 28 23:52:34 2009 +0100 + + Indentation fixes + + tests/test-parser.c | 94 +++++++++++++++++++++++++------------------------- + 1 files changed, 47 insertions(+), 47 deletions(-) + +commit 112a8ec8dfd9c46304008b62e8ab256ed7714644 +Author: Emmanuele Bassi +Date: Sun Jun 28 23:49:51 2009 +0100 + + [parser] Advance the tokenizer to avoid an infinite loop + + The tokenizer is not advanced when we peek a base value and return. + This causes an endless loop which terminates only if the OOM killer + in the kernel gets the right process. + + Thanks to Thomas Weidner for catching and reporting the issue. + + json-glib/json-parser.c | 16 +++++++++++----- + 1 files changed, 11 insertions(+), 5 deletions(-) + +commit 4ecdd6bedb7961f3a33971aa1f2338115a60f7f3 +Author: Emmanuele Bassi +Date: Sun Jun 28 23:48:36 2009 +0100 + + [tests] Verify parsing base values + + A JSON document containing just a base value (null, true/false, an + integer, a floating point value or a string) is still a valid JSON + document, and JsonParser must create the correct JsonNode. + + tests/test-parser.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 59 insertions(+), 0 deletions(-) + +commit 9a3b7a020716e23f1330915f1768a7c00bffbeb5 +Author: Emmanuele Bassi +Date: Thu Jun 25 10:37:43 2009 +0100 + + [docs] Fix typo in JsonObject::set_object_member() + + The passed value is a pointer to a JsonObject, not to a JsonArray. + + json-glib/json-object.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 68d872f15022217c0ff1cf516aec917d600fd762 +Author: Rodrigo Moya +Date: Wed Jun 24 14:07:18 2009 +0200 + + Use JSON_NODE_OBJECT, not JSON_NODE_ARRAY when creating the node in json_object_set_object_member + + Reviewed by Emmanuele Bassi + + json-glib/json-object.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 6e706fb2b7a371f25c169ed25d4b617f2dc05b63 +Author: Emmanuele Bassi +Date: Sun Jun 21 10:58:24 2009 +0100 + + [generator] Pre-compute the escape table + + Instead of allocating the escape table to be used with g_strescape() + for each string we can have it unrolled already in code. + + Thanks to: Christian Persch + + Fixes bug: + + http://bugzilla.openedhand.com/show_bug.cgi?id=1648 + + json-glib/json-generator.c | 43 +++++++++++++++++++++++++++++++++---------- + 1 files changed, 33 insertions(+), 10 deletions(-) + +commit a9416b7e2823f1fffe639d90bb352a337815a70e +Author: Emmanuele Bassi +Date: Thu Jun 18 11:23:56 2009 +0100 + + Post-release bump to 0.7.3 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 1d13a409bb911141653a75d44e90feac9bfc2862 +Author: Emmanuele Bassi +Date: Thu Jun 18 11:20:35 2009 +0100 + + [release] 0.7.2 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit ff97e679522c52be6b5dfe0f851416cdeac7f921 +Author: Emmanuele Bassi +Date: Thu Jun 18 11:14:37 2009 +0100 + + [release] Update NEWS + + NEWS | 19 +++++++++++++++++++ + 1 files changed, 19 insertions(+), 0 deletions(-) + +commit 182ca9cd9a3fabaa2533d841154856c61c570d08 +Author: Emmanuele Bassi +Date: Thu Jun 18 11:14:13 2009 +0100 + + [build] Fixes for passing distcheck + + Makefile.am | 17 ++++++++++++----- + build/Makefile.am | 2 ++ + build/autotools/Makefile.am | 4 +++- + doc/reference/Makefile.am | 20 +++++++++++--------- + json-glib/Makefile.am | 27 ++++++++++++++------------- + 5 files changed, 42 insertions(+), 28 deletions(-) + +commit 019d8d4c1c56250cd507283c0fc478a38364e92a +Author: Emmanuele Bassi +Date: Thu Jun 18 10:05:57 2009 +0100 + + [build] Let gtk-doc ignore json-types-private.h + + The type definitions in json-types-private.h are not meant to be + documented or scanned. + + doc/reference/Makefile.am | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 41649f049637864cfce21f6615c7d8bcaf3f03b8 +Author: Emmanuele Bassi +Date: Thu Jun 18 10:03:23 2009 +0100 + + [docs] Update the README file + + README | 30 ++++++++++++++++++++---------- + 1 files changed, 20 insertions(+), 10 deletions(-) + +commit 97fc00b1a5e3878074488ee93d9b52570983da1f +Author: Emmanuele Bassi +Date: Thu Jun 18 10:03:05 2009 +0100 + + [build] Use AS_CASE m4 macro, instead of AS_IF + + configure.ac | 30 ++++++++++++++++-------------- + 1 files changed, 16 insertions(+), 14 deletions(-) + +commit 610cd260ac50c09809d6273c328ecea5a21f51bb +Author: Emmanuele Bassi +Date: Thu Jun 18 10:02:39 2009 +0100 + + [build] Require automake 1.10 + + configure.ac | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +commit bddc717b8b6f2f1501c30f78159c20394665d335 +Author: Emmanuele Bassi +Date: Tue Jun 9 11:48:02 2009 +0100 + + [build] Add introspection generation + + Build the Introspection data when compiling JSON-GLib; this way, the + API should be available to other languages automatically. + + .gitignore | 3 + + build/autotools/Makefile.am | 7 +++- + build/autotools/introspection.m4 | 88 ++++++++++++++++++++++++++++++++++++++ + configure.ac | 13 +++-- + json-glib/Makefile.am | 30 +++++++++++++ + 5 files changed, 135 insertions(+), 6 deletions(-) + +commit 3ddeb7a8779a4c51453289aa15af130af480eea6 +Author: Emmanuele Bassi +Date: Tue Jun 9 11:24:54 2009 +0100 + + [build] Add AS_COMPILER_FLAGS + + Use the m4 AS_COMPILER_FLAGS macro to check for the maintainer + compiler flags supported by the C compiler. This should allow the + build to handle gracefully different versions of GCC. + + .gitignore | 1 + + Makefile.am | 2 + + build/autotools/Makefile.am | 2 +- + build/autotools/as-compiler-flag.m4 | 62 +++++++++++++++++++++++++++++++++++ + configure.ac | 5 ++- + 5 files changed, 70 insertions(+), 2 deletions(-) + +commit 9f817eae9fe0d09441fa78b11ef148a3f4affe22 +Author: Emmanuele Bassi +Date: Tue Jun 9 11:21:19 2009 +0100 + + [tests] Do not namespace with json- + + tests/test-generator.c | 10 +++++----- + tests/test-parser.c | 18 +++++++++--------- + 2 files changed, 14 insertions(+), 14 deletions(-) + +commit bde3da83b1a144fc0eff1db5a2aa790462bb685e +Author: Emmanuele Bassi +Date: Tue Jun 9 11:15:53 2009 +0100 + + [git ignore] Add m4 files under build/ + + .gitignore | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit c12786444e0175464295fb9a98c600060bffa0ed +Author: Emmanuele Bassi +Date: Tue Jun 9 11:13:30 2009 +0100 + + Fix license and copyright notices + + THere is no such thing as the "Lesser General Public License + version 2": the LGPL v2 is the "Library GPL", and has been + superceded by v2.1 with the new "Lesser GPL" name. + + Also, the copyright is now Intel Corp. + + json-glib/json-array.c | 8 ++++++-- + json-glib/json-generator.c | 8 ++++++-- + json-glib/json-generator.h | 8 ++++++-- + json-glib/json-glib.h | 23 +++++++++++++++++++++++ + json-glib/json-gobject.h | 8 ++++++-- + json-glib/json-node.c | 8 ++++++-- + json-glib/json-object.c | 9 ++++++--- + json-glib/json-parser.c | 8 ++++++-- + json-glib/json-parser.h | 8 ++++++-- + json-glib/json-version.h.in | 8 ++++++-- + 10 files changed, 77 insertions(+), 19 deletions(-) + +commit 069cdc8c4ea9024b1583f074815e16e9ddf7db7f +Author: Emmanuele Bassi +Date: Tue Jun 9 11:05:23 2009 +0100 + + [node] Make JsonNode completely private + + The JsonNode structure has always been meant to be completely + opaque; we indirectly exposed the :type member, but only for + access through the JSON_NODE_TYPE() macro. + + Since that macro has become a proxy for the json_node_get_node_type() + function we can safely move everything into a private, uninstalled + header file and let JsonNode be completely opaque to the developer. + + json-glib/Makefile.am | 1 + + json-glib/json-array.c | 9 +----- + json-glib/json-generator.c | 2 + + json-glib/json-gobject.c | 2 + + json-glib/json-node.c | 2 +- + json-glib/json-object.c | 9 +----- + json-glib/json-parser.c | 2 + + json-glib/json-types-private.h | 61 ++++++++++++++++++++++++++++++++++++++++ + json-glib/json-types.h | 38 +++++++++--------------- + json-glib/tests/node-test.c | 8 ++-- + 10 files changed, 89 insertions(+), 45 deletions(-) + +commit 930fdf4c4dff1f5310a946c2a9f5b6860f7c8ba8 +Author: Emmanuele Bassi +Date: Sun May 17 19:44:41 2009 +0100 + + Add JsonArray iteration function + + Similarly to commit 3057a172 for JsonObject, the newly added + json_array_foreach_element() iterates over a JSON array data + type. + + doc/reference/json-glib-sections.txt | 2 + + json-glib/json-array.c | 35 ++++++++++++++++++++++++ + json-glib/json-types.h | 21 +++++++++++++++ + json-glib/tests/array-test.c | 48 ++++++++++++++++++++++++++++++++++ + 4 files changed, 106 insertions(+), 0 deletions(-) + +commit 3057a1722e27a13b39ddec4754fb6abda1aea199 +Author: Emmanuele Bassi +Date: Sat May 16 20:09:07 2009 +0100 + + Add JsonObject iteration function + + The json_object_foreach_member() function iterates over a JsonObject + data type. + + doc/reference/json-glib-sections.txt | 2 + + json-glib/json-object.c | 55 ++++++++++++++++++++++++++++++++ + json-glib/json-types.h | 25 +++++++++++++- + json-glib/tests/object-test.c | 57 ++++++++++++++++++++++++++++++++++ + 4 files changed, 137 insertions(+), 2 deletions(-) + +commit 5778210462b8b7a1a5d98466508276f712ea8c47 +Author: Emmanuele Bassi +Date: Sat May 16 13:53:58 2009 +0100 + + Update the enum types templates + + The autogenerated enumeration types registration code should + be using the same pattern used by the G_DEFINE_TYPE() macros, + with GOnce to enable atomic (and thread-safe) type registration. + + json-glib/json-enum-types.c.in | 18 +++++++++++++----- + 1 files changed, 13 insertions(+), 5 deletions(-) + +commit 43d4bd151cd6979ecc6fdad5fab3e7988117c7be +Author: Emmanuele Bassi +Date: Sun Apr 19 00:36:08 2009 +0100 + + [doap] Add a category element + + json-glib.doap | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 58999bddac74c176fbd8544fa2cd30e2f067d863 +Author: Emmanuele Bassi +Date: Fri Apr 17 15:45:42 2009 +0100 + + [node] Add is_null() method + + The json_node_is_null() function is just a quick check for + nodes set to null. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-node.c | 20 ++++++++++++++++++++ + json-glib/json-types.h | 1 + + 3 files changed, 22 insertions(+), 0 deletions(-) + +commit ba46d8e07a8e2dd50a3b1fff8b8c3303e3686480 +Author: Emmanuele Bassi +Date: Fri Apr 17 15:38:40 2009 +0100 + + [node] Make JSON_NODE_TYPE call a function + + Second pass at adding type safety to the JsonNode type checks. + + The JSON_NODE_TYPE macro now calls the json_node_get_node_type() + function which hides the JsonNode.type structure field and gives + us a little bit more of future compatibility. + + json-glib/json-types.h | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit d1e7d1ecd05687624f7149dad75a5fac9a645e72 +Author: Emmanuele Bassi +Date: Fri Apr 17 15:36:09 2009 +0100 + + [node] Add function version of JSON_NODE_TYPE macro + + First pass at adding some type safety to the JsonNode type checks, + and at removing every mention of the JsonNode interna fields. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-node.c | 18 ++++++++++++++++++ + json-glib/json-types.h | 5 +++-- + 3 files changed, 22 insertions(+), 2 deletions(-) + +commit f2f43d17254317d35ea0cc8206592ecbcb856b68 +Author: Emmanuele Bassi +Date: Fri Apr 17 15:22:04 2009 +0100 + + Intern the remaining type names + + JsonArray and JsonSerializable type names should be interned like + the rest of the types. + + json-glib/json-array.c | 2 +- + json-glib/json-gobject.c | 3 ++- + 2 files changed, 3 insertions(+), 2 deletions(-) + +commit bfcc50d5b199e72ed3e5a7556ac2294442e3ac55 +Author: Emmanuele Bassi +Date: Fri Apr 17 15:19:42 2009 +0100 + + [docs] Remove newline from the version template + + The newline at the end of the version.xml.in file is retained + when using its contents as the version entity in the API reference + docbook. + + doc/reference/version.xml.in | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit cbc92b72b2d7870a916f24055f8c2aa2371aa4ea +Author: Emmanuele Bassi +Date: Fri Apr 17 15:02:44 2009 +0100 + + [tests] Use the typed accessors in test-generator + + Other than using the GValue-based methods we should also be using + the typed JsonNode accessors and the newly added JsonObject and + JsonArray typed accessors. + + tests/test-generator.c | 63 +++++++---------------------------------------- + 1 files changed, 10 insertions(+), 53 deletions(-) + +commit e437ce609e333c5b8ede21174dfba032c1bbfb00 +Author: Emmanuele Bassi +Date: Fri Apr 17 14:49:25 2009 +0100 + + Add convenience accessors to JsonArray + + Like commit 5bb6ea91 did for JsonObject, we should add typed + convenience accessors to JsonArray in order to cut down the + amount of nodes needed when parsing and generating JSON data + streams. + + As for JsonObject, the amount of types is small enough to avoid + the combinatorial API explosion. + + doc/reference/json-glib-sections.txt | 22 ++- + json-glib/json-array.c | 386 ++++++++++++++++++++++++++++++++++ + json-glib/json-types.h | 57 ++++-- + 3 files changed, 447 insertions(+), 18 deletions(-) + +commit 4819ca625d1b73270797f913495a3835297950c2 +Author: Emmanuele Bassi +Date: Fri Apr 17 11:44:01 2009 +0100 + + Update after the json_object_add_member() deprecation + + Since json_object_add_member() has been deprecated and it's using + a gcc compiler attribute to loudly complain while compiling the + library, we should restore the sanity and use json_object_set_member() + instead. + + json-glib/json-gobject.c | 2 +- + json-glib/json-parser.c | 6 +++--- + json-glib/tests/node-test.c | 2 +- + json-glib/tests/object-test.c | 4 ++-- + tests/test-generator.c | 26 +++++++++++++------------- + tests/test-serialize-complex.c | 4 ++-- + tests/test-serialize-full.c | 4 ++-- + 7 files changed, 24 insertions(+), 24 deletions(-) + +commit 5bb6ea91accb1d35f0ea4dae8f2b8f71bdbd134d +Author: Emmanuele Bassi +Date: Fri Apr 17 11:35:57 2009 +0100 + + Deprecate add_member() and add set_member() and friends + + The add_member() method of JsonObject has wee bit weird semantics: if + the member to be added already exists it prints a scary warning and + returns - and yet it calls g_hash_table_replace() internally as if it + overwrites the member. + + So, instead of changing semantics midway we can: + + - add a json_object_set_member() which adds a new member and + overwrites existing members + + - deprecate json_object_add_member() + + While we're at it, we can add convenience wrappers for set_member() + and get_member() that don't require us toying with nodes; luckily, + since the amount of valid types we can add to a JsonObject is limited, + this does not lead to a combinatorial API explosion. + + doc/reference/json-glib-sections.txt | 17 ++ + json-glib/json-object.c | 489 ++++++++++++++++++++++++++++++++-- + json-glib/json-types.h | 83 +++++-- + 3 files changed, 555 insertions(+), 34 deletions(-) + +commit 2a768cb5e553afc4a9ac0d9bf1dc36a183821983 +Author: Emmanuele Bassi +Date: Thu Apr 16 19:57:11 2009 +0100 + + [doap] Add GNOME extensions + + The GNOME project provides an extension to DOAP that allows the + description of the maintainer/author of a project to specify the + GNOME userid. + + json-glib.doap | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit 17822292acb4d1c9b4f414ea0e4b858809d3b858 +Author: Emmanuele Bassi +Date: Thu Apr 16 18:18:45 2009 +0100 + + [doap] Add 0.6 releases + + Describe the releases of the 0.6 cycle. + + json-glib.doap | 18 ++++++++++++++++++ + 1 files changed, 18 insertions(+), 0 deletions(-) + +commit 470f3a4299153362deea260c806d9b87870af3de +Author: Emmanuele Bassi +Date: Thu Apr 16 18:03:31 2009 +0100 + + Add JSON-GLib DOAP description + + Use DOAP (Description Of A Project) to describe JSON-GLib. The + GNOME Cgit instance will pick up the DOAP file and update itself. + + json-glib.doap | 30 ++++++++++++++++++++++++++++++ + 1 files changed, 30 insertions(+), 0 deletions(-) + +commit 764a3f79fe0d8d13e24bd921a16a46f4603bcbf1 +Author: Emmanuele Bassi +Date: Thu Apr 16 11:28:24 2009 +0100 + + Add MAINTAINERS file + + Every GNOME project needs a MAINTAINERS file. + + MAINTAINERS | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit f9956b4eadcfc9bbd5c0bc4b861ff782779e8a9a +Author: Emmanuele Bassi +Date: Tue Apr 14 00:09:35 2009 +0100 + + [node] Do not overwrite when copying + + Bug 1353 - Copying JSON_NODE_VALUE nodes unreliable at best + + When copying a JsonNode to another we do an implicit memcpy using: + + *copy = *src + + Which works well enough with pointers, but makes a mess out of the + value-based nodes. + + We should just copy the type of the original JsonNode and leave the + rest to the switch() block. + + In order to catch potential regressions, we also need a more + thorough test unit for the JsonNode copy operation. + + json-glib/json-node.c | 2 +- + json-glib/tests/node-test.c | 45 +++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 44 insertions(+), 3 deletions(-) + +commit e2c65a75d68aafa26f2084928e732961e48beb99 +Author: Emmanuele Bassi +Date: Mon Apr 13 23:46:52 2009 +0100 + + [build] Improve the build system + + Clean up the configure.ac and Makefile.am files to use something + that is not entirely made of FAIL. + + Also, use Shave to sanitize the libtool and compiler incantations + and restore sanity to the build system. + + .gitignore | 5 ++ + autogen.sh | 9 ++++ + build/Makefile.am | 1 + + build/autotools/Makefile.am | 3 + + build/autotools/shave-libtool.in | 69 +++++++++++++++++++++++++++++ + build/autotools/shave.in | 79 +++++++++++++++++++++++++++++++++ + build/autotools/shave.m4 | 77 ++++++++++++++++++++++++++++++++ + configure.ac | 88 +++++++++++++++++++++---------------- + json-glib/Makefile.am | 89 +++++++++++++++++++------------------ + 9 files changed, 339 insertions(+), 81 deletions(-) + +commit 8080df63b4b4eae3b59d1214fc67f48149f49773 +Author: Emmanuele Bassi +Date: Mon Apr 13 22:25:42 2009 +0100 + + [parser] Prevent leaks on error codepaths + + Static analysis of the code showed some potential leaks inside + error paths for JsonParser. + + Thanks to: Gordon Williams + + json-glib/json-parser.c | 38 +++++++++++++++++++++++++++++++++----- + 1 files changed, 33 insertions(+), 5 deletions(-) + +commit ca329a7d5c1185cdf15fb85891693eca30295de1 +Author: Emmanuele Bassi +Date: Mon Apr 13 22:30:05 2009 +0100 + + [docs] Show an example of assignment + + Since I decided to rant about assignments in JSON definitions, + I also need to show what an assignment looks like. + + json-glib/json-parser.c | 17 ++++++++++++----- + 1 files changed, 12 insertions(+), 5 deletions(-) + +commit b28d5894060aac1239e23665f42be3946b5450e7 +Author: Emmanuele Bassi +Date: Mon Apr 13 22:29:36 2009 +0100 + + Whitespace fixes + + Add more spaces and remove the ` from the error message. + + json-glib/json-parser.c | 18 ++++++++++++------ + 1 files changed, 12 insertions(+), 6 deletions(-) + +commit a19a51df9e95e36edf949e68581731bd31959081 +Author: Emmanuele Bassi +Date: Mon Apr 13 22:08:21 2009 +0100 + + [tests] Use 'static' when registering enum types + + Bug 1393 - Regression tests fails on OpenBSD + + When registering a GEnumClass we need to set the GEnumValue array + as static const because g_enum_register_static() does not contain + the word "static" just for fun. + + Reported by: Jasper + + tests/test-serialize-full.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit e583a9ec95f3d157091ec1e1a2ac8c493f0e23b2 +Author: Emmanuele Bassi +Date: Mon Apr 13 22:04:10 2009 +0100 + + [build] Use gnome-autogen script + + When setting up the build environment we should use the tools that + GNOME provides instead of calling autoreconf. + + autogen.sh | 21 ++++++++------------- + 1 files changed, 8 insertions(+), 13 deletions(-) + +commit 1d92c73bc05423872581d513f355783d4864edd5 +Author: Emmanuele Bassi +Date: Fri Nov 28 17:24:15 2008 +0000 + + Abstract the loading code into its own function + + The load_from_file() method must set the is_filename/filename fields + of the JsonParserPrivate structure, so that the error handler can + use them to print out the file, as well as the line in case of + error. + + Since load_from_data() needs to unset those two fields, to avoid + printing invalid/stale information, we need to have a generic "load" + function that can be invoked by both load_from_data() and + load_from_file(), and leave the JsonParser object set up to those + two methods. + + Hence, a private json_parser_load() has been added, moving most of + the code out of json_parser_load_from_data(). This function does not + perform type checks and requires that the length of the memory buffer + containing the JSON data stream is already a positive integer. + + json-glib/json-parser.c | 175 +++++++++++++++++++++++++++------------------- + 1 files changed, 103 insertions(+), 72 deletions(-) + +commit 6e7958785096806b3ffa60a4937642d713a051f2 +Author: Emmanuele Bassi +Date: Fri Nov 28 17:19:17 2008 +0000 + + Display the filename inside error messages + + Instead of just relaying the line number both when parsing files + and memory buffers, JsonParser should also print out the file name + in case it is available. + + The error message format should be make-like and emacs-friendly, + that is: + + filename:line_number: error message + + so that editors and development environments can parse the errors + easily. + + This commit adds the filename string, and a boolean flag for checking + whether the filename is set, inside the JsonParser private data + structure. The boolean flag is checked inside the JsonScanner error + handler when populating the GError or when printing the warning + directly on stderr. + + json-glib/json-parser.c | 28 ++++++++++++++++++++++++++-- + 1 files changed, 26 insertions(+), 2 deletions(-) + +commit a5bea353dead0bd69f9664df86050fc829a2dcd5 +Author: Emmanuele Bassi +Date: Mon Nov 3 16:01:03 2008 +0000 + + Bump to 0.7.1 after stable branch + + configure.ac | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +commit cf4c7360c69c0fe7587ec2456cd10839e1d0c1d6 +Author: Emmanuele Bassi +Date: Mon Nov 3 15:51:21 2008 +0000 + + Correctly terminate a string array + + When converting from a JsonArray of strings to a GStrv we need to + add a NULL at the end of the GPtrArray we use to perform the + conversion. + + This two lines patch fixes the issue. + + See bug 1203. + + Patch by: Kouhei Sutou + + Signed-off-by: Emmanuele Bassi + + json-glib/json-gobject.c | 4 +++- + 1 files changed, 3 insertions(+), 1 deletions(-) + +commit 9b94bc7f99063f45a2725861369f36b53ec3d440 +Author: Emmanuele Bassi +Date: Fri Jun 13 11:56:46 2008 +0100 + + Sync up with the current stable release numbers + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 4dd392342003147d55eee98db50e11d344b287d4 +Author: Emmanuele Bassi +Date: Fri Jun 13 11:27:50 2008 +0100 + + Fix a variable shadowing + + json-glib/json-generator.c | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +commit a02a42ef47e61456747e511556097b903f07d68a +Merge: fdbad1c bc5c722 +Author: Emmanuele Bassi +Date: Fri Jun 13 11:23:08 2008 +0100 + + Merge the fix for bug #965 + + Merge branch 'bug-965' + +commit bc5c7225535ef447743ac59ecdba8c237549a108 +Author: Emmanuele Bassi +Date: Fri Jun 13 11:22:35 2008 +0100 + + Conditionally compile the test suite + + If the --disable-glibtest command line switch has been given to the + configure script, the test suite should be disabled and not built. + + This commit disables recursion in the tests/ and json-glib/tests + when the ENABLE_GLIB_TEST Makefile conditional has been set. + + Makefile.am | 6 +++++- + configure.ac | 3 +++ + json-glib/Makefile.am | 2 ++ + 3 files changed, 10 insertions(+), 1 deletions(-) + +commit fdbad1cfd6ad6da9fab631f945ab8d2e85228fdf +Author: Emmanuele Bassi +Date: Fri Jun 13 11:06:15 2008 +0100 + + Escape to special characters in JsonGenerator + + When using json-glib to write a blog system, some deserialized objects + were not been interpreted by javascript because there were line breaks + in generated strings. + + Patch from Lincoln de Sousa. + + Bug #958 - JsonGenerator does not escape special characters + + Signed-off-by: Emmanuele Bassi + + json-glib/json-generator.c | 17 ++++++++++++++++- + 1 files changed, 16 insertions(+), 1 deletions(-) + +commit 81c02ef3db6901655f8a7117e5e2675d37096daf +Author: Emmanuele Bassi +Date: Sun May 18 11:56:04 2008 +0100 + + Remove the include for json-scanner.h + + The json-scanner.h header file is not shipped with JSON-GLib anymore. + + json-glib/json-glib.h | 1 - + 1 files changed, 0 insertions(+), 1 deletions(-) + +commit 9713843a4294b3e35a29125bf2bf1867a6443f27 +Author: Emmanuele Bassi +Date: Tue May 6 11:48:25 2008 +0100 + + Remove debian packaging + + debian/changelog | 5 --- + debian/compat | 1 - + debian/control | 61 --------------------------------------- + debian/copyright | 27 ----------------- + debian/libjson-glib-dev.install | 3 -- + debian/libjson-glib-doc.install | 1 - + debian/libjson-glib-doc.links | 1 - + debian/libjson-glib0.install | 1 - + debian/rules | 12 ------- + 9 files changed, 0 insertions(+), 112 deletions(-) + +commit d67000ab6c5ae6710f48f0517c98600af7cdaa0e +Author: Emmanuele Bassi +Date: Mon May 5 23:17:58 2008 +0100 + + Update changelog for the debian packages + + debian/changelog | 3 +-- + 1 files changed, 1 insertions(+), 2 deletions(-) + +commit 0830873edc5203655f04868184cedb428b6d2d54 +Author: Emmanuele Bassi +Date: Mon May 5 23:16:38 2008 +0100 + + Update git ignore file + + .gitignore | 72 ++++++++++++++++++----------------------------------------- + 1 files changed, 22 insertions(+), 50 deletions(-) + +commit eb1cd29f78ff52e3ec3b063716fb86b5f2c5ae4a +Author: Emmanuele Bassi +Date: Mon May 5 23:13:17 2008 +0100 + + Remove Vala bindings + + Vala bindings are maintained out of tree, directly inside the Vala + repository and official packages, so there's no point in duplicating + the effort here. + + Makefile.am | 6 +-- + configure.ac | 15 ----- + contrib/Makefile.am | 6 -- + contrib/json-glib-1.0.deps | 1 - + contrib/json-glib-1.0.vapi | 139 -------------------------------------------- + contrib/json-object.vala | 46 --------------- + contrib/json-test.vala | 122 -------------------------------------- + 7 files changed, 1 insertions(+), 334 deletions(-) + +commit 094acd1663210e803a8dab496f6de8066ef11f44 +Author: Emmanuele Bassi +Date: Mon May 5 12:44:29 2008 +0100 + + Bump to 0.6.1 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit a725bac9a287883f2d4f32130c7faedb121f754b +Author: Emmanuele Bassi +Date: Mon May 5 12:39:01 2008 +0100 + + Bump to 0.6.0 + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit cd598180b12a212525427a8cddf11bc0ed4f1836 +Author: Emmanuele Bassi +Date: Mon May 5 12:38:50 2008 +0100 + + Update NEWS for 0.6.0 + + NEWS | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +commit 26479ec3c342889a2ee822bdf73f2e5fb6e170d7 +Merge: 8b5b5f6 7ff9134 +Author: Emmanuele Bassi +Date: Mon May 5 12:35:16 2008 +0100 + + Add debian packaging to the master branch + + Merge branch 'debian-packaging' + +commit 8b5b5f6a4fdfb219897d75893889014f44ba4bb0 +Author: Emmanuele Bassi +Date: Mon May 5 12:34:35 2008 +0100 + + Add json-scanner.h to the EXTRA_DIST content + + Fix a distcheck error because json-scanner.h is not packaged inside + the tarball. + + json-glib/Makefile.am | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 705a73eb3dfa2323ec86c1d299b567a15acde7a6 +Author: Emmanuele Bassi +Date: Mon May 5 12:24:22 2008 +0100 + + Style fixes for the configure output + + configure.ac | 10 +++++----- + 1 files changed, 5 insertions(+), 5 deletions(-) + +commit e017d392a281594e350a8d74c142cbe014753d7e +Author: Emmanuele Bassi +Date: Mon May 5 12:24:00 2008 +0100 + + Fix typo in the libtool flags arguments + + The correct syntax for passing the versioning information to + libtool is '-version-info', not '-versio-info'. + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 2051be2f7185733bbc62d10ec096538fec924638 +Author: Emmanuele Bassi +Date: Mon May 5 12:22:59 2008 +0100 + + Add the Bugzilla URL to AC_INIT + + JSON-GLib has a bugzilla product on bugzilla.openedhand.com. By adding + the URL to the configure.ac template, the configure script will print + it when invoked with the --help command line switch. + + configure.ac | 7 ++++++- + 1 files changed, 6 insertions(+), 1 deletions(-) + +commit 4cb8a9078172c5f56ac5d56417b8656a7053df4a +Author: Emmanuele Bassi +Date: Mon May 5 12:21:40 2008 +0100 + + Update autogen.sh + + Make autogen.sh test for the location of the current directory, and + warn if the checkout is not complete. + + If gtk-doc is not installed (for instance, on an embedded minimal + set up), do not fail and just provide a stub gtk-doc.make. + + autogen.sh | 28 +++++++++++++++++++++++++--- + 1 files changed, 25 insertions(+), 3 deletions(-) + +commit 1571fcddd6f2a245f4bb6ad64f82d24fc9c4cec0 +Author: Emmanuele Bassi +Date: Thu May 1 23:00:28 2008 +0100 + + Test the deserialization of arrays of strings + + Arrays of strings are handled automatically by the GObject + deserialization and serialization code. We need to test that + they are correctly parsed and assigned to the instance. + + tests/test-serialize-full.c | 29 +++++++++++++++++++++++++++-- + 1 files changed, 27 insertions(+), 2 deletions(-) + +commit 4c11d0969a44bb70acf6463ef6305999ff443be2 +Author: Emmanuele Bassi +Date: Thu May 1 22:58:55 2008 +0100 + + Use an array to hold the strings in a vector + + Instead of building a GString by concatenating every string inside + an array to deserialize the array into a string vector property, + use a GPtrArray. This is far more efficient (no reallocations are + necessary, as we know the size of the array) and safe (the separator + used to build the string buffer and then split it might exist in + one of the original strings). + + json-glib/json-gobject.c | 15 +++++---------- + 1 files changed, 5 insertions(+), 10 deletions(-) + +commit 7ff91344d21b0c26d3a3a2834dbf09286cb1f904 +Author: Emmanuele Bassi +Date: Thu May 1 16:08:15 2008 +0100 + + Remove control re-generation rule + + We don't have a template control, so there's not need to clean it + up and regenerate it. + + This is why copying existing debian/rules templates is not a good + idea unless you read the Debian packaging rules. + + debian/rules | 3 --- + 1 files changed, 0 insertions(+), 3 deletions(-) + +commit e75321e64f17d69beac0c2e131572fe081680b08 +Author: Emmanuele Bassi +Date: Thu May 1 15:58:31 2008 +0100 + + Add debian packaging files + + The debian packaging files are obviously needed to generate Debian + packages for JSON-GLib. + + debian/changelog | 6 ++++ + debian/compat | 1 + + debian/control | 61 +++++++++++++++++++++++++++++++++++++++ + debian/copyright | 27 +++++++++++++++++ + debian/libjson-glib-dev.install | 3 ++ + debian/libjson-glib-doc.install | 1 + + debian/libjson-glib-doc.links | 1 + + debian/libjson-glib0.install | 1 + + debian/rules | 15 +++++++++ + 9 files changed, 116 insertions(+), 0 deletions(-) + +commit 30f9d47ecf43a17610cd9c0074ff832bfa37593c +Author: Emmanuele Bassi +Date: Thu Apr 24 15:47:11 2008 +0100 + + Test the equality of the serialize string length + + tests/test-serialize-simple.c | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit 07c2b916dadb4b40fbaabf49f0e80941993cc2cf +Author: Emmanuele Bassi +Date: Thu Apr 24 15:02:33 2008 +0100 + + Export only the public symbols + + All the symbols starting with an underscore or with something that + is not "json" are to considered private, and thus are not global + to the shared object. + + json-glib/Makefile.am | 6 +++++- + 1 files changed, 5 insertions(+), 1 deletions(-) + +commit 179899e5da801fda0981a1d446ba420efd4021c1 +Author: Emmanuele Bassi +Date: Thu Apr 24 14:55:51 2008 +0100 + + Update git ignore file + + .gitignore | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +commit b0daf7e3a2529dd9950d77a96a10233d68778f32 +Author: Emmanuele Bassi +Date: Thu Apr 24 14:54:05 2008 +0100 + + Include the newly added json-enum-types.h header + + When including json-glib/json-glib.h we get everything json-glib + expose as a public symbol. + + json-glib/json-glib.h | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit c7a7f429565195fe7aba56a254abf3637cf6fa8a +Author: Emmanuele Bassi +Date: Thu Apr 24 14:52:54 2008 +0100 + + Ignore the enumeration types header and the stamp file + + We don't need gtk-doc to look at those files; the enumeration + types are already documented, and the get_type() function and + macro are implied. + + doc/reference/Makefile.am | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 053bf43c813799c5f59e9241217fe31c560b5885 +Author: Emmanuele Bassi +Date: Thu Apr 24 14:52:07 2008 +0100 + + Install the autogenerated enum types header + + The enumeration GTypes are public and meant to be used by + language bindings and other users of the library. + + json-glib/Makefile.am | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 5977d95866cd9763e97a47d6c9909e6a2fbf0ed1 +Author: Emmanuele Bassi +Date: Thu Apr 24 14:50:34 2008 +0100 + + Autogenerate the GTypes for the enumerations + + json-glib/Makefile.am | 70 +++++++++++++++++++++++++++++++++---------------- + 1 files changed, 47 insertions(+), 23 deletions(-) + +commit 356413e8840a614914b84f40b1d8476ba036ded4 +Author: Emmanuele Bassi +Date: Thu Apr 24 14:49:45 2008 +0100 + + Add enumeration types templates for glib-mkenums + + The templates for glib-mkenums keep the Makefile.am sane and + editable by mere mortals. + + json-glib/json-enum-types.c.in | 31 +++++++++++++++++++++++++++++++ + json-glib/json-enum-types.h.in | 26 ++++++++++++++++++++++++++ + 2 files changed, 57 insertions(+), 0 deletions(-) + +commit 7d57ffc03783defbfeb26e21a14e3df32555c29d +Author: Emmanuele Bassi +Date: Mon Apr 21 10:57:05 2008 +0100 + + Update git ignore file + + .gitignore | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 7d259e03b9a556d155ade87417ab26830c3e7684 +Author: Emmanuele Bassi +Date: Sun Apr 20 22:43:32 2008 +0100 + + Validate the variable name in the assignment test + + Make sure that not only we report that there is an assignment + and that the returned variable is not NULL, but also that the + returned variable name is correct. + + tests/test-parser.c | 16 ++++++++++------ + 1 files changed, 10 insertions(+), 6 deletions(-) + +commit 6899bca0dcf72dac0cad9dcf1a7cd10159d471e5 +Author: Emmanuele Bassi +Date: Sat Apr 19 23:22:29 2008 +0100 + + Add more cases for the nested array parsing unit + + Try more combinations of elements inside an array with a nested + object. + + tests/test-parser.c | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 81addb645ac5fad28619bb660624902abe981377 +Author: Emmanuele Bassi +Date: Sat Apr 19 23:18:54 2008 +0100 + + Add verbose messages + + The unicode escaping unit should have the same verbosity as the + other units. + + tests/test-parser.c | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +commit 22a0db88c14895f98db76bca3cb078fbd3a2b07d +Author: Emmanuele Bassi +Date: Sat Apr 19 23:09:32 2008 +0100 + + Whitespace fixes in the licensing notice + + json-glib/json-scanner.h | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 676592cef006de16d966a1ca0469ccd0556f0e4b +Author: Emmanuele Bassi +Date: Sat Apr 19 23:09:20 2008 +0100 + + Documentation fixes in JsonNode + + json-glib/json-node.c | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +commit bf2249a1855b6bebe0b3528b4d5321a1ebe4b9e7 +Author: Emmanuele Bassi +Date: Sat Apr 19 23:08:20 2008 +0100 + + Do not copy node data if it's not there + + If the source JsonNode does not contain data yet, do not try + and copy it. + + json-glib/json-node.c | 17 +++++++++++++---- + 1 files changed, 13 insertions(+), 4 deletions(-) + +commit c191b2f8a8159457db3333390be10d6c7c0d4336 +Author: Emmanuele Bassi +Date: Sat Apr 19 23:05:03 2008 +0100 + + Ignore json-scanner.h and its types + + When generating the documentation, ignore the JsonScanner header + as it will not be installed. + + Also, we moved JsonTokenType from json-parser.h to json-scanner.h, + where it belongs, so we can remove it from the (private) subsection + of the json-parser section. + + doc/reference/Makefile.am | 7 ++++++- + doc/reference/json-glib-sections.txt | 1 - + 2 files changed, 6 insertions(+), 2 deletions(-) + +commit ffbd9063556a061ba3e7e4223b924ae4766adfbd +Author: Emmanuele Bassi +Date: Sat Apr 19 23:01:26 2008 +0100 + + Do not install the JsonScanner header + + The JsonScanner tokenizer is an internal copy of GScanner: it should + not be used outside JSON-GLib - JsonParser is the public API for + parsing JSON. + + json-glib/Makefile.am | 5 +++-- + 1 files changed, 3 insertions(+), 2 deletions(-) + +commit df2a310a6ed0a3b32415f53076ff4c053b3fd925 +Author: Emmanuele Bassi +Date: Sat Apr 19 22:59:28 2008 +0100 + + Fix the external gtk-doc references + + Gtk-doc can try and resolve the external symbols using the data + currently installed in other locations. + + Since we are using GLib and GObject it would be nice to backlink + to them inside our API reference. + + doc/reference/Makefile.am | 7 ++++--- + 1 files changed, 4 insertions(+), 3 deletions(-) + +commit 6a3d8266efe505b29555db21658c241eba20fe2d +Author: Emmanuele Bassi +Date: Sat Apr 19 22:57:05 2008 +0100 + + Add GLIB_PREFIX variable + + The gtk-doc-fixref script needs to know the location of the + libraries documentation; in order to get it, we can ask the + pkg-config file of the library for its prefix. + + configure.ac | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +commit 4e5ec69cce8ab27f965985ba69ea11224ae39ffd +Author: Emmanuele Bassi +Date: Sat Apr 19 22:28:21 2008 +0100 + + Update the description and dependencies + + README | 7 ++++++- + 1 files changed, 6 insertions(+), 1 deletions(-) + +commit f65a5d5e30a1242a899149b732d1139891876014 +Author: Emmanuele Bassi +Date: Sat Apr 19 18:05:45 2008 +0100 + + Bump to 0.5.1 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit ab0da7c61bde715e99dd16027b198c7760b30922 +Author: Emmanuele Bassi +Date: Sat Apr 19 18:01:57 2008 +0100 + + Update for 0.5.0 release + + NEWS | 14 ++++++++++++++ + 1 files changed, 14 insertions(+), 0 deletions(-) + +commit 203927c71231cf6cec6d88161211702b7105df2d +Author: Emmanuele Bassi +Date: Sat Apr 19 17:20:50 2008 +0100 + + Fix ignore rule + + .gitignore | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 76921ac27534aa0a25b39790ce4d99cf6e19a20c +Author: Emmanuele Bassi +Date: Sat Apr 19 17:19:30 2008 +0100 + + Verify the correct handling of Unicode escaping + + Check that JsonParser correctly handles Unicode characters escaped + using the \uXXXX notation, as per RFC. + + tests/test-parser.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 67 insertions(+), 0 deletions(-) + +commit 379a2fe972c6e7436be9f345fe018bf4fe575354 +Author: Emmanuele Bassi +Date: Sat Apr 19 17:16:28 2008 +0100 + + Add support for parsing \uXXXX into Unicode + + JsonScanner was forked from GScanner in order to support the + JSON-specific Unicode escaping. + + JsonScanner now intercepts the \u escape character and, if it + is followed by a hexadecimal value, it will retrieve the + Unicode character encoded in the following 4 values and insert + the character itself in the buffer. + + This allows full compatibility with JSON. + + json-glib/json-scanner.c | 56 +++++++++++++++++++++++++++++++++++++++++----- + 1 files changed, 50 insertions(+), 6 deletions(-) + +commit 9429ecaa88cdb6705189b6970481911f11339ff6 +Merge: d313aa1 445a3f7 +Author: Emmanuele Bassi +Date: Sat Apr 19 15:33:15 2008 +0100 + + Merge master branch back for testing JsonScanner + + Merge branch 'master' into json-scanner + +commit 445a3f71d046abed4ff99e0ab4cd9cc8e047f5be +Author: Emmanuele Bassi +Date: Sat Apr 12 13:21:21 2008 +0100 + + Allow null nodes to return a value without a warning + + Value nodes might contain 'null' as a valid value, so the fast accessors + should not barf out when encountering a JSON_NODE_NULL instead of the + expected JSON_NODE_VALUE. + + json-glib/json-node.c | 21 +++++++++++++++------ + 1 files changed, 15 insertions(+), 6 deletions(-) + +commit 4e826d9f209a8f68023bae54c91b2bbbb202a54a +Merge: 98b6e5e 2a579b5 +Author: Emmanuele Bassi +Date: Thu Apr 3 16:32:53 2008 +0100 + + Merge branch 'test-framework' + +commit 2a579b51f38df1c009201703279a3e6cb4f57eea +Author: Emmanuele Bassi +Date: Thu Apr 3 16:31:48 2008 +0100 + + Wrap the test report information section inside an info element + + Makefile.decl | 6 ++++-- + 1 files changed, 4 insertions(+), 2 deletions(-) + +commit 8e2e539d02b84e0fe452174f3dfab06f35a4e6a1 +Author: Emmanuele Bassi +Date: Sun Mar 9 20:52:29 2008 +0000 + + Add value testing to the JsonNode unit + + Test the GValue API for storing fundamental types into a JsonNode. + + json-glib/tests/node-test.c | 29 +++++++++++++++++++++++++++++ + 1 files changed, 29 insertions(+), 0 deletions(-) + +commit 646b90e79d3424a332064a6a1e9fc62d1ce99386 +Author: Emmanuele Bassi +Date: Wed Mar 5 17:43:06 2008 +0000 + + Add package and version information to the test-report XML + + Makefile.decl | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit b8943381b0df44d9e6e21ae8321df2376c8458a8 +Author: Emmanuele Bassi +Date: Wed Mar 5 16:03:24 2008 +0000 + + Add JsonObject test unit + + Use a similar test unit as the JsonArray one, testing creation, empty + objects, addition and removal of members. + + .gitignore | 2 + + json-glib/tests/Makefile.am | 17 +++++++---- + json-glib/tests/object-test.c | 62 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 75 insertions(+), 6 deletions(-) + +commit 7eab612d9b200aef197fa1dffa4f89b936c76b1f +Author: Emmanuele Bassi +Date: Wed Mar 5 15:46:41 2008 +0000 + + Add element removal unit to the Array test + + Remove the json_array_remove_element() call from the add-element test unit + and set up a separate test case for the element removal. This keeps the + test cases clean. + + json-glib/tests/array-test.c | 12 ++++++++++++ + 1 files changed, 12 insertions(+), 0 deletions(-) + +commit 6aa1ddb891594e0f567bc3ebeb3bf5d3ab8e7e0c +Author: Emmanuele Bassi +Date: Tue Mar 4 13:07:08 2008 +0000 + + Clean the test-report XML output, if found + + Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 6eee8282d327614380854d572b2680f40b2faaf5 +Author: Emmanuele Bassi +Date: Tue Mar 4 12:37:52 2008 +0000 + + Update gitignore file + + .gitignore | 18 ++++++++---------- + 1 files changed, 8 insertions(+), 10 deletions(-) + +commit d5cb48681c68c3d53d8ec331ef37fb60820a9421 +Author: Emmanuele Bassi +Date: Tue Mar 4 12:30:48 2008 +0000 + + Build test-serialize-full + + Complete the porting of the old test suite into the new, GTest based one. + + The new test suite is automatically run with make test; make test-report + will generate an XML report of the test. + + The API coverage for the data types is part of the json-glib/tests + directory and will be expanded later. + + tests/Makefile.am | 8 ++++- + tests/test-serialize-full.c | 56 ++++++++++++++++++++++++++---------------- + 2 files changed, 41 insertions(+), 23 deletions(-) + +commit 1f6307ea2d7dc6ebe279d08b9f95f3074fbdd462 +Author: Emmanuele Bassi +Date: Tue Mar 4 12:18:19 2008 +0000 + + Rename test-08 into test-serialize-full + + The test-08 case was the last numbered test unit. + + tests/test-08.c | 337 ------------------------------------------- + tests/test-serialize-full.c | 337 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 337 insertions(+), 337 deletions(-) + +commit cdb194e8d64343e8bcb00e046c5fbeefd677b02a +Author: Emmanuele Bassi +Date: Tue Mar 4 12:17:29 2008 +0000 + + Rename test-07 into test-serialize-complex + + The new test-serialize-complex adds a test unit for the JsonSerializable + interface; the TestObject implements the serialization interface to + create a JSON data type from a boxed GType. + + tests/Makefile.am | 4 + + tests/test-07.c | 252 -------------------------------------- + tests/test-serialize-complex.c | 261 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 265 insertions(+), 252 deletions(-) + +commit 4c766a029d8f6a2d06b749527020fd93fc4a4f99 +Author: Emmanuele Bassi +Date: Tue Mar 4 12:12:37 2008 +0000 + + Fix compilation of test-serialize-simple and add it to the test suite + + The new test-serialize-simple tests the GObject integration for + serializing simple GObjects into JSON. + + tests/Makefile.am | 4 ++++ + tests/test-serialize-simple.c | 5 +++-- + 2 files changed, 7 insertions(+), 2 deletions(-) + +commit 325185c5955f8942ddf8d55a9e3b5d6a8c2723d1 +Author: Emmanuele Bassi +Date: Tue Mar 4 12:10:23 2008 +0000 + + Rename test-06 to test-serialize-simple + + tests/test-06.c | 162 ----------------------------------------- + tests/test-serialize-simple.c | 162 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 162 insertions(+), 162 deletions(-) + +commit cbea4698c1b7284d8004fff659a7354fb3af29ee +Author: Emmanuele Bassi +Date: Tue Mar 4 12:09:44 2008 +0000 + + Move test-06 to GTest API + + First pass into replacing the last three numbered tests into named + tests. + + tests/test-06.c | 28 ++++++++++++++++++---------- + 1 files changed, 18 insertions(+), 10 deletions(-) + +commit 5cc1d3d6061830de403ccbc10c2414f29cd1cf82 +Author: Emmanuele Bassi +Date: Tue Mar 4 10:52:51 2008 +0000 + + Coalesce JsonGenerator test cases into a single test unit + + Like we did for JsonParser, JsonGenerator has now a test unit checking the + output of a DOM built and dumped into a buffer. + + For the arrays is quite easy to verify that the output is correct; + unfortunately, JsonObject does not guarantee any ordering on the members, + with it being an associative array. Hence, for the object test case we + just compare the length of the output. + + tests/Makefile.am | 4 + + tests/test-04.c | 238 ---------------------------- + tests/test-05.c | 227 --------------------------- + tests/test-generator.c | 399 ++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 403 insertions(+), 465 deletions(-) + +commit e100c69a4fca046cbeb6db3fc8ba49160796d87c +Author: Emmanuele Bassi +Date: Tue Mar 4 10:44:56 2008 +0000 + + Append to EXTRA_DIST, since it was defined elsewhere + + contrib/Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 98b6e5e2f151c4ee800754675bf01e585fee850b +Author: Emmanuele Bassi +Date: Tue Mar 4 07:05:01 2008 +0000 + + Update the README + + README | 36 ++++++++++++++++++++++++++++++++++++ + 1 files changed, 36 insertions(+), 0 deletions(-) + +commit 1a078c783a5a03b3367ce59eb25248dd6e23b5c6 +Author: Emmanuele Bassi +Date: Tue Mar 4 07:05:01 2008 +0000 + + Update the README + + README | 36 ++++++++++++++++++++++++++++++++++++ + 1 files changed, 36 insertions(+), 0 deletions(-) + +commit 44891ea1040ceae95a994b646174696de7779ea1 +Author: Emmanuele Bassi +Date: Tue Mar 4 06:44:59 2008 +0000 + + Update gitignore file + + .gitignore | 11 +++++------ + 1 files changed, 5 insertions(+), 6 deletions(-) + +commit 3ea189acf52ea39653c6505c136f1f45a660a24c +Author: Emmanuele Bassi +Date: Tue Mar 4 06:34:11 2008 +0000 + + Add validation to the simple object test values + + Like for the simple arrays test case, add validation of the values types + for the members of the simple objects test case. + + tests/test-parser.c | 31 ++++++++++++++++++++++++------- + 1 files changed, 24 insertions(+), 7 deletions(-) + +commit 40b3e9330f482e002aea90b0c1a4b9db2adffff0 +Author: Emmanuele Bassi +Date: Tue Mar 4 06:27:24 2008 +0000 + + Add validation of the simple array test values + + Instead of just checking that the array contains something, the simple-array + test case should validate the contents of the array. + + The test now uses a struct defining: + - the test array + - the size of the array + - an element to check + - the type of the node for the element + - the type of the value for the node + + All the fields are checked for a match. This makes the simple arrays test + case more reliable. + + tests/test-parser.c | 40 +++++++++++++++++++++++++++++++--------- + 1 files changed, 31 insertions(+), 9 deletions(-) + +commit 7e6dab14302b94979672acf81eec8710ea95e288 +Author: Emmanuele Bassi +Date: Sun Mar 2 17:44:27 2008 +0000 + + Add array-test to the JSON-GLib types unit tests + + This simple unit will test the JsonArray API, as part of the coverage + test for the JSON-GLib types. + + json-glib/tests/Makefile.am | 10 ++++++-- + json-glib/tests/array-test.c | 50 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 57 insertions(+), 3 deletions(-) + +commit 441ee88a6e024fc5ab2cf8355adad1fecc276088 +Author: Emmanuele Bassi +Date: Sun Mar 2 15:09:05 2008 +0000 + + Coalesce test-03 into test-parser + + The third test under the tests/ directory was the last JsonParser test, + dealing with object parsing. Now, test-parser is complete and contains + all the JsonParser test cases. It still needs further testing to + verify the DOM created by the parser object. + + tests/test-02.c | 225 --------------------------------------------------- + tests/test-03.c | 214 ------------------------------------------------ + tests/test-parser.c | 167 ++++++++++++++++++++++++++++++++++++-- + 3 files changed, 161 insertions(+), 445 deletions(-) + +commit 71db86d84a5544d603b03721891e0ad14f92178d +Author: Emmanuele Bassi +Date: Sun Mar 2 14:58:58 2008 +0000 + + Update the tests build to be a testing unit + + Instead of having stand alone tests, use the GTest framework and start + coalescing multiple test cases into one. + + The tests directory will be used for JsonParser, JsonGenerator and + the JSON-GObject integration API, by reusing the previous test cases + and remolding them into a more interesting test framework. + + tests/Makefile.am | 39 +++++---------------------------------- + 1 files changed, 5 insertions(+), 34 deletions(-) + +commit 1e6aa16a2a6432cf96d0a86d4650c13e63c67e43 +Author: Emmanuele Bassi +Date: Sun Mar 2 14:56:56 2008 +0000 + + Remove old, empty string test + + tests/test-01.c | 57 ------------------------------------------------------- + 1 files changed, 0 insertions(+), 57 deletions(-) + +commit 989c807fd61cc65c1e4058c5d0794ae2fe438ba1 +Author: Emmanuele Bassi +Date: Sun Mar 2 14:56:28 2008 +0000 + + Rename JsonParser test, and coaelesce basic parser and array tests + + Instead of using a single test for empty strings and another test for + arrays, merge the two into a single test unit, using the GTest API. + + The JsonObject parsing test will also be merged later. + + tests/test-parser.c | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 262 insertions(+), 0 deletions(-) + +commit f1e1c0e7796903abb725e8216fc54f53573ecdc9 +Author: Emmanuele Bassi +Date: Sun Mar 2 10:51:09 2008 +0000 + + Port the empty string test to the GLib testing framework + + The old test-01 program tested the JsonParser with an empty string; instead + of relying on the exit code and error messages on screen, it shoul use the + new GTest API. + + This is the first test of the old test suite to be ported to the new test + framework API. + + tests/test-01.c | 44 +++++++++++++++++++++++++++++++++++--------- + 1 files changed, 35 insertions(+), 9 deletions(-) + +commit cb5b3d5fa6244b0d20258203bd9df7e3148af57b +Author: Emmanuele Bassi +Date: Sun Mar 2 10:50:17 2008 +0000 + + Add a JsonNode copy test unit + + The test unit copies a NULL JsonNode and checks that the copy and the + original nodes are equivalent. + + json-glib/tests/node-test.c | 15 +++++++++++++++ + 1 files changed, 15 insertions(+), 0 deletions(-) + +commit 40c757835e329fe01f8f22aba74d2e47f3a5e55c +Author: Emmanuele Bassi +Date: Sun Mar 2 10:49:52 2008 +0000 + + Update Makefile templates to include the testing framework + + contrib/Makefile.am | 2 ++ + doc/Makefile.am | 2 ++ + doc/reference/Makefile.am | 1 + + tests/Makefile.am | 38 ++++++++++++++++++++++++++++++++------ + 4 files changed, 37 insertions(+), 6 deletions(-) + +commit 75939c2b43430583150b18f0a8e82e3d60a4f4f1 +Author: Emmanuele Bassi +Date: Sat Mar 1 19:35:47 2008 +0000 + + Update gitignore file + + .gitignore | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +commit e4be0d896359cc1d0ba99dd81a66855389ebd5df +Author: Emmanuele Bassi +Date: Sat Mar 1 19:30:41 2008 +0000 + + Add initial test suite support using GLib's new testing framework + + GLib 2.15 added a new test unit framework to the GLib API. It allows + integrating unit testing into GLib and GObject based libraries and + applications. + + It requires a specially crafter Makefile holding a set of declarations, + which must be included into the project own Makefile templates; then + it is possible to drop tests inside a subdirectory, which will be built + after the library or application, and executed upon "make check". + + At the moment, there is a simple test for the JsonNode API, with a + single unit for the "null" node type. + + Makefile.am | 4 ++- + Makefile.decl | 57 +++++++++++++++++++++++++++++++++++++++++++ + configure.ac | 3 +- + json-glib/Makefile.am | 9 ++++++- + json-glib/tests/Makefile.am | 15 +++++++++++ + json-glib/tests/node-test.c | 27 ++++++++++++++++++++ + 6 files changed, 112 insertions(+), 3 deletions(-) + +commit d313aa1e9e472834252ec173c0409eba11ebcff9 +Author: Emmanuele Bassi +Date: Fri Feb 29 16:40:33 2008 +0000 + + Update gitignore + + .gitignore | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 8a2bb3c4400b9df6cd864eb29920a7767e8170c7 +Author: Emmanuele Bassi +Date: Fri Feb 29 16:37:18 2008 +0000 + + Sanitize JsonScanner code + + Use a closure instead of (*eugh*) an array of gpointers and casting + to void*. + + Also, use the Slice allocator for the ScannerKey structs. + + json-glib/json-scanner.c | 42 ++++++++++++++++++++---------------------- + 1 files changed, 20 insertions(+), 22 deletions(-) + +commit 42f7800c5b1c30ff187fc824a9bb39565033488e +Author: Emmanuele Bassi +Date: Fri Feb 29 16:01:48 2008 +0000 + + Update JsonParser to fix compilation + + JsonParser now uses JsonScanner, the internal copy of GScanner. + + json-glib/json-parser.c | 33 +++++++++++++++++---------------- + 1 files changed, 17 insertions(+), 16 deletions(-) + +commit 314f24fd0c8c6b024c93b1d8d275f9049ef22951 +Author: Emmanuele Bassi +Date: Fri Feb 29 16:00:55 2008 +0000 + + Completely internalize the JsonScanner + + Rename all the API and the data types to the Json namespace, so we can + use the internal copy instead of GScanner. + + json-glib/json-scanner.c | 637 +++++++++++++++++++++++----------------------- + json-glib/json-scanner.h | 6 +- + 2 files changed, 327 insertions(+), 316 deletions(-) + +commit 65708000ab02d0a4080b662bb409ac4b79101488 +Author: Emmanuele Bassi +Date: Fri Feb 29 16:00:30 2008 +0000 + + Add JsonScanner to the build + + json-glib/Makefile.am | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 8e27ac27a9b22361296564d740301c2c49b387f8 +Author: Emmanuele Bassi +Date: Fri Feb 29 15:59:57 2008 +0000 + + Bump up autotools requirements + + Do not require an ancient version of automake, and settle for one slightly + less ancient. + + configure.ac | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit a475b0896d5760c893a099c9411f9790e5c492ba +Author: Emmanuele Bassi +Date: Fri Feb 29 12:24:14 2008 +0000 + + Update the JsonScanner default configuration + + json-glib/json-scanner.c | 84 +++++++++++++++++++-------------------------- + 1 files changed, 36 insertions(+), 48 deletions(-) + +commit 10ac1734b326700257ef318d9a2000c538b5cd38 +Author: Emmanuele Bassi +Date: Fri Feb 29 12:22:02 2008 +0000 + + Use JsonScanner API into JsonParser + + Rename the GScanner function calls into JsonScanner, to see what we + actually need to have public. + + Also, remove the configuration: JsonScanner will provide us with one. + + json-glib/json-parser.c | 127 ++++++++++++++++------------------------------ + 1 files changed, 44 insertions(+), 83 deletions(-) + +commit 4a4095c360fc29f766e8baba3529a78b6d736b19 +Author: Emmanuele Bassi +Date: Fri Feb 29 12:21:20 2008 +0000 + + Add json-scanner.h to the exported headers + + json-glib/json-glib.h | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 469a22e4f1fac6b4fd8110823322b7fbfa9b6552 +Author: Emmanuele Bassi +Date: Fri Feb 29 12:13:54 2008 +0000 + + Include JsonScanner into JsonParser + + json-glib/json-parser.c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit 8537549a5a65f3111a4436b69499664c167b1534 +Author: Emmanuele Bassi +Date: Fri Feb 29 12:10:16 2008 +0000 + + Move JsonTokenType into JsonScanner + + The GTokenType extension enumeration belongs with the tokenizer. + + json-glib/json-parser.h | 15 ++------------- + 1 files changed, 2 insertions(+), 13 deletions(-) + +commit 1ddd45d36a3a25aa86a95c60e0c29e83687971bd +Author: Emmanuele Bassi +Date: Fri Feb 29 12:06:19 2008 +0000 + + Copy GScanner into JSON-GLib as JsonScanner + + Instead of writing our tokenizer we can fork GScanner and make a + specialized version for JSON (as per RFC), luckily the licenses + are compatible (LGPLv2.1 with "any later" clause). + + GScanner does not support Unicode "\uNNNN" escaping and we need to + ensure UTF-8 strings as well. + + The API will mostly be the same, but the generic bits not used by + JsonParser will be hidden: this is, after all, a specialized + tokenizer. + + json-glib/json-scanner.c | 1809 ++++++++++++++++++++++++++++++++++++++++++++++ + json-glib/json-scanner.h | 167 +++++ + 2 files changed, 1976 insertions(+), 0 deletions(-) + +commit 3a9ec8f1ca9bf525875c3fbfaf1ab2f3c708bf36 +Author: Emmanuele Bassi +Date: Tue Jan 29 19:13:15 2008 +0000 + + Update the JSON-GLib Vala bindings + + Add a dependencies file, so that valac can simply use the json-glib-1.0 + package and correcly chain up all the dependencies needed (at the moment, + only glib-2.0). + + Update the vapi file to match with the GLib bindings with regards to the + out length parameters and some weak pointers. The only way to properly + solve the weak assignments issue would be to make JsonNode, JsonObject + and JsonArray proper GObjects, or at least add reference counting to + JsonNode. Not going to happend in 0.6, but it's worth adding it to the + 1.0 roadmap. + + contrib/json-glib-1.0.deps | 1 + + contrib/json-glib-1.0.vapi | 14 +++++----- + contrib/json-test.vala | 62 +++++++++++++++++++++---------------------- + 3 files changed, 38 insertions(+), 39 deletions(-) + +commit e8a59f086f43e5abd5414033ea8c9a886eb112ed +Author: Emmanuele Bassi +Date: Tue Jan 29 19:12:35 2008 +0000 + + Remove % from the pkg-config file rule + + Rules starting with '%' are a make-isms, and automake 1.10 complains a + lot about them. + + Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit b6963328f3d8d8e85ca0b21f9bdeca77b5611019 +Author: Emmanuele Bassi +Date: Tue Jan 29 18:19:16 2008 +0000 + + Remove json_parser_peek_root from the gtk-doc sections + + The json_parser_peek_root() function has been removed, and so we do not + its symbol in the json-glib-sections.txt file anymore. + + doc/reference/json-glib-sections.txt | 1 - + 1 files changed, 0 insertions(+), 1 deletions(-) + +commit 48079c2f580171235eecc298b0193022a67e0b79 +Author: Emmanuele Bassi +Date: Tue Jan 29 18:18:01 2008 +0000 + + Update after the JsonParser::get_root change + + Do not free the root node returned by the get_root() method in the + JSON-GObject API and in the JsonParser tests. + + json-glib/json-gobject.c | 4 +--- + tests/test-01.c | 2 +- + tests/test-02.c | 2 -- + tests/test-03.c | 2 -- + 4 files changed, 2 insertions(+), 8 deletions(-) + +commit a86a300fdc256ad4270881cf9b7b97367cd87c87 +Author: Emmanuele Bassi +Date: Tue Jan 29 18:14:49 2008 +0000 + + Revert JsonParser::get_root semantics + + The get_root() method should not return a copy of the parsed node: it is + up to the developer copying it, if it needs to be kept around across multiple + parsing runs. + + This commit reverts the 0b6b09c0 commit, by removing the peek_root() method + and restoring the previous get_root() method behaviour. + + json-glib/json-parser.c | 26 ++------------------------ + json-glib/json-parser.h | 1 - + 2 files changed, 2 insertions(+), 25 deletions(-) + +commit 9a167068edcde51b50a15bab4ee8623994eb9c04 +Author: Emmanuele Bassi +Date: Mon Jan 28 11:02:30 2008 +0000 + + Use the normalized member name in has_member + + The json_object_has_member() used the passed in member name, instead of + the correctly normalized one. + + json-glib/json-object.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit f2f08d282ac6bc69ba9029d065979b1332abd5a5 +Author: Emmanuele Bassi +Date: Sun Jan 27 21:26:51 2008 +0000 + + Extract the parser state clearing into its own function + + The JsonParser object clears its state upon starting the parsing sequence + or during the instance destruction process. It's worth moving the free + and unref calls into their own function to be called by the load_from_data() + and dispose methods. As further optimisation, inlining them should be + worth as well. + + json-glib/json-parser.c | 51 +++++++++++++++++++++++++++------------------- + 1 files changed, 30 insertions(+), 21 deletions(-) + +commit 629d4a54913c578d7b612f10d9b04addc8e0c7fb +Author: Emmanuele Bassi +Date: Tue Dec 25 21:44:03 2007 +0000 + + Wrap config.h include with conditionals + + Including the autotools generated config.h should always be conditional + on the HAVE_CONFIG_H definitions. + + json-glib/json-array.c | 2 ++ + json-glib/json-generator.c | 2 ++ + json-glib/json-gobject.c | 2 ++ + json-glib/json-node.c | 3 +++ + json-glib/json-object.c | 2 ++ + 5 files changed, 11 insertions(+), 0 deletions(-) + +commit e172e9ccec88999e7d8433df08d8d92568537cf7 +Author: Emmanuele Bassi +Date: Tue Dec 25 21:41:44 2007 +0000 + + Kill off a few indirections + + Use an intermediary pointer to avoid a lot of pointer dereferences + + json-glib/json-parser.c | 25 ++++++++++++++----------- + 1 files changed, 14 insertions(+), 11 deletions(-) + +commit 61a6a144a2182946be4fb975d05e1c26d814c323 +Author: Emmanuele Bassi +Date: Tue Dec 25 11:55:36 2007 +0000 + + Some miscellaneous fixes to the Vala bindings + + Expose the properties as members and remove the accessor methods in case + it's obvious that they are just function proxies. + + Also, start binding the basic GObject API, even though no serializable + support is ready, yet. + + contrib/json-glib-1.0.vapi | 21 ++++++++++-- + contrib/json-object.vala | 10 ++++- + contrib/json-test.vala | 81 ++++++++++++++++++++++++++++++++------------ + 3 files changed, 85 insertions(+), 27 deletions(-) + +commit 8ec0c93715f6f43e4829a6f3ac534f9ec05e0363 +Author: Emmanuele Bassi +Date: Tue Dec 25 11:53:04 2007 +0000 + + Provide a default implementation of JsonSerializable + + The JsonSerializable interface can provide a default implementation, using + the powers of GTypeInterface. This means that classes implementing the + interface can opt to implement both, either or none of the JsonSerializable + methods, and still be able to retain some basic functionality for the methods + they decide not to implement. + + json-glib/json-gobject.c | 208 ++++++++++++++++++++++++++------------------- + 1 files changed, 120 insertions(+), 88 deletions(-) + +commit 5bb6dd7b77dcd0d1996f484990223287d2144ff3 +Author: Emmanuele Bassi +Date: Tue Dec 25 11:51:20 2007 +0000 + + Plug a leak when loading a new buffer with the same parser + + When the same JsonParser loads a different buffer it needs to clear out the + current state; this means clearing the variable_name string it saves when + the passed JSON stream is a JavaScript assignment. + + json-glib/json-parser.c | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit a90a842f269314c4423de9d84d03391a8efbd7b5 +Author: Emmanuele Bassi +Date: Tue Dec 25 11:49:20 2007 +0000 + + Use gssize for the buffer length parameter + + Since we allow a negative value, meaning "take the whole string", for the + length parameter, when need a signed size_t. This also fixes the bug where + we implicitly always computed the buffer length and discarded the passed + length parameter. + + json-glib/json-parser.c | 10 +++++----- + json-glib/json-parser.h | 2 +- + 2 files changed, 6 insertions(+), 6 deletions(-) + +commit 7229b9bf0bd15c6c1e5b695f8e291218a041ab45 +Author: Emmanuele Bassi +Date: Tue Dec 25 11:47:10 2007 +0000 + + Initialise to zero when creating/copying a JsonNode + + Avoid feeding garbage to the callers by using g_slice_new0(). + + json-glib/json-node.c | 9 +++++---- + 1 files changed, 5 insertions(+), 4 deletions(-) + +commit ba7282dd23e2980203208cb73942535bfefa5906 +Author: Emmanuele Bassi +Date: Tue Dec 25 11:45:56 2007 +0000 + + Do not leak the intermediate strings when generating JSON + + Found this very dumb leak while using Valgrind. + + json-glib/json-generator.c | 6 ++++++ + 1 files changed, 6 insertions(+), 0 deletions(-) + +commit 193aca9aabbfc3db8e1faca0a65b19ac7ba96341 +Author: Emmanuele Bassi +Date: Tue Dec 25 11:44:18 2007 +0000 + + Do not free the pointer to the boxed type we get when serialising + + The pointer returned by g_value_get_boxed() is just a pointer to the internal + copy inside GValue, and it's not ours to free, as Valgrind gently pointed + out. + + tests/test-07.c | 2 -- + 1 files changed, 0 insertions(+), 2 deletions(-) + +commit a6fae2d61b1cfefa742574761edc19c8d6f702f5 +Author: Emmanuele Bassi +Date: Thu Nov 22 16:00:35 2007 +0000 + + Add a simple Vala serialization example + + We don't provide the Serializable interface at the moment, because it + is too much C-oriented. This example shows how to serialize a class in + Vala to a JSON string. + + contrib/json-object.vala | 40 ++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 40 insertions(+), 0 deletions(-) + +commit ea6cc8756f70d488eae128e4a01f63ce2766cc3a +Author: Emmanuele Bassi +Date: Thu Nov 22 15:59:25 2007 +0000 + + Omit the lenght argument now that we provide a default + + contrib/json-test.vala | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 9492f3439dd491bb5afadfe6b2bd49a72ec9bb1e +Author: Emmanuele Bassi +Date: Thu Nov 22 15:58:23 2007 +0000 + + Fix the Vala bindings + + Miscellaneous fixes for the Vala bindings of JSON-GLib: + * add missing return values for a couple of methods + * decorate the Object and Array constructors with their C names + * correctly transfer the ownership when adding members/elements + * add the non-null value marker for strings + * provide default values for the length argument in from_data()/to_data() + + contrib/json-glib-1.0.vapi | 20 +++++++++++--------- + 1 files changed, 11 insertions(+), 9 deletions(-) + +commit a255b4f5eb8769c444a9ac3a4cc7404fccd44ee3 +Author: Emmanuele Bassi +Date: Thu Nov 22 00:01:37 2007 +0000 + + Recurse into the contrib directory only if Vala is available + + If Vala is not available then there's no point in getting into the + contrib directory. This might change in the future, so we already + have the HAVE_VALA conditional for the Makefiles. + + Makefile.am | 8 +++++++- + configure.ac | 3 +++ + 2 files changed, 10 insertions(+), 1 deletions(-) + +commit aa8911ca4d63bfb3dd34997f9a745d70d12673af +Author: Emmanuele Bassi +Date: Wed Nov 21 20:44:08 2007 +0000 + + Update the test case for the Vala bindings + + contrib/json-test.vala | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 7083e9e6733611b5313f0708c4b93b9726ef26d0 +Author: Emmanuele Bassi +Date: Wed Nov 21 20:43:35 2007 +0000 + + Wrap the newly added API in the Vala bindings + + Add the Json.Object.dup_member() and Json.Array.dup_element() functions. + Also, export the JSON_NODE_TYPE() macro as Json.Node.type() method. + + contrib/json-glib-1.0.vapi | 8 ++++++-- + 1 files changed, 6 insertions(+), 2 deletions(-) + +commit 9d4588e97a88960a4dd6a28debb3cb0f8d75f098 +Author: Emmanuele Bassi +Date: Wed Nov 21 20:42:07 2007 +0000 + + Add symbols of the newly committed API + + doc/reference/json-glib-sections.txt | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit cf2eceeb69ccbda4f2ff583ee7869c7fd3cda603 +Author: Emmanuele Bassi +Date: Wed Nov 21 20:41:42 2007 +0000 + + Add API to retrieve copies of the nodes inside objects and arrays + + Getting copies of the nodes might work better for high level languages + binding the JSON-GLib API, because they can manage the lifetime of the + returned values using their own rules. + + json-glib/json-array.c | 29 +++++++++++++++++++++++++++++ + json-glib/json-object.c | 29 +++++++++++++++++++++++++++++ + json-glib/json-types.h | 4 ++++ + 3 files changed, 62 insertions(+), 0 deletions(-) + +commit 5a4a8761af0562fbee8e1a56ce1771a20c1ad8e3 +Author: Emmanuele Bassi +Date: Wed Nov 21 20:07:12 2007 +0000 + + Fix leaks in the test suite + + Dispose the root node when using the JsonParser and JsonGenerator + objects after the change in sematics for their accessors. + + tests/test-01.c | 2 +- + tests/test-02.c | 2 ++ + tests/test-03.c | 2 ++ + tests/test-04.c | 8 ++++++++ + tests/test-05.c | 6 ++++++ + 5 files changed, 19 insertions(+), 1 deletions(-) + +commit ac699263493e09eeb203f9bf88c0a8296f9e4a8f +Author: Emmanuele Bassi +Date: Wed Nov 21 20:06:06 2007 +0000 + + Free the root node obtained using json_parser_get_root() + + Update json_construct_gobject() to the change of behaviour in the + root node getter function of JsonParser. + + json-glib/json-gobject.c | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 0b6b09c0fe2fdd21b18593192e7213d1b0098f12 +Author: Emmanuele Bassi +Date: Wed Nov 21 20:05:07 2007 +0000 + + Change json_parser_get_root() semantics + + The json_parser_get_root() returns a pointer to the root node. This does + not conform to the API naming convention inherited from GLib, where + functions returning an internal pointer are called "peek" and function + returning a copy are called "get". + + Thus, json_parser_get_root() will now return a copy of the root node and + it is left to the developer to free the returned JsonNode. + + A function returning the pointer has also been added, and it's called + json_parser_peek_root(). + + json-glib/json-parser.c | 26 ++++++++++++++++++++++++-- + json-glib/json-parser.h | 2 ++ + 2 files changed, 26 insertions(+), 2 deletions(-) + +commit 5f110dc5ad338497c95418b9ad1b267c95fb8ded +Author: Emmanuele Bassi +Date: Wed Nov 21 18:39:04 2007 +0000 + + Add JSON-GLib Vala bindings + + Add bindings for the basic JSON-GLib API. GObject API will arrive later. + + Makefile.am | 2 +- + configure.ac | 12 +++++ + contrib/Makefile.am | 4 ++ + contrib/json-glib-1.0.vapi | 118 ++++++++++++++++++++++++++++++++++++++++++++ + contrib/json-test.vala | 87 ++++++++++++++++++++++++++++++++ + 5 files changed, 222 insertions(+), 1 deletions(-) + +commit 557a60b4e0f1fd6fc0fa79efc60f9bfd777e0670 +Author: Emmanuele Bassi +Date: Wed Nov 21 14:52:15 2007 +0000 + + Use a unicode character for the indent-char property of JsonGenerator + + The indent character should be any Unicode character available instead + of a generic char. + + json-glib/json-generator.c | 16 ++++++++-------- + tests/test-05.c | 6 +++++- + 2 files changed, 13 insertions(+), 9 deletions(-) + +commit 5fc37f8f2a1594821623419f75ceaa4b95fe07cf +Author: Emmanuele Bassi +Date: Wed Nov 21 01:39:00 2007 +0000 + + Add the indent-char property to JsonGenerator + + The JsonGenerator:indent-char can be used to control the character that + indents the lines when pretty printing. + + json-glib/json-generator.c | 54 ++++++++++++++++++++++++++++++++----------- + 1 files changed, 40 insertions(+), 14 deletions(-) + +commit 58133b51152ca76b8cb5c93b5991f9b1a565e9a9 +Author: Emmanuele Bassi +Date: Wed Nov 21 01:20:20 2007 +0000 + + Use G_TYPE_DOUBLE when dumping a value. + + JsonNode of type value for floating point numbers is G_TYPE_DOUBLE + and not G_TYPE_FLOAT anymore. + + json-glib/json-generator.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit 80006db7bcea2bd8c44a648a64ba3da6a05a6fd1 +Author: Emmanuele Bassi +Date: Tue Nov 20 18:30:42 2007 +0000 + + Test correct deserialization of enum values + + Update the GObject deserialization test suite to check for the correct + deserialization of the enum/flags values. + + tests/test-08.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++------ + 1 files changed, 53 insertions(+), 7 deletions(-) + +commit 1110e098fa879abf7f67f24af2a1b736ce359dd6 +Author: Emmanuele Bassi +Date: Tue Nov 20 18:20:33 2007 +0000 + + Allow deserialization of strings into enums/flags + + If the target value is a G_TYPE_ENUM or a G_TYPE_FLAGS we can effectively + deserialize a string into the corresponding value (or bit mask) using + the introspection API for the GEnum and GFlags types. + + This code is taken from ClutterScript and it was adapted from GtkBuilder. + + json-glib/json-gobject.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 170 insertions(+), 0 deletions(-) + +commit 2674ce68e574cebeca147944cf748b41fbe27507 +Author: Emmanuele Bassi +Date: Tue Nov 13 11:34:05 2007 +0000 + + Bump up to 0.5.0 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit c1547eb3aba6f09b3ce4c23bd66a798e03b35a34 +Author: Emmanuele Bassi +Date: Tue Nov 13 11:29:19 2007 +0000 + + Bump up to 0.4.0 + + NEWS | 9 +++++++++ + configure.ac | 2 +- + 2 files changed, 10 insertions(+), 1 deletions(-) + +commit 03325e108e40d5deba979852ee402d7d6579ac81 +Author: Emmanuele Bassi +Date: Tue Nov 13 11:20:34 2007 +0000 + + Fix variable shadowing for distcheck + + json-glib/json-parser.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit afeec9b40468d289351c78007b5122ff716bb977 +Author: Emmanuele Bassi +Date: Tue Nov 13 11:16:51 2007 +0000 + + Add 0.4 symbols index + + doc/reference/json-glib-docs.sgml | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +commit 32023e03ca9c976e66b9af949d4aea36e1d4d874 +Author: Emmanuele Bassi +Date: Tue Nov 13 10:59:14 2007 +0000 + + Change "responsible" to "asked" in the (de)serialization functions docs + + We provide fallbacks in case a JsonSerializable object does not translate + a property into a JSON object member and vice versa. + + json-glib/json-gobject.c | 15 ++++++++------- + 1 files changed, 8 insertions(+), 7 deletions(-) + +commit c669a28d37795be68f3a51e9353f6f9d8e695a52 +Author: Emmanuele Bassi +Date: Tue Nov 13 10:55:23 2007 +0000 + + Add new symbols to the documentation + + doc/reference/json-glib-sections.txt | 7 ++++++- + 1 files changed, 6 insertions(+), 1 deletions(-) + +commit b83a2bfa96885837ca48bacb6492fd68a2b5b564 +Merge: 1f9b3e5 198ed83 +Author: Emmanuele Bassi +Date: Tue Nov 13 10:51:58 2007 +0000 + + Merge branch 'gobject-deserialize' into work + +commit 198ed839424dc7791d22dede152f4d7abc16a8b2 +Author: Emmanuele Bassi +Date: Tue Nov 13 10:51:31 2007 +0000 + + Use the fallback value-to-node generator even for serializables + + To avoid reimplementing the same code all over again, if the implementation + of the serialize_property virtual function of JsonSerializable returns NULL + we will fall back to the simple value-to-node generator we provide for + non-serializable classes. + + json-glib/json-gobject.c | 17 +++++++++++------ + tests/test-07.c | 13 +------------ + 2 files changed, 12 insertions(+), 18 deletions(-) + +commit 10e937a68bd802a4f5948d900aa2201344cfc138 +Author: Emmanuele Bassi +Date: Tue Nov 13 10:46:02 2007 +0000 + + Add test unit for the GObject deserialization + + .gitignore | 2 + + tests/Makefile.am | 4 +- + tests/test-08.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 296 insertions(+), 1 deletions(-) + +commit 7b93db7ad996b29a6c576db33803029dc94e16fc +Author: Emmanuele Bassi +Date: Tue Nov 13 10:45:23 2007 +0000 + + Fix a couple of dumb typos in the GObject deserialization code + + We need to skip if the CONSTRUCT_ONLY flag is set, not unset. We also need + to copy the value from the JSON node into the target GValue, not the other + way around. + + json-glib/json-gobject.c | 6 ++++-- + 1 files changed, 4 insertions(+), 2 deletions(-) + +commit 9e61004365982017bfe9b76889e2a7d1a0320350 +Author: Emmanuele Bassi +Date: Tue Nov 13 10:11:34 2007 +0000 + + Update git ignore file + + .gitignore | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit 4b496c7d906c81dceafe6a5f442b55b6e161f762 +Author: Emmanuele Bassi +Date: Tue Nov 13 10:11:00 2007 +0000 + + Add node-to-property simple fallback parser for deserialization + + The fallback parser for json_construct_gobject() is invoked if the GType + does not implement the JsonSerializable interface, or if the interface + was not handling the property. + + It will natively convert integers, booleans, strings and double precision + floating point values; it also handles string vectors in form of arrays. + + json-glib/json-gobject.c | 94 +++++++++++++++++++++++++++++++++++++++++---- + 1 files changed, 85 insertions(+), 9 deletions(-) + +commit a7c39c910e08093ee0d0723685005623f26b9eae +Author: Emmanuele Bassi +Date: Tue Nov 13 09:28:57 2007 +0000 + + Add the JsonGenerator:root property + + JsonGenerator now has a :root property, so it can be constructed using + just g_object_new(): + + generator = g_object_new (JSON_TYPE_GENERATOR, + "pretty", TRUE, + "indent", 4, + "root", node, + NULL); + + This means that the root node is copied inside the generator, instead of + just taking ownership (it was quite confusing). The documentation now + clearly states what happens, and that you can safely free the node after + feeding it to the JsonGenerator. + + json-glib/json-generator.c | 30 ++++++++++++++++++++++++++++-- + 1 files changed, 28 insertions(+), 2 deletions(-) + +commit 6132d7325c33e26740b4c955d8ccbe53fd817cd8 +Author: Emmanuele Bassi +Date: Tue Nov 13 09:26:02 2007 +0000 + + Fix member name in json_node_get_value_type() + + JsonNode payload is inside a union. + + json-glib/json-node.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit f051b948714e4928a7d7f903e891639ce4ba82fd +Author: Emmanuele Bassi +Date: Tue Nov 13 09:17:22 2007 +0000 + + Add a GType for JsonNode + + We need a GType for nodes if we want to add JsonNode properties or signal + marshallers to a GObject class. We could use pointers, but we'd loose type + safety, so it's a no-no. + + json-glib/json-node.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ + json-glib/json-types.h | 8 ++++++-- + 2 files changed, 54 insertions(+), 2 deletions(-) + +commit 1f9b3e50282f8aa4a421c83ad596f6186ef82ec9 +Author: Emmanuele Bassi +Date: Sat Nov 10 15:12:20 2007 +0000 + + When parsing a full assignment, also swallow any trailing semi-colon + + There's no end to web developers insanity when they can twist and turn a + perfectly simple and elegant notation into something that allows lazyness + and breakage. + + json-glib/json-parser.c | 12 +++++++++++- + tests/test-02.c | 1 + + 2 files changed, 12 insertions(+), 1 deletions(-) + +commit 261d05a4cdcc1f64824615cdc81b4b467d0a5f57 +Author: Emmanuele Bassi +Date: Sat Nov 10 02:15:44 2007 +0000 + + Add support for parsing assignments + + Some JSON web APIs return a full JavaScript assignment instead of just + the data structure (and I'm looking at you, Tumblr). This is done in clear + violation of the grammar published in the RFC 4627, and it's evidently + done because most web developers are too lazy for doing a + + var foo = eval('(' + text ')'); + + and want everything defined for them. JsonParser will blissfully ignore + the left-hand part of the assignment but, in the interest of the developer + who cares, will record: 1. that the parsed statement was in fact an + assignment and 2. the name of the variable used. + + The function json_parser_has_assignment() can be used to query both the + presence of an assignment and the variable name at the same time. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-parser.c | 80 +++++++++++++++++++++++++++++++++- + json-glib/json-parser.h | 5 ++ + tests/test-02.c | 5 ++ + 4 files changed, 89 insertions(+), 2 deletions(-) + +commit 45c697263364a975fd6a54373f999f2932436b6f +Author: Emmanuele Bassi +Date: Sat Nov 10 01:26:46 2007 +0000 + + Initial implementation of GObject deserialization function + + The json_construct_gobject() function takes a GType and a JSON data stream + and constructs a new instance for the given type. If the type is a + JsonSerializable, it will use the JsonSerializable interface for parsing + the JsonNodes of each object member. + + This is the initial implementation of the function: the JsonNode-to-GValue + fallback parser is just a stub. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-gobject.c | 182 +++++++++++++++++++++++++++++++--- + json-glib/json-gobject.h | 8 +- + 3 files changed, 177 insertions(+), 14 deletions(-) + +commit 4eade1089355ebcf0614d0cfbc9c441513b7d97a +Author: Emmanuele Bassi +Date: Mon Oct 29 22:23:36 2007 +0000 + + Fix objects test suite to match the arrays one + + tests/test-03.c | 8 ++++---- + 1 files changed, 4 insertions(+), 4 deletions(-) + +commit 3948d83795d68d665b8b969c08b6c40498eb2661 +Author: Emmanuele Bassi +Date: Mon Oct 29 22:23:07 2007 +0000 + + More sanity checks for objects + + Add a check for commas after complex data types inside object members. + + json-glib/json-parser.c | 8 +++++++- + 1 files changed, 7 insertions(+), 1 deletions(-) + +commit 6182457fa305a1f159e9009d3fbef42a93f2768e +Author: Emmanuele Bassi +Date: Mon Oct 29 22:17:44 2007 +0000 + + Fix the test suite for arrays + + Fix a typo in test number 7 (missing comma to separate the C strings) and + use G_TYPE_DOUBLE instead of G_TYPE_FLOAT for the floating point values. + + tests/test-02.c | 8 ++++---- + 1 files changed, 4 insertions(+), 4 deletions(-) + +commit 25cdc18be8acdad54561f919c5805ad4fa9b3ac9 +Author: Emmanuele Bassi +Date: Mon Oct 29 22:16:31 2007 +0000 + + Add more sanity checks to the parser + + Catch missing commas after an array element or an object member. + + json-glib/json-parser.c | 10 ++++++++++ + 1 files changed, 10 insertions(+), 0 deletions(-) + +commit 812422e4ed4df83374cdda6d94637448ab94dfef +Author: Emmanuele Bassi +Date: Mon Oct 29 18:18:34 2007 +0000 + + Backport fixes from Clutter trunk + + The copy of JSON-GLib in Clutter trunk has two fixes for the JsonParser + object: + + * support for negative numbers (ints and floats); + * correct parse error propagation + + which should make the JsonParser hopefully complete. + + json-glib/json-parser.c | 123 +++++++++++++++++++++++++++++++++++++++-------- + 1 files changed, 103 insertions(+), 20 deletions(-) + +commit 72d007d865a822875dfa311698fb2d13f5d3df69 +Author: Emmanuele Bassi +Date: Mon Oct 29 18:16:24 2007 +0000 + + Check if the payload is set in json_node_free() + + Before calling json_object_unref() or json_array_unref() in json_node_free() + we need to check if the payload of JsonNode is set to avoid a critical. + + json-glib/json-node.c | 6 ++++-- + 1 files changed, 4 insertions(+), 2 deletions(-) + +commit 59a66484befba457eda2ee7a9540f2b33ff04031 +Author: Emmanuele Bassi +Date: Tue Oct 16 23:20:22 2007 +0100 + + Add test unit for the JsonSerializable interface + + This should have been done before relasing 0.2.0. Well, that's what + point releases are for. + + tests/Makefile.am | 4 +- + tests/test-07.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 268 insertions(+), 1 deletions(-) + +commit 34515af7bb1d69317b9e52d0ced92beaf6770e7d +Author: Emmanuele Bassi +Date: Tue Oct 16 23:19:35 2007 +0100 + + We need the actual implementation of the JsonSerializable GType function + + A placeholder, while syntactically correct, won't do. + + json-glib/json-gobject.c | 6 ++++++ + 1 files changed, 6 insertions(+), 0 deletions(-) + +commit 0656c4bee8e14c375481c89a62f0e6e95be758ce +Author: Emmanuele Bassi +Date: Tue Oct 16 23:18:47 2007 +0100 + + Fix the compiler fixes + + This is what happens when you fix a compiler warning about a shadowing + variable and you don't run the test suite to check the results. + + json-glib/json-generator.c | 10 ++++++---- + 1 files changed, 6 insertions(+), 4 deletions(-) + +commit 88794df33e08cb409f71d1aaf702cd509b600954 +Author: Emmanuele Bassi +Date: Tue Oct 16 19:53:27 2007 +0100 + + Bump up to 0.3.0 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 0ce3bf30d84aedcf5f8f75e6c1112d91dcc1d654 +Author: Emmanuele Bassi +Date: Tue Oct 16 19:49:42 2007 +0100 + + Bump up to 0.2.0 + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 77248800533f3dabfb09dfa8ba32e3084ea62d5f +Author: Emmanuele Bassi +Date: Tue Oct 16 19:43:27 2007 +0100 + + Fix DISTCHECK configure flags + + Makefile.am | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 70be36c97a8cd598af061eb9ac5a9a5852858682 +Author: Emmanuele Bassi +Date: Tue Oct 16 19:42:05 2007 +0100 + + Fix compilation errors with extra maintainer CFLAGS + + json-glib/json-array.c | 2 +- + json-glib/json-generator.c | 16 ++++++++-------- + json-glib/json-parser.c | 4 ++-- + 3 files changed, 11 insertions(+), 11 deletions(-) + +commit 57c39582818b8a6579fed327f5c6e2fc34e93694 +Author: Emmanuele Bassi +Date: Tue Oct 16 19:41:18 2007 +0100 + + Drop -Wextra from the maintainer CFLAGS + + Some autogenerated code caused -Werror to croak, so -Wextra has to go. + + configure.ac | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +commit 9c690da734ee4513f19841f93a08ae35bbaa65b1 +Author: Emmanuele Bassi +Date: Tue Oct 16 18:20:19 2007 +0100 + + Autogenerate the ChangeLog when disting + + Makefile.am | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +commit 95fc9c4fb5af82c766d2f4f515b361028bc348e7 +Author: Emmanuele Bassi +Date: Tue Oct 16 18:20:01 2007 +0100 + + Fix EXTRA_DIST files for distcheck + + json-glib/Makefile.am | 16 +++++++++------- + 1 files changed, 9 insertions(+), 7 deletions(-) + +commit 87a36b8a5e23f3cf80730627f9b89061934a5bbf +Author: Emmanuele Bassi +Date: Tue Oct 16 17:40:06 2007 +0100 + + Add the JsonSerializable interface + + The JsonSerializable interface allows implementations to override the + GObject-to-JSON serialization process, by providing two virtual methods + to control the (de)serialization of GObject properties. This way it's + possible to serialize GObjects with properties holding complex data types. + + doc/reference/json-glib-sections.txt | 15 +++ + json-glib/json-gobject.c | 188 ++++++++++++++++++++++++++++------ + json-glib/json-gobject.h | 51 +++++++++ + json-glib/json-types.h | 1 + + 4 files changed, 224 insertions(+), 31 deletions(-) + +commit 85cb0f44c4c7297a75141999674f3eb0fdf6a308 +Author: Emmanuele Bassi +Date: Tue Oct 16 17:25:08 2007 +0100 + + Add a method for getting all the nodes from a JsonObject + + To map json_array_get_elements(), a json_object_get_values() method has + been added which returns the list of JsonNodes contained by a JsonObject. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-object.c | 41 ++++++++++++++++++++++++++++++++++ + json-glib/json-types.h | 1 + + 3 files changed, 43 insertions(+), 0 deletions(-) + +commit 474ecf1cc6aae35b184edffc75fc8626c3f12f61 +Author: Emmanuele Bassi +Date: Tue Oct 16 16:40:57 2007 +0100 + + Add a note in the JsonObject documentation for the member name normalization + + json-glib/json-object.c | 6 +++++- + 1 files changed, 5 insertions(+), 1 deletions(-) + +commit 93e26b94d9bb1909f3b16a4b0500f27660d7e621 +Author: Emmanuele Bassi +Date: Tue Oct 16 16:37:01 2007 +0100 + + Normalize every member name before using it + + Every member name is stored inside the internal hash table of JsonObject + using a normalized string: every delimiter found matching G_STR_DELIMITERS + is automatically transformed in an underscore ('_'). + + This means that "member-name" and "member_name" are completely equivalent + for a JsonObject. + + json-glib/json-object.c | 31 ++++++++++++++++++++++++++----- + 1 files changed, 26 insertions(+), 5 deletions(-) + +commit 73a7671dedfd3bb9bc1a8c197a68dcef90e627f7 +Author: Emmanuele Bassi +Date: Mon Oct 15 10:28:39 2007 +0100 + + Provide an internal g_hash_table_get_keys() and bump down GLib dependency + + Even though GLib 2.14 is now available, many systems still come out with + GLib 2.12. Since we are using just a single 2.14 function for retrieving + the members from a JsonObject, we can provide an internal version of that + function and hideit behind a pre-processor macro. + + configure.ac | 2 +- + json-glib/json-object.c | 25 +++++++++++++++++++++++++ + 2 files changed, 26 insertions(+), 1 deletions(-) + +commit 28928a2f1cbe83a2f1bf699c915daa6eca80f587 +Author: Emmanuele Bassi +Date: Mon Oct 15 10:25:09 2007 +0100 + + Add API for removing nodes from arrays and objects + + Write and document json_object_remove_member() and json_array_remove_element() + which can be used to remove a JsonNode from a JsonObject or a JsonArray + respectively. This way, the JsonObject and JsonArray are API-complete and + the object model can be manipulated in code. + + doc/reference/json-glib-sections.txt | 2 + + json-glib/json-array.c | 18 +++++ + json-glib/json-object.c | 17 +++++ + json-glib/json-types.h | 120 +++++++++++++++++---------------- + 4 files changed, 99 insertions(+), 58 deletions(-) + +commit b3ecd6e2fbdd58250427f406c43d60b8cb8d2644 +Author: Emmanuele Bassi +Date: Wed Oct 10 12:03:59 2007 +0100 + + Parse bare root values + + If the root node contains just a bare value (true, false, null, fundamental + type) then it's still valid JSON. + + Also, use the commodity JsonNode API to avoid using values in the parser + code. + + json-glib/json-parser.c | 79 +++++++++++++++++----------------------------- + 1 files changed, 29 insertions(+), 50 deletions(-) + +commit a7839a06da53d32eb372b4813e5883a04e1c36b7 +Author: Emmanuele Bassi +Date: Tue Oct 9 20:52:56 2007 +0100 + + Add the forgotten JsonGenerator type function + + doc/reference/json-glib.types | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit daeef3345c3a7f3325cea8c945f0e1d814defcc1 +Author: Emmanuele Bassi +Date: Mon Oct 8 18:29:44 2007 +0100 + + Add json_node_dup_string() + + The newly added json_node_dup_string() is a convenience function for + getting a copy of the string contained inside a JsonNode. + + doc/reference/json-glib-sections.txt | 1 + + json-glib/json-node.c | 20 ++++++++++++++++++++ + json-glib/json-types.h | 1 + + 3 files changed, 22 insertions(+), 0 deletions(-) + +commit b12efcec57fff6e7637fc924fc50333b97eb2594 +Author: Emmanuele Bassi +Date: Sun Oct 7 00:47:32 2007 +0100 + + Fix documentation about the ownership of the nodes + + When adding a JsonNode to a JsonObject or a JsonArray, the containers + take ownership of the node. + + json-glib/json-array.c | 7 +++++-- + json-glib/json-object.c | 13 ++++++++----- + 2 files changed, 13 insertions(+), 7 deletions(-) + +commit bd5a60ca658257752993ccea47950b97cdc45246 +Author: Emmanuele Bassi +Date: Fri Oct 5 18:24:27 2007 +0100 + + Add convenience accessors for fundamental types in JsonNode + + This commit adds some convenience accessors for setting and + getting fundamental types in and from a JsonNode, to avoid + jumping through all the GValue hoops. + + doc/reference/json-glib-sections.txt | 8 ++ + json-glib/json-node.c | 201 ++++++++++++++++++++++++++++++++++ + json-glib/json-types.h | 12 ++ + 3 files changed, 221 insertions(+), 0 deletions(-) + +commit ad95c8bf8e4103058d42ae71f47e6980e3b52c34 +Author: Emmanuele Bassi +Date: Fri Oct 5 18:23:15 2007 +0100 + + Use doubles when parsing, not floats + + GScanner advertise the floating point values as floats, but it really + uses doubles. Hence, we need to use G_TYPE_DOUBLE when saving the + parsed constants into a GValue. + + json-glib/json-parser.c | 8 ++++---- + 1 files changed, 4 insertions(+), 4 deletions(-) + +commit 220838e4e61c5a8fd9cf627da7309acf44fa82f0 +Author: Emmanuele Bassi +Date: Fri Oct 5 16:39:10 2007 +0100 + + Add line/position getters to JsonParser + + Add two methods to JsonParser to retrieve the currently parsed line + and position within that line. These methods works only while parsing, + so within the signal handlers and inside subclasses. + + doc/reference/json-glib-sections.txt | 6 ++++ + json-glib/json-parser.c | 46 +++++++++++++++++++++++++++++++-- + json-glib/json-parser.h | 21 +++++++++------ + 3 files changed, 61 insertions(+), 12 deletions(-) + +commit ea5ee264a5b82fd1d09fa84ec81e17c4ea0d0c4a +Author: Emmanuele Bassi +Date: Fri Oct 5 15:13:21 2007 +0100 + + Document the newly added signals + + json-glib/json-parser.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++ + json-glib/json-parser.h | 10 ++++++- + 2 files changed, 73 insertions(+), 1 deletions(-) + +commit fa9d1676da588306be5a14bcab100ad58a41b799 +Author: Emmanuele Bassi +Date: Fri Oct 5 11:59:44 2007 +0100 + + Add more signals to the JsonParser class + + JsonParser should emit signals in critical places, like: start/end of + the parsing process; start and end of a JsonObject and a JsonArray; + completion of every member and element of a JsonObject and a JsonArray. + These signals require the addition of some custom marshaller. + + json-glib/json-marshal.list | 4 ++ + json-glib/json-parser.c | 118 +++++++++++++++++++++++++++++++++++++++++- + json-glib/json-parser.h | 25 ++++++++- + 3 files changed, 141 insertions(+), 6 deletions(-) + +commit aa2b31234f7cfac63b870e8d9d74e682b3121d4a +Author: Emmanuele Bassi +Date: Fri Oct 5 11:57:53 2007 +0100 + + Implement the GType functions for JsonObject and JsonArray + + The type functions for the JsonObject and JsonArray types were declared, + albeit with the wrong return value, but not implemented. This commit + fixed the return value and implements them. + + JsonObject and JsonArray are boxed types because we don't need them to + be GObjects (no signals, no inheritance and their implementation must be + completely opaque for the developer). + + json-glib/json-array.c | 13 +++++++++++++ + json-glib/json-object.c | 13 +++++++++++++ + json-glib/json-types.h | 4 ++-- + 3 files changed, 28 insertions(+), 2 deletions(-) + +commit bd41854505f47d2176ea4cfba8083ce998c482cc +Author: Emmanuele Bassi +Date: Tue Oct 2 12:13:23 2007 +0100 + + Document the GObject integration API into the API reference + + doc/reference/json-glib-docs.sgml | 6 ++++++ + doc/reference/json-glib-sections.txt | 6 ++++++ + 2 files changed, 12 insertions(+), 0 deletions(-) + +commit cea3f69f2cde1cb4e6ea9242f6014b31864ad80f +Author: Emmanuele Bassi +Date: Tue Oct 2 12:12:49 2007 +0100 + + Add test case for GObject integration + + .gitignore | 2 + + tests/Makefile.am | 4 +- + tests/test-06.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 159 insertions(+), 1 deletions(-) + +commit 3666613e47b66d67a28bc06c6dcf678fe97eae50 +Author: Emmanuele Bassi +Date: Tue Oct 2 12:11:55 2007 +0100 + + Add GObject serialization support for JSON-GLib + + This commit adds json-gobject.h and json_serialize_gobject() to + JSON-GLib, to serialize a GObject instance into a JSON data stream. + + .gitignore | 1 + + json-glib/Makefile.am | 2 + + json-glib/json-gobject.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++ + json-glib/json-gobject.h | 33 +++++++++ + 4 files changed, 205 insertions(+), 0 deletions(-) + +commit 8398253c76cf5dda23891b49b1aaa49e57d95a8d +Author: Emmanuele Bassi +Date: Tue Oct 2 10:03:00 2007 +0100 + + Complete the tests suite with the object deserialization + + Add a test unit for JSON object generation using JsonGenerator. The + empty, simple (1-depth) and complex (nested object and array) cases + are exercised. + + The complex object generation is taken from the RFC 4627, Examples + section. + + .gitignore | 2 + + tests/Makefile.am | 4 +- + tests/test-05.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 222 insertions(+), 1 deletions(-) + +commit 2b25dbfa802f9af6e76fbe72140b687535cae8e5 +Author: Emmanuele Bassi +Date: Tue Oct 2 08:07:56 2007 +0100 + + Add objects support + + JsonGenerator can now create objects and array-nested objects, with and without + pretty printing. The test suite has been updated accordingly. + + json-glib/json-generator.c | 125 ++++++++++++++++++++++++++++++++++++++++++- + tests/test-04.c | 68 ++++++++++++++++++++++++ + 2 files changed, 190 insertions(+), 3 deletions(-) + +commit 326cdead6f1c7b1bbaae0961d28bfe5ca3af0a1a +Author: Emmanuele Bassi +Date: Mon Oct 1 23:07:18 2007 +0100 + + Add nested arrays support + + JsonGenerator now supports nested arrays, both with and without pretty + printing. The tests suite has been updated accordingly. + + json-glib/json-generator.c | 10 +++++++ + tests/test-04.c | 61 ++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 71 insertions(+), 0 deletions(-) + +commit 6f4dd6b154a150e8b4221800b61f85e5c4277d1b +Author: Emmanuele Bassi +Date: Mon Oct 1 22:58:04 2007 +0100 + + Simple arrays generation + + JsonGenerator now can create simple arrays, with "pretty" enabled and disabled. + Simple arrays are just one-level, value-only arrays. + + The test unit has been updated to exercise this new feature. + + json-glib/json-generator.c | 160 ++++++++++++++++++++++++++++++++++++++++++-- + tests/test-04.c | 86 ++++++++++++++++++++++-- + 2 files changed, 234 insertions(+), 12 deletions(-) + +commit 7f0232eaab0af434e2e086c83b68395146e410f9 +Author: Emmanuele Bassi +Date: Mon Oct 1 22:22:12 2007 +0100 + + Update list of git ignored files + + .gitignore | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +commit 47b34271f989cb5f2ba01a633d003adb6052cbd1 +Author: Emmanuele Bassi +Date: Mon Oct 1 22:20:23 2007 +0100 + + Add initial test unit for the JsonGenerator object + + tests/Makefile.am | 4 +++- + tests/test-04.c | 29 +++++++++++++++++++++++++++++ + 2 files changed, 32 insertions(+), 1 deletions(-) + +commit 8edb4ef3232428ac9e548ae6c1dfa2328bf7198c +Author: Emmanuele Bassi +Date: Mon Oct 1 22:19:28 2007 +0100 + + Add missing json-generator.h header + + json-glib/json-glib.h | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +commit f09acae8e0d6ceba5d13de75551491835a7e71de +Author: Emmanuele Bassi +Date: Mon Oct 1 19:49:43 2007 +0100 + + Document the new public functions + + Now that we moved the constructors and setters for the JSON data types into + the public symbols we need to document them to get back to 100% doc coverage. + + doc/reference/json-glib-sections.txt | 26 ++++++++++- + json-glib/json-array.c | 22 ++++++++++ + json-glib/json-node.c | 78 ++++++++++++++++++++++++++++++++++ + json-glib/json-object.c | 15 +++++++ + json-glib/json-types.h | 2 + + 5 files changed, 141 insertions(+), 2 deletions(-) + +commit 228a5e4efe65995778847f48d2be43f3df988e10 +Author: Emmanuele Bassi +Date: Mon Oct 1 18:01:20 2007 +0100 + + Move data types ctors and setters into the public headers + + Now that we are providing a generator class we need to provide the + constructors and setters for JsonNode, JsonObject and JsonArray. This + also means that the json-private.h header is now useless, so we can + remove it from the build and repository. + + json-glib/Makefile.am | 2 +- + json-glib/json-array.c | 1 - + json-glib/json-generator.c | 1 - + json-glib/json-node.c | 1 - + json-glib/json-object.c | 1 - + json-glib/json-parser.c | 1 - + json-glib/json-private.h | 35 ----------------------------------- + json-glib/json-types.h | 36 ++++++++++++++++++++++++++++++------ + 8 files changed, 31 insertions(+), 47 deletions(-) + +commit bfa60e9ef9da71e3c0d171f81cd08e8eac749061 +Author: Emmanuele Bassi +Date: Mon Oct 1 17:48:34 2007 +0100 + + Add stub class for JsonGenerator + + JsonGenerator is an object that creates JSON data streams from a data + model tree. This commit adds the JsonGenerator class to the build and + API reference. + + doc/reference/json-glib-docs.sgml | 1 + + doc/reference/json-glib-sections.txt | 25 ++++ + json-glib/Makefile.am | 2 + + json-glib/json-generator.c | 255 ++++++++++++++++++++++++++++++++++ + json-glib/json-generator.h | 82 +++++++++++ + 5 files changed, 365 insertions(+), 0 deletions(-) + +commit 41849494890f1f51bc7c4429f0ee7b5d168da302 +Author: Emmanuele Bassi +Date: Mon Oct 1 16:37:14 2007 +0100 + + Fix ids inside the main documentation index + + doc/reference/json-glib-docs.sgml | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +commit c3f8ea960ef7f8733969d9fb0f71531bc7449a16 +Author: Emmanuele Bassi +Date: Mon Oct 1 16:25:47 2007 +0100 + + Complete API reference of JSON-GLib + + With this commit, we reach 100% coverage. + + json-glib/json-parser.h | 19 +++++++++++++++++++ + json-glib/json-types.h | 20 ++++++++++++++++++++ + 2 files changed, 39 insertions(+), 0 deletions(-) + +commit 2211e4e60b90b92d868c9b5f6b61e133d2435b4d +Author: Emmanuele Bassi +Date: Mon Oct 1 16:25:11 2007 +0100 + + Implement json_node_get_parent() + + It seems that the parent accessor fell through. This commit implements + the declared json_node_get_parent() function. + + json-glib/json-node.c | 16 ++++++++++++++++ + 1 files changed, 16 insertions(+), 0 deletions(-) + +commit 29feafc236f888021b817fdfe0cfe685f5e3b65e +Author: Emmanuele Bassi +Date: Mon Oct 1 16:16:15 2007 +0100 + + Add licensing informations to the source code + + json-glib/json-array.c | 29 ++++++++++++++++++++++++ + json-glib/json-node.c | 52 ++++++++++++++++++++++++++++++++++++------ + json-glib/json-object.c | 34 ++++++++++++++++++++++++--- + json-glib/json-parser.c | 28 +++++++++++++++++++++- + json-glib/json-parser.h | 19 +++++++++++++++ + json-glib/json-types.h | 35 +++++++++++++++++++++++++++++ + json-glib/json-version.h.in | 19 +++++++++++++++ + 7 files changed, 202 insertions(+), 14 deletions(-) + +commit 6eb1a5e94957d3555e7de5f6744a8777cd89efaf +Author: Emmanuele Bassi +Date: Mon Oct 1 15:55:04 2007 +0100 + + Fix a stray newline in the documentation + + doc/reference/json-glib-docs.sgml | 3 +-- + 1 files changed, 1 insertions(+), 2 deletions(-) + +commit a5ecf553082816a1ce6c4354ff23e8addf8d9c80 +Author: Emmanuele Bassi +Date: Mon Oct 1 15:52:54 2007 +0100 + + Update git ignore file + + .gitignore | 22 ++++++++++++++++++++++ + 1 files changed, 22 insertions(+), 0 deletions(-) + +commit 9bdee4c01adef7e9d7ed18d5dd94671057f1459b +Author: Emmanuele Bassi +Date: Mon Oct 1 15:50:49 2007 +0100 + + Add API reference for JSON-GLib + + Use gtk-doc to build the various bits and pieces of the API reference + for JSON-GLib. + + doc/Makefile.am | 1 + + doc/reference/Makefile.am | 2 +- + doc/reference/json-glib-docs.sgml | 153 ++++++++++++++++++++++++++++++++++ + doc/reference/json-glib-sections.txt | 92 ++++++++++++++++++++ + doc/reference/json-glib.types | 1 + + json-glib/json-node.c | 2 +- + json-glib/json-version.h.in | 8 ++ + 7 files changed, 257 insertions(+), 2 deletions(-) + +commit 2e362edd46f11f2fd1fa327877372b902b70c280 +Author: Emmanuele Bassi +Date: Mon Oct 1 14:57:39 2007 +0100 + + Add more test cases for the object parsing + + tests/test-03.c | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit 79423943a8588a8ed6d5bedbc038255111897261 +Author: Emmanuele Bassi +Date: Mon Oct 1 14:57:25 2007 +0100 + + Swallow the comma earlier in the parser + + The comma is used as a member and element separator, so it should be + swallowed by the parser as soon as possible. + + json-glib/json-parser.c | 25 ++++++++++++++++--------- + 1 files changed, 16 insertions(+), 9 deletions(-) + +commit e711b883dba88d68b6986d87f14a06361cf54be7 +Author: Emmanuele Bassi +Date: Mon Oct 1 14:46:31 2007 +0100 + + Update gitignore + + .gitignore | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +commit 591989c80b16fe68bdd79bcc7759a4249fdaf129 +Author: Emmanuele Bassi +Date: Mon Oct 1 14:45:42 2007 +0100 + + Test object parsing + + Add JSON object parsing testing to the test units. + + tests/Makefile.am | 8 +- + tests/test-02.c | 73 +++++++++++++++++- + tests/test-03.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 285 insertions(+), 7 deletions(-) + +commit ba3a6e6afed0d4fe3853b8e7400516557a20f775 +Author: Emmanuele Bassi +Date: Mon Oct 1 14:44:51 2007 +0100 + + Add JSON object parsing + + This commit completes the JsonParser class by adding the ability to + parse JSON objects, either alone or inside arrays. JsonParser is now + a JSON parser. + + json-glib/json-parser.c | 235 ++++++++++++++++++++++++++++++++++++++++++++--- + 1 files changed, 220 insertions(+), 15 deletions(-) + +commit fd89ad3d7127e68df06345fa52863e73ada93689 +Author: Emmanuele Bassi +Date: Mon Oct 1 14:43:12 2007 +0100 + + Declare json_node_take_object() and json_node_take_array() + + JsonParser uses the take variant of JsonNode setters for objects and arrays + since it's the one creating the objects. This way, we avoid leaks and the + extra complexity of unreffing the newly created complex containers after + setting them into the JsonNodes. + + json-glib/json-private.h | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +commit b2ee98f9b0baac019540020e177189b6a3780b83 +Author: Emmanuele Bassi +Date: Mon Oct 1 13:01:32 2007 +0100 + + Chain up nodes to their parent + + When parsing a JSON node we need to correctly set up the parent node + of the newly created ones. + + json-glib/json-parser.c | 6 +++++- + 1 files changed, 5 insertions(+), 1 deletions(-) + +commit 88b11be5eec1da769328e93189bc2f3316c9cd0a +Author: Emmanuele Bassi +Date: Mon Oct 1 12:57:46 2007 +0100 + + Add JsonNode, a generic container for JSON types + + This huge commit removes JsonData and adds JsonNode, the generic container + for fundamental and complex data types extracted from a JSON stream. The + contents of a JsonNode can be extracted from it in form of a GValue for + fundamental types (integers, floats, strings, booleans) or in form of + JsonObject and JsonArray objects. JsonObject and JsonArray now accept + JsonNodes instead of GValues. + + The JsonParser object builds the data model tree when parsing a JSON stream; + the tree can be recursed by getting the root node and walking it using the + GValue API for the fundamental types and the objects/arrays API for complex + types. + + The API has been updated and the tests now recurse through the generated + data model tree. + + json-glib/Makefile.am | 2 +- + json-glib/json-array.c | 64 +++++------ + json-glib/json-data.c | 93 ---------------- + json-glib/json-node.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++ + json-glib/json-object.c | 44 ++------ + json-glib/json-parser.c | 87 +++++++++++---- + json-glib/json-parser.h | 2 +- + json-glib/json-private.h | 38 +++---- + json-glib/json-types.h | 55 ++++++---- + tests/test-01.c | 2 +- + tests/test-02.c | 112 ++++++++++++++++++- + 11 files changed, 533 insertions(+), 235 deletions(-) + +commit c11ebd32f73a1e21d6097bf9eba8e12f7e35497a +Author: Emmanuele Bassi +Date: Fri Sep 21 21:07:12 2007 +0100 + + Add JsonData, an opaque container for JSON data types + + JsonData is like GValue, but it stores JSON data types (objects and + arrays) and allows us to retrieve them safely. This way we can actually + know the type of the objects returned by the parser and by the other + object walking functions. + + json-glib/Makefile.am | 1 + + json-glib/json-data.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++ + json-glib/json-private.h | 7 +++ + json-glib/json-types.h | 49 +++++++++++++++++------- + 4 files changed, 136 insertions(+), 14 deletions(-) + +commit f661c7e1a04c2fb198279030c9dd812f357240a3 +Author: Emmanuele Bassi +Date: Fri Sep 21 21:05:12 2007 +0100 + + Skip the token check after parsing a nested array + + Since there cannot be any other token except for the comma, which we + eat anyway, there's no point in going through the switch() check + after we have finished parsing a nested array. + + json-glib/json-parser.c | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +commit ae3a66e6a90b6cfd71cb8c41c0d752fd1b0a67cc +Author: Emmanuele Bassi +Date: Fri Sep 21 20:15:52 2007 +0100 + + Update gitignore file + + .gitignore | 7 +++++++ + 1 files changed, 7 insertions(+), 0 deletions(-) + +commit 7875c5f573fdc6c2a39e958d2032f4c26d1f91ff +Author: Emmanuele Bassi +Date: Fri Sep 21 20:14:49 2007 +0100 + + Start the test suite + + Add the first two test units: + + - test-01.c: build/empty test unit + - test-02.c: array test unit + + Every test unit must follow the same naming policy, so we can add + a run-tests script later on and a pre-commit hook to invoke it + and catch regressions automagically. + + tests/Makefile.am | 12 ++++++++++++ + tests/test-01.c | 31 +++++++++++++++++++++++++++++++ + tests/test-02.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 95 insertions(+), 0 deletions(-) + +commit 81ad2e5c61db2391d2d98a6b5df1247e3abf16ec +Author: Emmanuele Bassi +Date: Fri Sep 21 20:12:02 2007 +0100 + + Parse JSON arrays + + Add the array parsing code. The parser identifies and builds nested + levels of arrays, but the storage is not quite right. That is a problem + of the parser object, though, so this can be considered a first raw + pass at the problem. + + json-glib/json-parser.c | 114 ++++++++++++++++++++++++++++++++++++++++------- + 1 files changed, 97 insertions(+), 17 deletions(-) + +commit b433703db6722785e33f968830e23c5806230ac2 +Author: Emmanuele Bassi +Date: Fri Sep 21 12:16:15 2007 +0100 + + Add RFC 4627 text, where JSON is defined + + doc/rfc4627.txt | 563 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 563 insertions(+), 0 deletions(-) + +commit c77af0ec13e3e11cdb784510e6a75125bb8ef667 +Author: Emmanuele Bassi +Date: Fri Sep 21 12:08:09 2007 +0100 + + Top-levels in JSON can only be objects or arrays + + JSON is an object serialisation format (thanks, RFC4627), so it can + only express objects and/or arrays as top-levels. + + json-glib/json-parser.c | 24 ++++++++++++++---------- + 1 files changed, 14 insertions(+), 10 deletions(-) + +commit 0ff67fb63c5e021b88d6a3d17c5e34dc95ac2676 +Author: Emmanuele Bassi +Date: Fri Sep 21 11:58:00 2007 +0100 + + Update gitignore file + + .gitignore | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +commit d98d8c3d245192abe6ec5a531c9d0d678b07d061 +Author: Emmanuele Bassi +Date: Fri Sep 21 11:54:40 2007 +0100 + + Add stubs to JsonParser for actually parsing a JSON stream + + Initial commit for getting JsonParser to work. Because GScanner API + is old and mostly sucks, we need to do some magic with signals. + + If json_parser_load_from_data() fails, the passed GError will be set + with a JSON_PARSER_ERROR code and message; unfortunately, we can't get + the nice error message out of GScanner. We can, however, override the + default message handler and make it emit a signal on the JsonParser + object. + + So, to make a long story short: the GError passed to the load_from_data() + method is filled with a short error message; the *real* error message + is passed to the ::error signal handlers so they can actually use it. + + GScanner should really get a way to retrieve the last error message. + + json-glib/json-parser.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++- + json-glib/json-parser.h | 28 +++++- + 2 files changed, 290 insertions(+), 4 deletions(-) + +commit cd1040e2fb6f6da50aaf72017746b33234c39704 +Author: Emmanuele Bassi +Date: Fri Sep 21 11:08:06 2007 +0100 + + Add marshallers generation to the build + + Use glib-genmarshal to generate the marshallers we need. For the + time being, we just need a (void,pointer). + + json-glib/Makefile.am | 30 +++++++++++++++++++++++++----- + json-glib/json-marshal.list | 1 + + 2 files changed, 26 insertions(+), 5 deletions(-) + +commit 821ac3e602f936318f9a2c3b831f135408ca5d74 +Author: Emmanuele Bassi +Date: Thu Sep 20 20:41:09 2007 +0100 + + Add gitignore file + + .gitignore | 29 +++++++++++++++++++++++++++++ + 1 files changed, 29 insertions(+), 0 deletions(-) + +commit c5bfb22f964b8f1feecdc8fb29d8a74b36861d32 +Author: Emmanuele Bassi +Date: Thu Sep 20 17:33:28 2007 +0100 + + Initial import of JSON-GLib + + JSON-GLib is a JSON parser library written with GLib and GObject. + + JSON is the JavaScript Object Notation, and it's used to define objects + and object hierarchies in a human-readable way. + + AUTHORS | 1 + + COPYING | 504 ++++++++++++++++++++++++++++++++++++++++++ + ChangeLog | 6 + + Makefile.am | 15 ++ + README | 39 ++++ + autogen.sh | 4 + + configure.ac | 119 ++++++++++ + doc/reference/Makefile.am | 82 +++++++ + doc/reference/version.xml.in | 1 + + json-glib.pc.in | 11 + + json-glib/Makefile.am | 52 +++++ + json-glib/json-array.c | 176 +++++++++++++++ + json-glib/json-glib.h | 8 + + json-glib/json-object.c | 201 +++++++++++++++++ + json-glib/json-parser.c | 152 +++++++++++++ + json-glib/json-parser.h | 57 +++++ + json-glib/json-private.h | 26 +++ + json-glib/json-types.h | 32 +++ + json-glib/json-version.h.in | 65 ++++++ + 19 files changed, 1551 insertions(+), 0 deletions(-) diff --git a/json-glib/Makefile.am b/json-glib/Makefile.am new file mode 100644 index 0000000..8c3d1af --- /dev/null +++ b/json-glib/Makefile.am @@ -0,0 +1,22 @@ +include $(top_srcdir)/build/autotools/Makefile.am.gtest +include $(top_srcdir)/build/autotools/Makefile.am.silent + +ACLOCAL_AMFLAGS = -I build/autotools + +SUBDIRS = json-glib build + +CLEANFILES = $(pcfiles) test-report.xml + +DISTCHECK_CONFIGURE_FLAGS = --enable-maintainer-flags --enable-introspection + +dist-hook: + @if test -d "$(srcdir)/.git"; then \ + echo Generating ChangeLog ; \ + ( cd "$(srcdir)" \ + && $(top_srcdir)/build/missing --run git log --stat ) > ChangeLog.tmp \ + && mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \ + || ( rm -f ChangeLog.tmp; \ + echo Failed to generate ChangeLog >&2 ); \ + else \ + echo A git checkout is required to generate a ChangeLog >&2; \ + fi diff --git a/json-glib/NEWS b/json-glib/NEWS new file mode 100644 index 0000000..fdfae67 --- /dev/null +++ b/json-glib/NEWS @@ -0,0 +1,108 @@ +Overview of changes for 0.14.2 +============================== +• Build fixes for compiling against the latest GLib [Matthias Clasen] +• Documentation fixes + +Overview of changes for 0.14.0 +============================== +• Documentation fixes +• Bump the dependency on GLib to 2.26 +• Allow building on Windows using VisualStudio [Chun-wei Fan] +• Add JSON ↔ GVariant translation [Eduardo Lima Mitev] +• Improve sanity checks when (de)serializing GObject + properties [Tristan Van Berkom] +• Add missing introspection annotations [Luca Bruno] +• Add JsonReader.list_members() +• Allow using JsonReader.read_element() on JSON objects +• Remove all G_CONST_RETURN usage after the deprecation +• Allow JsonSerializable to override the introspection and + modification of properties +• Add i18n support for GError messages +• Do not serialize GObject properties that match their default + values. +• Make JsonReader perform a stricter validation especially when using + the strongly typed accessors. + +Overview of changes for 0.12.0 +============================== +• Support surrogate pairs in JSON strings [Eiichi Sato] +• Update the test suite +• Add (optional) coverage reports +• Improve strictness of JsonParser +• Improve error reporting of JsonParser +• Add JsonBuilder, a convenience API for programmatic building + of JSON trees [Luca Bruno] +• Add methods for JsonParser and JsonGenerator to handle Input|OutputStream + objects coming from GIO. +• Add JsonReader, a convenience API for cursor-based parsing of + JSON trees +• Depend on GObject-Introspection ≥ 0.9.5 + +Overview of changes for 0.10.0 +============================== +• Fix generation of doubles [Cornelius Hald] +• Add more units to the test suite +• Add JsonNode macros for quick type checking +• Guarantee insertion order when parsing and generating JSON Objects +• Serialize GParamSpecObject properties +• Add serialization and deserialization for GBoxed types +• Add API for serializing GObjects to, and deserializing from, JsonNode +• Build environment fixes +• Documentation fixes +• Generate correct introspection data +• Make JsonSerializable in complete control of deserialization [Tristan Van + Berkom] + +Overview of changes for 0.8.0 +============================= +* Remove the in-tree Vala bindings: they are part of Vala, now +* Remove the in-tree Debian packaging +* Fix bug #958: JsonGenerator does not escape special characters +* Fix bug #965: Conditionally compile the test suite +* Display the filename and line inside the error messages when + loading from a file +* Fix bug #1203: Correctly terminate a string array +* Fix bug #1393: Regression tests fail on OpenBSD +* Do not leak memory on error code paths +* Improve and clean up the build system +* Make JsonNode completely opaque +* Conditionally generate introspection data on build +* Fix bug #1353: Do not overwrite when copying +* Deprecate json_object_add_member() +* Add convenience accessors for JsonObject and JsonArray +* Add convenience iteration functions for JsonObject and JsonArray +* Automatically promote integers to gint64, to compensate for the + lack of integer size in the JSON specificiation +* Disallow the inclusion of single header files: only json-glib,h + and json-gobject.h can be included directly +* Documentation fixes +* Clean up and remove code duplication inside the Parser object + +Overview of changes for 0.6.0 +============================= +* Allow deserialization of strings into enum and flag types +* Add the :indent-char property to JsonGenerator +* Add functions to retrieve copies of the nodes inside Object and Array +* Fix leaks and invalid accesses +* Use the right type for the buffer length parameter in JsonParser +* Provide a default implementation for JsonSerializable +* Provide our own JSON tokenizer (using GScanner) for the JSON-only + features that would have been lost by using GScanner +* Add a fully automated test suite, using the GTest framework +* Allow 'null' nodes to return a value without warnings +* Add support for parsing Unicode characters escaped using \uXXXX +* Make the deserialization of G_TYPE_STRV properties more robust +* Export the public symbols only +* Provide GTypes for the enumerations +* Avoid a warning when trying to copy an empty JsonNode +* Fix gtk-doc cross-references with GLib and GObject documentation + +Overview of changes for 0.4.0 +============================= +* Support parsing of negative numbers +* Fix parse error propagation and message +* More parser sanity checks +* GObject deserialization support +* Detect and parse JSON masked as a JavaScript assignment +* Allow using JsonNode with GObject properties and signals +* Add JsonGenerator:root property diff --git a/json-glib/README b/json-glib/README new file mode 100644 index 0000000..d50bfb0 --- /dev/null +++ b/json-glib/README @@ -0,0 +1,103 @@ +JSON-GLib - A JSON parser for GLib-based libraries and applications +=============================================================================== + +JSON-GLib implements a full JSON parser using GLib and GObject. + +Use JSON-GLib it is possible to parse and generate valid JSON data +structures, using a DOM-like API. JSON-GLib also offers GObject +integration, providing the ability to serialize and deserialize +GObject instances to and from JSON data types. + +JSON is the JavaScript Object Notation; it can be used to represent +objects and object hierarchies while retaining human-readability. + +GLib is a C library providing common and efficient data types for +the C developers. + +GObject is a library providing a run-time Object Oriented type system +for C developers. GLib and GObject are extensively used by the GTK+ +toolkit and by the GNOME project. + +For more information, see: + • JSON: http://www.json.org + • GLib and GObject: http://www.gtk.org + +REQUIREMENTS +------------ +In order to build JSON-GLib you will need: + + • pkg-config + • gtk-doc ≥ 1.13 + • GLib, GIO ≥ 2.26 + +Optionally, JSON-GLib depends on: + + • GObject-Introspection ≥ 0.9.5 + +INSTALLATION +------------------------------------------------------------------------------- +To build JSON-GLib just run: + + $ ./configure + $ make all + # make install + +BUGS +------------------------------------------------------------------------------- +If you find a bug in JSON-GLib, please create a Bugzilla entry here: + + http://bugzilla.gnome.org/enter_bug.cgi?product=json-glib + +Attaching: + + • the version of JSON-GLib + ◦ if it is a development version, the branch of the git repository + • the JSON data that produced the bug (if any) + • a small test case, if none of the test units exhibit the behaviour + • in case of a segmentation fault, a full stack trace with debugging + symbols obtained through gdb is greatly appreaciated + +RELEASE NOTES +------------------------------------------------------------------------------- +• Prior to JSON-GLib 0.10, a JsonSerializable implementation could + automatically fall back to the default serialization code by simply + returning NULL from an overridden JsonSerializable::serialize_property() + virtual function. Since JSON-GLib 0.10 this is not possible any more. A + JsonSerializable is always expected to serialize and deserialize all + properties. JSON-GLib provides public API for the default implementation + in case the serialization code wants to fall back to that. + +HACKING +------------------------------------------------------------------------------- +JSON-GLib is developed mainly inside a GIT repository available at: + + http://git.gnome.org/browse/json-glib + +You can clone the GIT repository with: + + git clone git://git.gnome.org/json-glib + +If you want to contribute functionality or bug fixes to JSON-GLib you +can either notify me to pull from your GIT repository or send me a set +of patches using: + + git format-patch master -k -s + +or: + + git send-email -k -s + +Make sure you always run the test suite when you are fixing bugs. New +features should come with a test unit. + +AUTHOR, COPYRIGHT AND LICENSING +------------------------------------------------------------------------------- +JSON-GLib has been written by Emmanuele Bassi + +JSON-GLib is released under the terms of the GNU Lesser General Public License, +either version 2.1 or (at your option) any later version. + +See the file COPYING for details. + +Copyright (C) 2007, 2008 OpenedHand Ltd +Copyright (C) 2009, 2010 Intel Corp. diff --git a/json-glib/build/Makefile.am b/json-glib/build/Makefile.am new file mode 100644 index 0000000..5dce928 --- /dev/null +++ b/json-glib/build/Makefile.am @@ -0,0 +1,11 @@ +SUBDIRS = autotools + +test-report: + @true + +test: + @true + +check-local: test + +.PHONY: test-report test check-local diff --git a/json-glib/build/autotools/Makefile.am b/json-glib/build/autotools/Makefile.am new file mode 100644 index 0000000..cc2d11c --- /dev/null +++ b/json-glib/build/autotools/Makefile.am @@ -0,0 +1,21 @@ +EXTRA_DIST = \ + as-compiler-flag.m4 \ + as-linguas.m4 \ + introspection.m4 \ + Makefile.am.silent \ + Makefile.am.enums \ + Makefile.am.marshal \ + Makefile.am.gtest \ + Makefile.am.gcov \ + Makefile.am.gitignore + +# needed to avoid including Makefile.am.gtest +test-report: + @true + +test: + @true + +check-local: test + +.PHONY: test test-report check-local diff --git a/json-glib/build/autotools/Makefile.am.enums b/json-glib/build/autotools/Makefile.am.enums new file mode 100644 index 0000000..0f34732 --- /dev/null +++ b/json-glib/build/autotools/Makefile.am.enums @@ -0,0 +1,43 @@ +# Rules for generating enumeration types using glib-mkenums +# +# Define: +# glib_enum_h = header template file +# glib_enum_c = source template file +# glib_enum_headers = list of headers to parse +# +# before including Makefile.am.enums. You will also need to have +# the following targets already defined: +# +# CLEANFILES +# DISTCLEANFILES +# BUILT_SOURCES +# EXTRA_DIST +# +# Author: Emmanuele Bassi + +enum_tmpl_h=$(glib_enum_h:.h=.h.in) +enum_tmpl_c=$(glib_enum_c:.c=.c.in) + +CLEANFILES += stamp-enum-types +DISTCLEANFILES += $(glib_enum_h) $(glib_enum_c) +BUILT_SOURCES += $(glib_enum_h) $(glib_enum_c) +EXTRA_DIST += $(srcdir)/$(enum_tmpl_h) $(srcdir)/$(enum_tmpl_c) + +stamp-enum-types: $(glib_enum_headers) $(srcdir)/$(enum_tmpl_h) + $(QUIET_GEN)$(GLIB_MKENUMS) \ + --template $(srcdir)/$(enum_tmpl_h) \ + $(glib_enum_headers) > xgen-eh \ + && (cmp -s xgen-eh $(glib_enum_h) || cp -f xgen-eh $(glib_enum_h)) \ + && rm -f xgen-eh \ + && echo timestamp > $(@F) + +$(glib_enum_h): stamp-enum-types + @true + +$(glib_enum_c): $(glib_enum_h) $(srcdir)/$(enum_tmpl_c) + $(QUIET_GEN)$(GLIB_MKENUMS) \ + --template $(srcdir)/$(enum_tmpl_c) \ + $(glib_enum_headers) > xgen-ec \ + && cp -f xgen-ec $(glib_enum_c) \ + && rm -f xgen-ec + diff --git a/json-glib/build/autotools/Makefile.am.gitignore b/json-glib/build/autotools/Makefile.am.gitignore new file mode 100644 index 0000000..26fe561 --- /dev/null +++ b/json-glib/build/autotools/Makefile.am.gitignore @@ -0,0 +1,24 @@ +# this file should only be used in directories that generate test +# or example binaries through noinst_PROGRAMS; it is *not* a full +# generator of Git ignore files, and it's not meant to be used as +# the top-level Git ignore file generator. + +$(srcdir)/.gitignore: Makefile.am + $(QUIET_GEN)( \ + echo "*.o" ; \ + echo ".gitignore" ; \ + ) > $(srcdir)/.gitignore ; \ + for p in $(noinst_PROGRAMS); do \ + echo "/$$p" >> $(srcdir)/.gitignore ; \ + done + +gitignore: $(srcdir)/.gitignore + +gitignore-clean: + $(QUIET_RM)rm -f $(srcdir)/.gitignore + +.PHONY: gitignore gitignore-clean + +all: gitignore + +maintainer-clean: gitignore-clean diff --git a/json-glib/build/autotools/Makefile.am.gtest b/json-glib/build/autotools/Makefile.am.gtest new file mode 100644 index 0000000..f847599 --- /dev/null +++ b/json-glib/build/autotools/Makefile.am.gtest @@ -0,0 +1,66 @@ +# JSON-GLib - JSON reader and writer library + +GTESTER = gtester +GTESTER_REPORT = gtester-report + +# initialize variables for unconditional += appending +EXTRA_DIST = +TEST_PROGS = + +### testing rules + +# test: run all tests in cwd and subdirs +test: test-nonrecursive + @for subdir in $(SUBDIRS) . ; do \ + test "$$subdir" = "." -o "$$subdir" = "po" || \ + ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ + done + +# test-nonrecursive: run tests only in cwd +test-nonrecursive: ${TEST_PROGS} + @test -z "${TEST_PROGS}" || ${GTESTER} --verbose ${TEST_PROGS} + +# test-report: run tests in subdirs and generate report +# perf-report: run tests in subdirs with -m perf and generate report +# full-report: like test-report: with -m perf and -m slow +test-report perf-report full-report: ${TEST_PROGS} + @test -z "${TEST_PROGS}" || { \ + case $@ in \ + test-report) test_options="-k";; \ + perf-report) test_options="-k -m=perf";; \ + full-report) test_options="-k -m=perf -m=slow";; \ + esac ; \ + if test -z "$$GTESTER_LOGDIR" ; then \ + ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \ + elif test -n "${TEST_PROGS}" ; then \ + ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \ + fi ; \ + } + @ ignore_logdir=true ; \ + if test -z "$$GTESTER_LOGDIR" ; then \ + GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \ + ignore_logdir=false ; \ + fi ; \ + for subdir in $(SUBDIRS) . ; do \ + test "$$subdir" = "." -o "$$subdir" = "po" || \ + ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ + done ; \ + $$ignore_logdir || { \ + echo '' > $@.xml ; \ + echo '' >> $@.xml ; \ + echo '' >> $@.xml ; \ + echo ' $(PACKAGE)' >> $@.xml ; \ + echo ' $(VERSION)' >> $@.xml ; \ + echo '' >> $@.xml ; \ + for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \ + sed '1,1s/^?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \ + done ; \ + echo >> $@.xml ; \ + echo '' >> $@.xml ; \ + rm -rf "$$GTESTER_LOGDIR"/ ; \ + ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $@.xml >$@.html ; \ + } +.PHONY: test test-report perf-report full-report test-nonrecursive + +# run tests in cwd as part of make check +check-local: test-nonrecursive diff --git a/json-glib/build/autotools/Makefile.am.marshal b/json-glib/build/autotools/Makefile.am.marshal new file mode 100644 index 0000000..cab117d --- /dev/null +++ b/json-glib/build/autotools/Makefile.am.marshal @@ -0,0 +1,45 @@ +# Rules for generating marshal files using glib-genmarshal +# +# Define: +# glib_marshal_list = marshal list file +# glib_marshal_prefix = prefix for marshal functions +# +# before including Makefile.am.marshal. You will also need to have +# the following targets already defined: +# +# CLEANFILES +# DISTCLEANFILES +# BUILT_SOURCES +# EXTRA_DIST +# +# Author: Emmanuele Bassi + +marshal_h = $(glib_marshal_list:.list=.h) +marshal_c = $(glib_marshal_list:.list=.c) + +CLEANFILES += stamp-marshal +DISTCLEANFILES += $(marshal_h) $(marshal_c) +BUILT_SOURCES += $(marshal_h) $(marshal_c) +EXTRA_DIST += $(srcdir)/$(glib_marshal_list) + +stamp-marshal: $(glib_marshal_list) + $(QUIET_GEN)$(GLIB_GENMARSHAL) \ + --prefix=$(glib_marshal_prefix) \ + --header \ + $(srcdir)/$(glib_marshal_list) > xgen-mh \ + && (cmp -s xgen-mh $(marshal_h) || cp -f xgen-mh $(marshal_h)) \ + && rm -f xgen-mh \ + && echo timestamp > $(@F) + +$(marshal_h): stamp-marshal + @true + +$(marshal_c): $(marshal_h) + $(QUIET_GEN)(echo "#include \"$(marshal_h)\"" ; \ + $(GLIB_GENMARSHAL) \ + --prefix=$(glib_marshal_prefix) \ + --body \ + $(srcdir)/$(glib_marshal_list)) > xgen-mc \ + && cp xgen-mc $(marshal_c) \ + && rm -f xgen-mc + diff --git a/json-glib/build/autotools/Makefile.am.silent b/json-glib/build/autotools/Makefile.am.silent new file mode 100644 index 0000000..8576846 --- /dev/null +++ b/json-glib/build/autotools/Makefile.am.silent @@ -0,0 +1,11 @@ +# custom rules for quiet builds + +QUIET_GEN = $(AM_V_GEN) + +QUIET_LN = $(QUIET_LN_$(V)) +QUIET_LN_ = $(QUIET_LN_$(AM_DEFAULT_VERBOSITY)) +QUIET_LN_0 = @echo ' LN '$@; + +QUIET_RM = $(QUIET_RM_$(V)) +QUIET_RM_ = $(QUIET_RM_$(AM_DEFAULT_VERBOSITY)) +QUIET_RM_0 = @echo ' RM '$@; diff --git a/json-glib/build/autotools/as-compiler-flag.m4 b/json-glib/build/autotools/as-compiler-flag.m4 new file mode 100644 index 0000000..0f660cf --- /dev/null +++ b/json-glib/build/autotools/as-compiler-flag.m4 @@ -0,0 +1,62 @@ +dnl as-compiler-flag.m4 0.1.0 + +dnl autostars m4 macro for detection of compiler flags + +dnl David Schleef + +dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $ + +dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_COMPILER_FLAG], +[ + AC_MSG_CHECKING([to see if compiler understands $1]) + + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CFLAGS="$save_CFLAGS" + + if test "X$flag_ok" = Xyes ; then + m4_ifvaln([$2],[$2]) + true + else + m4_ifvaln([$3],[$3]) + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + +dnl AS_COMPILER_FLAGS(VAR, FLAGS) +dnl Tries to compile with the given CFLAGS. + +AC_DEFUN([AS_COMPILER_FLAGS], +[ + list=$2 + flags_supported="" + flags_unsupported="" + AC_MSG_CHECKING([for supported compiler flags]) + for each in $list + do + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $each" + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CFLAGS="$save_CFLAGS" + + if test "X$flag_ok" = Xyes ; then + flags_supported="$flags_supported $each" + else + flags_unsupported="$flags_unsupported $each" + fi + done + AC_MSG_RESULT([$flags_supported]) + if test "X$flags_unsupported" != X ; then + AC_MSG_WARN([unsupported compiler flags: $flags_unsupported]) + fi + $1="$$1 $flags_supported" +]) + diff --git a/json-glib/build/autotools/as-linguas.m4 b/json-glib/build/autotools/as-linguas.m4 new file mode 100644 index 0000000..92b28f7 --- /dev/null +++ b/json-glib/build/autotools/as-linguas.m4 @@ -0,0 +1,24 @@ +# Set ALL_ALL_LINGUAS based on the .po files present. Optional argument is the +# name of the po directory. $podir/LINGUAS.ignore can be used to ignore a +# subset of the po files. + +AC_DEFUN([AS_ALL_LINGUAS], +[ + AC_MSG_CHECKING([for linguas]) + podir="m4_default([$1],[$srcdir/po])" + linguas=`cd $podir && ls *.po 2>/dev/null | awk 'BEGIN { FS="."; ORS=" " } { print $[]1 }'` + if test -f "$podir/LINGUAS.ignore"; then + ALL_LINGUAS=""; + ignore_linguas=`sed -n -e 's/^\s\+\|\s\+$//g' -e '/^#/b' -e '/\S/!b' \ + -e 's/\s\+/\n/g' -e p "$podir/LINGUAS.ignore"`; + for lang in $linguas; do + if ! echo "$ignore_linguas" | grep -q "^${lang}$"; then + ALL_LINGUAS="$ALL_LINGUAS $lang"; + fi; + done; + else + ALL_LINGUAS="$linguas"; + fi; + AC_SUBST([ALL_LINGUAS]) + AC_MSG_RESULT($ALL_LINGUAS) +]) diff --git a/json-glib/configure.ac b/json-glib/configure.ac new file mode 100644 index 0000000..bc678ae --- /dev/null +++ b/json-glib/configure.ac @@ -0,0 +1,155 @@ +# bump micro_version to the next even number for each point release +# bump micro_version to the next odd number after each release +m4_define([json_major_version], [0]) +m4_define([json_minor_version], [14]) +m4_define([json_micro_version], [2]) + +m4_define([json_version], [json_major_version.json_minor_version.json_micro_version]) + +m4_define([json_release_status], + [m4_if(m4_eval(json_micro_version % 2), [1], [git], + [m4_if(m4_eval(json_minor_version % 2), [1], [snapshot], + [release])])]) + +# bump up by 1 for every micro release with no API changes, otherwise +# set to 0. after release, bump up by 1 +m4_define([json_interface_age], [2]) +m4_define([json_binary_age], [m4_eval(100 * json_minor_version + json_micro_version)]) + +m4_define([lt_current], [m4_eval(100 * json_minor_version + json_micro_version - json_interface_age)]) +m4_define([lt_revision], [json_interface_age]) +m4_define([lt_age], [m4_eval(json_binary_age - json_interface_age)]) + +m4_define([glib_req_version], [2.26]) + +AC_PREREQ([2.63]) + +AC_INIT([json-glib], + [json_version], + [http://bugzilla.gnome.org/enter_bug.cgi?product=json-glib], + [json-glib], + [http://live.gnome.org/JsonGlib]) + +AC_CONFIG_HEADER([config.h]) +AC_CONFIG_SRCDIR([json-glib/json-glib.h]) +AC_CONFIG_AUX_DIR([build]) +AC_CONFIG_MACRO_DIR([build/autotools]) + +AM_INIT_AUTOMAKE([1.11 no-define foreign -Wno-portability dist-xz no-dist-gzip tar-ustar]) + +AM_SILENT_RULES([yes]) +AM_PATH_GLIB_2_0 +AM_PROG_CC_C_O + +LT_PREREQ([2.2.6]) +LT_INIT([disable-shared]) +dnl LT_INIT([disable-static]) + +# Honor aclocal flags +ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS" + +JSON_MAJOR_VERSION=json_major_version +JSON_MINOR_VERSION=json_minor_version +JSON_MICRO_VERSION=json_micro_version +JSON_VERSION=json_version +JSON_RELEASE_STATUS=json_release_status +AC_SUBST(JSON_MAJOR_VERSION) +AC_SUBST(JSON_MICRO_VERSION) +AC_SUBST(JSON_MINOR_VERSION) +AC_SUBST(JSON_VERSION) +AC_SUBST(JSON_RELEASE_STATUS) + +JSON_LT_CURRENT=lt_current +JSON_LT_REVISION=lt_revision +JSON_LT_AGE=lt_age +JSON_LT_VERSION="$JSON_LT_CURRENT:$JSON_LT_REVISION:$JSON_LT_AGE" +JSON_LT_LDFLAGS="-version-info $JSON_LT_VERSION" + +AC_CANONICAL_HOST +AC_MSG_CHECKING([if building for some Win32 platform]) +AS_CASE([$host], + + [*-*-mingw*|*-*-cygwin*], + [ + JSON_LT_LDFLAGS="$JSON_LT_LDFLAGS -no-undefined" + platform_win32=yes + ], + + [platform_win32=no] +) +AC_MSG_RESULT([$platform_win32]) + +AC_SUBST(JSON_LT_LDFLAGS) + +GLIB_PREFIX="`$PKG_CONFIG --variable=prefix glib-2.0`" +AC_SUBST(GLIB_PREFIX) + +PKG_CHECK_MODULES(JSON, [gobject-2.0 >= glib_req_version gio-2.0]) +AC_SUBST(JSON_CFLAGS) +AC_SUBST(JSON_LIBS) + +AM_CONDITIONAL(ENABLE_GLIB_TEST, [test "x$enable_glibtest" = "xyes"]) + +dnl = Enable debug level ====================================================== + +m4_define([debug_default], [m4_if(m4_eval(json_minor_version % 2), [1], [yes], [minimum])]) + +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug=@<:@no/minimum/yes@:>@], + [turn on debugging @<:@default=]debug_default[@:>@])], + [], + [enable_debug=debug_default]) + +AS_CASE([$enable_debug], + + [yes], + [ + test "$cflags_set" = set || CFLAGS="$CFLAGS -g" + JSON_DEBUG_CFLAGS="-DJSON_ENABLE_DEBUG" + ], + + [minimum], + [ + JSON_DEBUG_CFLAGS="-DJSON_ENABLE_DEBUG -DG_DISABLE_CAST_CHECKS" + ], + + [no], + [ + JSON_DEBUG_CFLAGS="-DG_DISABLE_ASSERT -DG_DISABLE_CHECKS -DG_DISABLE_CAST_CHECKS" + ], + + [AC_MSG_ERROR([Unknown argument to --enable-debug])] +) + +AC_SUBST(JSON_DEBUG_CFLAGS) + +GETTEXT_PACKAGE="json-glib-1.0" +AC_SUBST(GETTEXT_PACKAGE) +AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], + ["$GETTEXT_PACKAGE"], + [The prefix for our gettext translation domains.]) + +AC_CONFIG_FILES([ + Makefile + build/Makefile + build/autotools/Makefile + json-glib/Makefile + json-glib/json-version.h + json-glib/tests/Makefile +]) + +AC_OUTPUT + +dnl === Summary =============================================================== + +echo "" +echo " Json-GLib - $VERSION (${JSON_RELEASE_STATUS})" +echo "" +echo " • Prefix: ${prefix}" +echo "" +echo " • Debug level: ${enable_debug}" +echo " • Compiler flags: ${CFLAGS}" +echo "" +echo "" +echo " • Enable test suite: ${enable_glibtest}" +echo "" diff --git a/json-glib/json-glib/Json-1.0.gir b/json-glib/json-glib/Json-1.0.gir new file mode 100644 index 0000000..7c11cf2 --- /dev/null +++ b/json-glib/json-glib/Json-1.0.gir @@ -0,0 +1,3702 @@ + + + + + + + + + + + A JSON array type. The contents of the #JsonArray structure are private +and should only be accessed by the provided API + + Creates a new #JsonArray. + + the newly created #JsonArray + + + + + Creates a new #JsonArray with @n_elements slots already allocated. + + the newly created #JsonArray + + + + + number of slots to pre-allocate + + + + + + Conveniently adds an array into @array. The @array takes ownership +of the newly added #JsonArray + +See also: json_array_add_element(), json_node_take_array() + + + + + + a #JsonArray + + + + + + Conveniently adds a boolean @value into @array + +See also: json_array_add_element(), json_node_set_boolean() + + + + + + a boolean value + + + + + + Conveniently adds a floating point @value into @array + +See also: json_array_add_element(), json_node_set_double() + + + + + + a floating point value + + + + + + Appends @node inside @array. The array will take ownership of the +#JsonNode. + + + + + + a #JsonNode + + + + + + Conveniently adds an integer @value into @array + +See also: json_array_add_element(), json_node_set_int() + + + + + + an integer value + + + + + + Conveniently adds a null element into @array + +See also: json_array_add_element(), %JSON_NODE_NULL + + + + + + Conveniently adds an object into @array. The @array takes ownership +of the newly added #JsonObject + +See also: json_array_add_element(), json_node_take_object() + + + + + + a #JsonObject + + + + + + Conveniently adds a string @value into @array + +See also: json_array_add_element(), json_node_set_string() + + + + + + a string value + + + + + + Retrieves a copy of the #JsonNode containing the value of the +element at @index_ inside a #JsonArray + +index. Use json_node_free() when done. + + a copy of the #JsonNode at the requested + + + + + the index of the element to retrieve + + + + + + Iterates over all elements of @array and calls @func on +each one of them. + +It is safe to change the value of a #JsonNode of the @array +from within the iterator @func, but it is not safe to add or +remove elements from the @array. + + + + + + the function to be called on each element + + + + data to be passed to the function + + + + + + Conveniently retrieves the array from the element at @index_ +inside @array + +See also: json_array_get_element(), json_node_get_array() + + the array + + + + + the index of the element to retrieve + + + + + + Conveniently retrieves the boolean value of the element at @index_ +inside @array + +See also: json_array_get_element(), json_node_get_boolean() + + the integer value + + + + + the index of the element to retrieve + + + + + + Conveniently retrieves the floating point value of the element at +@index_ inside @array + +See also: json_array_get_element(), json_node_get_double() + + the floating point value + + + + + the index of the element to retrieve + + + + + + Retrieves the #JsonNode containing the value of the element at @index_ +inside a #JsonArray. + + a pointer to the #JsonNode at the requested index + + + + + the index of the element to retrieve + + + + + + Gets the elements of a #JsonArray as a list of #JsonNode<!-- -->s. + +containing the elements of the array. The contents of the list are +owned by the array and should never be modified or freed. Use +g_list_free() on the returned list when done using it + + a #GList + + + + + + + Conveniently retrieves the integer value of the element at @index_ +inside @array + +See also: json_array_get_element(), json_node_get_int() + + the integer value + + + + + the index of the element to retrieve + + + + + + Retrieves the length of a #JsonArray + + the length of the array + + + + + Conveniently retrieves whether the element at @index_ is set to null + +See also: json_array_get_element(), JSON_NODE_TYPE(), %JSON_NODE_NULL + + %TRUE if the element is null + + + + + the index of the element to retrieve + + + + + + Conveniently retrieves the object from the element at @index_ +inside @array + +See also: json_array_get_element(), json_node_get_object() + + the object + + + + + the index of the element to retrieve + + + + + + Conveniently retrieves the string value of the element at @index_ +inside @array + +See also: json_array_get_element(), json_node_get_string() + +the #JsonArray and should not be modified or freed + + the string value; the returned string is owned by + + + + + the index of the element to retrieve + + + + + + Increase by one the reference count of a #JsonArray. + +increased by one. + + the passed #JsonArray, with the reference count + + + + + Removes the #JsonNode inside @array at @index_ freeing its allocated +resources. + + + + + + the position of the element to be removed + + + + + + Decreases by one the reference count of a #JsonArray. If the +reference count reaches zero, the array is destroyed and all +its allocated resources are freed. + + + + + + + The function to be passed to json_array_foreach_element(). You +should not add or remove elements to and from @array within +this function. It is safe to change the value of @element_node. + + + + + + the iterated #JsonArray + + + + the index of the element + + + + a #JsonNode containing the value at @index_ + + + + data passed to the function + + + + + + Deserializes the contents of the passed #JsonNode into a #GBoxed + + the newly created boxed type + + + + + a #JsonNode + + + + + + Serializes the passed #GBoxed and stores it inside a #JsonNode + + the newly created #JsonNode + + + + + a #GBoxed + + + + + + The <structname>JsonBuilder</structname> structure contains only +private data and shouls be accessed using the provided API + + Creates a new #JsonBuilder. You can use this object to generate a +JSON tree and obtain the root #JsonNode<!-- -->s. + + the newly created #JsonBuilder instance + + + + + If called after json_builder_set_member_name(), sets @value as member of the +most recent opened object, otherwise @value is added as element of the most +recent opened array. + +See also: json_builder_add_value() + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + the value of the member or element + + + + + + If called after json_builder_set_member_name(), sets @value as member of the +most recent opened object, otherwise @value is added as element of the most +recent opened array. + +See also: json_builder_add_value() + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + the value of the member or element + + + + + + If called after json_builder_set_member_name(), sets @value as member of the +most recent opened object, otherwise @value is added as element of the most +recent opened array. + +See also: json_builder_add_value() + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + the value of the member or element + + + + + + If called after json_builder_set_member_name(), sets null as member of the +most recent opened object, otherwise null is added as element of the most +recent opened array. + +See also: json_builder_add_value() + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + If called after json_builder_set_member_name(), sets @value as member of the +most recent opened object, otherwise @value is added as element of the most +recent opened array. + +See also: json_builder_add_value() + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + the value of the member or element + + + + + + If called after json_builder_set_member_name(), sets @node as member of the +most recent opened object, otherwise @node is added as element of the most +recent opened array. + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + the value of the member or element + + + + + + Opens a subarray inside the given @builder. When done adding members to +the subarray, json_builder_end_array() must be called. + +Can be called for first or only if the call is associated to an object member +or an array element. + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + Opens a subobject inside the given @builder. When done adding members to +the subobject, json_builder_end_object() must be called. + +Can be called for first or only if the call is associated to an object member +or an array element. + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + Closes the subarray inside the given @builder that was opened by the most +recent call to json_builder_begin_array(). + +Cannot be called after json_builder_set_member_name(). + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + Closes the subobject inside the given @builder that was opened by the most +recent call to json_builder_begin_object(). + +Cannot be called after json_builder_set_member_name(). + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + Returns the root of the current constructed tree, if the build is complete +(ie: all opened objects, object members and arrays are being closed). + +Free the returned value with json_node_free(). + + the #JsonNode, or %NULL if the build is not complete. + + + + + Resets the state of the @builder back to its initial state. + + + + + + Set the name of the next member in an object. The next call must add a value, +open an object or an array. + +Can be called only if the call is associated to an object. + + the #JsonBuilder, or %NULL if the call was inconsistent + + + + + the name of the member + + + + + + + + + + + + + The <structname>JsonBuilder</structname> structure contains only +private data + + + + + + + + + + + + + + + + + + + + + + JSON data streams generator. The contents of the #JsonGenerator structure +are private and should only be accessed via the provided API. + + Creates a new #JsonGenerator. You can use this object to generate a +JSON data stream starting from a data object model composed by +#JsonNode<!-- -->s. + + the newly created #JsonGenerator instance + + + + + Retrieves the value set using json_generator_set_indent(). + + the number of repetitions per indentation level + + + + + Retrieves the value set using json_generator_set_indent_char(). + + the character to be used when indenting + + + + + Retrieves the value set using json_generator_set_pretty(). + +%FALSE otherwise + + %TRUE if the generated JSON should be pretty-printed, and + + + + + Retrieves a pointer to the root #JsonNode set using +json_generator_set_root(). + +is owned by the #JsonGenerator and it should not be freed + + a #JsonNode, or %NULL. The returned node + + + + + Sets the number of repetitions for each indentation level. + + + + + + the number of repetitions of the indentation character that should be applied when pretty printing + + + + + + Sets the character to be used when indenting + + + + + + a Unicode character to be used when indenting + + + + + + Sets whether the generated JSON should be pretty printed, using the +indentation character specified in the #JsonGenerator:indent-char +property and the spacing specified in #JsonGenerator:indent property. + + + + + + whether the generated string should be pretty printed + + + + + + Sets @node as the root of the JSON data stream to be serialized by +the #JsonGenerator. + +<note>The node is copied by the generator object, so it can be safely +freed after calling this function.</note> + + + + + + a #JsonNode + + + + + + Generates a JSON data stream from @generator and returns it as a +buffer. + +Use g_free() to free the allocated resources. + + a newly allocated buffer holding a JSON data stream. + + + + + return location for the length of the returned buffer, or %NULL + + + + + + Creates a JSON data stream and puts it inside @filename, overwriting the +current file contents. This operation is atomic. + + %TRUE if saving was successful. + + + + + path to the target file + + + + + + Outputs JSON data and streams it (synchronously) to @stream. + +on failure. In case of error, the #GError will be filled accordingly + + %TRUE if the write operation was successful, and %FALSE + + + + + a #GOutputStream + + + + a #GCancellable, or %NULL + + + + + + Number of spaces to be used to indent when pretty printing. + + + + The character that should be used when indenting in pretty print. + + + + Whether the output should be "pretty-printed", with indentation and +newlines. The indentation level can be controlled by using the +JsonGenerator:indent property + + + + The root #JsonNode to be used when constructing a JSON data +stream. + + + + + + + + + + + #JsonGenerator class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A generic container of JSON data types. The contents of the #JsonNode +structure are private and should only be accessed via the provided +functions and never directly. + + Creates a new #JsonNode of @type. + + the newly created #JsonNode + + + + + a #JsonNodeType + + + + + + Copies @node. If the node contains complex data types then the reference +count of the objects is increased. + + the copied #JsonNode + + + + + Retrieves the #JsonArray stored inside a #JsonNode and returns it +with its reference count increased by one. + +count increased. + + the #JsonArray with its reference + + + + + Retrieves the #JsonObject inside @node. The reference count of +the returned object is increased. + + the #JsonObject + + + + + Gets a copy of the string value stored inside a #JsonNode + +of the #JsonNode contents. Use g_free() to free the allocated resources + + a newly allocated string containing a copy + + + + + Frees the resources allocated by @node. + + + + + + Retrieves the #JsonArray stored inside a #JsonNode + + the #JsonArray + + + + + Gets the boolean value stored inside a #JsonNode + + a boolean value. + + + + + Gets the double value stored inside a #JsonNode + + a double value. + + + + + Gets the integer value stored inside a #JsonNode + + an integer value. + + + + + Retrieves the #JsonNodeType of @node + + the type of the node + + + + + Retrieves the #JsonObject stored inside a #JsonNode + + the #JsonObject + + + + + Retrieves the parent #JsonNode of @node. + +the root node + + the parent node, or %NULL if @node is + + + + + Gets the string value stored inside a #JsonNode + + a string value. + + + + + Retrieves a value from a #JsonNode and copies into @value. When done +using it, call g_value_unset() on the #GValue. + + + + + + return location for an uninitialized value + + + + + + Returns the #GType of the payload of the node. + + a #GType for the payload. + + + + + Checks whether @node is a %JSON_NODE_NULL + +<note>A null node is not the same as a %NULL #JsonNode</note> + + %TRUE if the node is null + + + + + Sets @array inside @node and increases the #JsonArray reference count + + + + + + a #JsonArray + + + + + + Sets @value as the boolean content of the @node, replacing any existing +content. + + + + + + a boolean value + + + + + + Sets @value as the double content of the @node, replacing any existing +content. + + + + + + a double value + + + + + + Sets @value as the integer content of the @node, replacing any existing +content. + + + + + + an integer value + + + + + + Sets @objects inside @node. The reference count of @object is increased. + + + + + + a #JsonObject + + + + + + Sets the parent #JsonNode of @node + + + + + + the parent #JsonNode of @node + + + + + + Sets @value as the string content of the @node, replacing any existing +content. + + + + + + a string value + + + + + + Sets @value inside @node. The passed #GValue is copied into the #JsonNode + + + + + + the #GValue to set + + + + + + Sets @array into @node without increasing the #JsonArray reference count. + + + + + + a #JsonArray + + + + + + Sets @object inside @node. The reference count of @object is not increased. + + + + + + a #JsonObject + + + + + + Retrieves the user readable name of the data type contained by @node. + +is owned by the node and should never be modified or freed + + a string containing the name of the type. The returned string + + + + + + Indicates the content of a #JsonNode. + + + + + + + A JSON object type. The contents of the #JsonObject structure are private +and should only be accessed by the provided API + + Creates a new #JsonObject, an JSON object type representation. + + the newly created #JsonObject + + + + + Adds a member named @member_name and containing @node into a #JsonObject. +The object will take ownership of the #JsonNode. + +This function will return if the @object already contains a member +@member_name. + + + + + + the name of the member + + + + the value of the member + + + + + + Retrieves a copy of the #JsonNode containing the value of @member_name +inside a #JsonObject + +object member or %NULL. Use json_node_free() when done. + + a copy of the node for the requested + + + + + the name of the JSON object member to access + + + + + + Iterates over all members of @object and calls @func on +each one of them. + +It is safe to change the value of a #JsonNode of the @object +from within the iterator @func, but it is not safe to add or +remove members from the @object. + + + + + + the function to be called on each member + + + + data to be passed to the function + + + + + + Convenience function that retrieves the array +stored in @member_name of @object + +See also: json_object_get_member() + + the array inside the object's member + + + + + the name of the member + + + + + + Convenience function that retrieves the boolean value +stored in @member_name of @object + +See also: json_object_get_member() + + the boolean value of the object's member + + + + + the name of the member + + + + + + Convenience function that retrieves the floating point value +stored in @member_name of @object + +See also: json_object_get_member() + + the floating point value of the object's member + + + + + the name of the member + + + + + + Convenience function that retrieves the integer value +stored in @member_name of @object + +See also: json_object_get_member() + + the integer value of the object's member + + + + + the name of the member + + + + + + Retrieves the #JsonNode containing the value of @member_name inside +a #JsonObject. + +member, or %NULL + + a pointer to the node for the requested object + + + + + the name of the JSON object member to access + + + + + + Retrieves all the names of the members of a #JsonObject. You can +obtain the value for each member using json_object_get_member(). + +of member names. The content of the list is owned by the #JsonObject +and should never be modified or freed. When you have finished using +the returned list, use g_list_free() to free the resources it has +allocated. + + a #GList + + + + + + + Convenience function that checks whether the value +stored in @member_name of @object is null + +See also: json_object_get_member() + + %TRUE if the value is null + + + + + the name of the member + + + + + + Convenience function that retrieves the object +stored in @member_name of @object + +See also: json_object_get_member() + + the object inside the object's member + + + + + the name of the member + + + + + + Retrieves the number of members of a #JsonObject. + + the number of members + + + + + Convenience function that retrieves the string value +stored in @member_name of @object + +See also: json_object_get_member() + + the string value of the object's member + + + + + the name of the member + + + + + + Retrieves all the values of the members of a #JsonObject. + +#JsonNode<!-- -->s. The content of the list is owned by the #JsonObject +and should never be modified or freed. When you have finished using the +returned list, use g_list_free() to free the resources it has allocated. + + a #GList of + + + + + + + Checks whether @object has a member named @member_name. + + %TRUE if the JSON object has the requested member + + + + + the name of a JSON object member + + + + + + Increase by one the reference count of a #JsonObject. + +increased by one. + + the passed #JsonObject, with the reference count + + + + + Removes @member_name from @object, freeing its allocated resources. + + + + + + the name of the member to remove + + + + + + Convenience function for setting an array @value of +@member_name inside @object. + +The @object will take ownership of the passed #JsonArray + +See also: json_object_set_member() + + + + + + the name of the member + + + + the value of the member + + + + + + Convenience function for setting a boolean @value of +@member_name inside @object. + +See also: json_object_set_member() + + + + + + the name of the member + + + + the value of the member + + + + + + Convenience function for setting a floating point @value +of @member_name inside @object. + +See also: json_object_set_member() + + + + + + the name of the member + + + + the value of the member + + + + + + Convenience function for setting an integer @value of +@member_name inside @object. + +See also: json_object_set_member() + + + + + + the name of the member + + + + the value of the member + + + + + + Sets @node as the value of @member_name inside @object. + +If @object already contains a member called @member_name then +the member's current value is overwritten. Otherwise, a new +member is added to @object. + + + + + + the name of the member + + + + the value of the member + + + + + + Convenience function for setting a null @value of +@member_name inside @object. + +See also: json_object_set_member() + + + + + + the name of the member + + + + + + Convenience function for setting an object @value of +@member_name inside @object. + +The @object will take ownership of the passed #JsonObject + +See also: json_object_set_member() + + + + + + the name of the member + + + + the value of the member + + + + + + Convenience function for setting a string @value of +@member_name inside @object. + +See also: json_object_set_member() + + + + + + the name of the member + + + + the value of the member + + + + + + Decreases by one the reference count of a #JsonObject. If the +reference count reaches zero, the object is destroyed and all +its allocated resources are freed. + + + + + + + The function to be passed to json_object_foreach_member(). You +should not add or remove members to and from @object within +this function. It is safe to change the value of @member_node. + + + + + + the iterated #JsonObject + + + + the name of the member + + + + a #JsonNode containing the @member_name value + + + + data passed to the function + + + + + + JSON data streams parser. The contents of the #JsonParser structure are +private and should only be accessed via the provided API. + + Creates a new #JsonParser instance. You can use the #JsonParser to +load a JSON stream from either a file or a buffer and then walk the +hierarchy using the data types API. + +to release all the memory it allocates. + + the newly created #JsonParser. Use g_object_unref() + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Retrieves the line currently parsed, starting from 1. + +This function has defined behaviour only while parsing; calling this +function from outside the signal handlers emitted by #JsonParser will +yield 0. + + the currently parsed line, or 0. + + + + + Retrieves the current position inside the current line, starting +from 0. + +This function has defined behaviour only while parsing; calling this +function from outside the signal handlers emitted by #JsonParser will +yield 0. + + the position in the current line, or 0. + + + + + Retrieves the top level node from the parsed JSON stream. + +node is owned by the #JsonParser and should never be modified +or freed. + + the root #JsonNode . The returned + + + + + A JSON data stream might sometimes contain an assignment, like: + +|[ +var _json_data = { "member_name" : [ ... +]| + +even though it would technically constitute a violation of the RFC. + +#JsonParser will ignore the left hand identifier and parse the right +hand value of the assignment. #JsonParser will record, though, the +existence of the assignment in the data stream and the variable name +used. + +@variable_name is not %NULL it will be set to the name of the variable +used in the assignment. The string is owned by #JsonParser and should +never be modified or freed. + + %TRUE if there was an assignment, %FALSE otherwise. If + + + + + Return location for the variable name, or %NULL + + + + + + Loads a JSON stream from a buffer and parses it. You can call this function +multiple times with the same #JsonParser object, but the contents of the +parser will be destroyed each time. + +of error, @error is set accordingly and %FALSE is returned + + %TRUE if the buffer was succesfully parser. In case + + + + + the buffer to parse + + + + the length of the buffer, or -1 + + + + + + Loads a JSON stream from the content of @filename and parses it. See +json_parser_load_from_data(). + +In case of error, @error is set accordingly and %FALSE is returned + + %TRUE if the file was successfully loaded and parsed. + + + + + the path for the file to parse + + + + + + Loads the contents of an input stream and parses them. + +If @cancellable is not %NULL, then the operation can be cancelled by +triggering the @cancellable object from another thread. If the +operation was cancelled, the error %G_IO_ERROR_CANCELLED will be set +on the passed @error. + +parsed, and %FALSE otherwise + + %TRUE if the data stream was successfully read and + + + + + an open #GInputStream + + + + a #GCancellable, or %NULL + + + + + + Asynchronously reads the contents of @stream. + +For more details, see json_parser_load_from_stream() which is the +synchronous version of this call. + +When the operation is finished, @callback will be called. You should +then call json_parser_load_from_stream_finish() to get the result +of the operation. + + + + + + a #GInputStream + + + + a #GCancellable, or %NULL + + + + a #GAsyncReadyCallback to call when the request is satisfied + + + + the data to pass to @callback + + + + + + Finishes an asynchronous stream loading started with +json_parser_load_from_stream_async(). + +and parsed, and %FALSE otherwise. In case of error, the #GError will be +filled accordingly. + + %TRUE if the content of the stream was successfully retrieves + + + + + a #GAsyncResult + + + + + + + + + + + + The ::array-element signal is emitted each time the #JsonParser +has successfully parsed a single element of a #JsonArray. The +array and element index are passed to the signal handlers. + + + + + + a #JsonArray + + + + the index of the newly parsed element + + + + + + The ::array-end signal is emitted each time the #JsonParser +has successfully parsed an entire #JsonArray + + + + + + the parsed #JsonArray + + + + + + The ::array-start signal is emitted each time the #JsonParser +starts parsing a #JsonArray + + + + + + The ::error signal is emitted each time a #JsonParser encounters +an error in a JSON stream. + + + + + + a pointer to the #GError + + + + + + The ::object-end signal is emitted each time the #JsonParser +has successfully parsed an entire #JsonObject. + + + + + + the parsed #JsonObject + + + + + + The ::object-member signal is emitted each time the #JsonParser +has successfully parsed a single member of a #JsonObject. The +object and member are passed to the signal handlers. + + + + + + a #JsonObject + + + + the name of the newly parsed member + + + + + + The ::object-start signal is emitted each time the #JsonParser +starts parsing a #JsonObject. + + + + + + The ::parse-end signal is emitted when the parser successfully +finished parsing a JSON data stream + + + + + + The ::parse-start signal is emitted when the parser began parsing +a JSON data stream. + + + + + + + #JsonParser class. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Error enumeration for #JsonParser + +This enumeration can be extended at later date + + + + + + + + + + + The <structname>JsonPath</structname> structure is an opaque object +whose members cannot be directly accessed except through the provided +API. + + Creates a new #JsonPath instance. + +Once created, the #JsonPath object should be used with json_path_compile() +and json_path_match(). + +g_object_unref() to free the allocated resources when done + + the newly created #JsonPath instance. Use + + + + + + + + + + Queries a JSON tree using a JSONPath expression. + +This function is a simple wrapper around json_path_new(), +json_path_compile() and json_path_match(). It implicitly +creates a #JsonPath instance, compiles @expression and +matches it against the JSON tree pointed by @root. + +%JSON_NODE_ARRAY containing an array of matching #JsonNode<!-- -->s. +Use json_node_free() when done + + a newly-created #JsonNode of type + + + + + a JSONPath expression + + + + the root of a JSON tree + + + + + + Validates and decomposes @expression. + +A JSONPath expression must be compiled before calling json_path_match(). + +the %JSON_PATH_ERROR domain and a code from the #JsonPathError +enumeration, and %FALSE will be returned + + %TRUE on success; on error, @error will be set with + + + + + a JSONPath expression + + + + + + Matches the JSON tree pointed by @root using the expression compiled +into the #JsonPath. + +The matching #JsonNode<!-- -->s will be copied into a #JsonArray and +returned wrapped in a #JsonNode. + +%JSON_NODE_ARRAY containing an array of matching #JsonNode<!-- -->s. +Use json_node_free() when done + + a newly-created #JsonNode of type + + + + + a #JsonNode + + + + + + + The <structname>JsonPathClass</structname> structure is an opaque +object class whose members cannot be directly accessed. + + + Error code enumeration for the %JSON_PATH_ERROR domain. + + + + The <structname>JsonReader</structname> structure contains only +private data and should only be accessed using the provided API + + Creates a new #JsonReader. You can use this object to read the contents of +the JSON tree starting from @node + +release the allocated resources when done + + the newly created #JsonReader. Use g_object_unref() to + + + + + a #JsonNode, or %NULL + + + + + + + + + + + Counts the elements of the current position, if @reader is +positioned on an array + +the #JsonReader is set in an error state + + the number of elements, or -1. In case of failure + + + + + Counts the members of the current position, if @reader is +positioned on an object + +the #JsonReader is set in an error state + + the number of members, or -1. In case of failure + + + + + Moves the cursor back to the previous node after being positioned +inside an array + +This function resets the error state of @reader, if any was set + + + + + + Moves the cursor back to the previous node after being positioned +inside an object + +This function resets the error state of @reader, if any was set + + + + + + Retrieves the boolean value of the current position of @reader + + the boolean value + + + + + Retrieves the floating point value of the current position of @reader + + the floating point value + + + + + Retrieves the #GError currently set on @reader, if the #JsonReader +is in error state + + the pointer to the error, or %NULL + + + + + Retrieves the integer value of the current position of @reader + + the integer value + + + + + Retrieves the name of the current member. + + the name of the member, or %NULL + + + + + Checks whether the value of the current position of @reader is 'null' + + %TRUE if 'null' is set, and %FALSE otherwise + + + + + Retrieves the string value of the current position of @reader + + the string value + + + + + Retrieves the #JsonNode of the current position of @reader + +is owned by the #JsonReader and it should not be modified or freed +directly + + a #JsonNode, or %NULL. The returned node + + + + + Checks whether the @reader is currently on an array + +otherwise + + %TRUE if the #JsonReader is on an array, and %FALSE + + + + + Checks whether the @reader is currently on an object + +otherwise + + %TRUE if the #JsonReader is on an object, and %FALSE + + + + + Checks whether the @reader is currently on a value + +otherwise + + %TRUE if the #JsonReader is on a value, and %FALSE + + + + + Retrieves a list of member names from the current position, if @reader +is positioned on an object. + +array of strings holding the members name. Use g_strfreev() when +done. + + a newly allocated, %NULL-terminated + + + + + + + Advances the cursor of @reader to the element @index_ of the array +or the object at the current position. + +You can use the json_reader_get_value* family of functions to retrieve +the value of the element; for instance: + +|[ +json_reader_read_element (reader, 0); +int_value = json_reader_get_int_value (reader); +]| + +After reading the value, json_reader_end_element() should be called to +reposition the cursor inside the #JsonReader, e.g.: + +|[ +json_reader_read_element (reader, 1); +str_value = json_reader_get_string_value (reader); +json_reader_end_element (reader); + +json_reader_read_element (reader, 2); +str_value = json_reader_get_string_value (reader); +json_reader_end_element (reader); +]| + +If @reader is not currently on an array or an object, or if the @index_ is +bigger than the size of the array or the object, the #JsonReader will be +put in an error state until json_reader_end_element() is called. + + %TRUE on success, and %FALSE otherwise + + + + + the index of the element + + + + + + Advances the cursor of @reader to the @member_name of the object at the +current position. + +You can use the json_reader_get_value* family of functions to retrieve +the value of the member; for instance: + +|[ +json_reader_read_member (reader, "width"); +width = json_reader_get_int_value (reader); +]| + +After reading the value, json_reader_end_member() should be called to +reposition the cursor inside the #JsonReader, e.g.: + +|[ +json_reader_read_member (reader, "author"); +author = json_reader_get_string_value (reader); +json_reader_end_element (reader); + +json_reader_read_element (reader, "title"); +title = json_reader_get_string_value (reader); +json_reader_end_element (reader); +]| + +If @reader is not currently on an object, or if the @member_name is not +defined in the object, the #JsonReader will be put in an error state until +json_reader_end_member() is called. + + %TRUE on success, and %FALSE otherwise + + + + + the name of the member to read + + + + + + Sets the root #JsonNode to be read by @reader. The @reader will take +a copy of @root + +If another #JsonNode is currently set as root, it will be replaced. + + + + + + a #JsonNode + + + + + + The root of the JSON tree that the #JsonReader should read. + + + + + + + + + + + The <structname>JsonReaderClass</structname> structure contains only +private data + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Error codes enumeration for #JsonReader errors + + + + + + + + + + + + + Asks a #JsonSerializable implementation to deserialize the +property contained inside @property_node into @value. + + %TRUE if the property was successfully deserialized. + + + + + the name of the property + + + + a pointer to an uninitialized #GValue + + + + a #GParamSpec + + + + a #JsonNode containing the serialized property + + + + + + FIXME + +or %NULL if no property was found + + the #GParamSpec for the property + + + + + the name of the property + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Asks a #JsonSerializable implementation to serialize a #GObject +property into a #JsonNode object. + + a #JsonNode containing the serialized property + + + + + the name of the property + + + + the value of the property + + + + a #GParamSpec + + + + + + + + + + + + + + + + + + + Calls the default implementation of the #JsonSerializable +deserialize_property() virtual function + +This function can be used inside a custom implementation +of the deserialize_property() virtual function in lieu of: + +|[ +JsonSerializable *iface; +gboolean res; + +iface = g_type_default_interface_peek (JSON_TYPE_SERIALIZABLE); +res = iface->deserialize_property (serializable, property_name, +value, +pspec, +property_node); +]| + + %TRUE if the property was successfully deserialized. + + + + + the name of the property + + + + a pointer to an uninitialized #GValue + + + + a #GParamSpec + + + + a #JsonNode containing the serialized property + + + + + + Calls the default implementation of the #JsonSerializable +serialize_property() virtual function + +This function can be used inside a custom implementation +of the serialize_property() virtual function in lieu of: + +|[ +JsonSerializable *iface; +JsonNode *node; + +iface = g_type_default_interface_peek (JSON_TYPE_SERIALIZABLE); +node = iface->serialize_property (serializable, property_name, +value, +pspec); +]| + +property + + a #JsonNode containing the serialized + + + + + the name of the property + + + + the value of the property + + + + a #GParamSpec + + + + + + Asks a #JsonSerializable implementation to deserialize the +property contained inside @property_node into @value. + + %TRUE if the property was successfully deserialized. + + + + + the name of the property + + + + a pointer to an uninitialized #GValue + + + + a #GParamSpec + + + + a #JsonNode containing the serialized property + + + + + + FIXME + +or %NULL if no property was found + + the #GParamSpec for the property + + + + + the name of the property + + + + + + + + + + + + + + + + + + + FIXME + +of #GParamSpec. Use g_free() to free the array when done. + + an array + + + + + + + return location for the length of the array of #GParamSpec returned by the function + + + + + + Asks a #JsonSerializable implementation to serialize a #GObject +property into a #JsonNode object. + + a #JsonNode containing the serialized property + + + + + the name of the property + + + + the value of the property + + + + a #GParamSpec + + + + + + + + + + + + + + + + + + + + Interface that allows serializing and deserializing #GObject<!-- -->s +with properties storing complex data types. The json_serialize_gobject() +function will check if the passed #GObject implements this interface, +so it can also be used to override the default property serialization +sequence. + + + + + + + a #JsonNode containing the serialized property + + + + + + + + the name of the property + + + + the value of the property + + + + a #GParamSpec + + + + + + + + + %TRUE if the property was successfully deserialized. + + + + + + + + the name of the property + + + + a pointer to an uninitialized #GValue + + + + a #GParamSpec + + + + a #JsonNode containing the serialized property + + + + + + + + + the #GParamSpec for the property + + + + + + + + the name of the property + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Checks whether it is possible to deserialize a #GBoxed of +type @gboxed_type from a #JsonNode of type @node_type + + %TRUE if the type can be deserialized, %FALSE otherwise + + + + + a boxed type + + + + a #JsonNode type + + + + + + Checks whether it is possible to serialize a #GBoxed of +type @gboxed_type into a #JsonNode. The type of the +#JsonNode is placed inside @node_type if the function +returns %TRUE and it's undefined otherwise. + +and %FALSE otherwise. + + %TRUE if the type can be serialized, + + + + + a boxed type + + + + the #JsonNode type to which the boxed type can be serialized into + + + + + + Deserializes @node into a #GBoxed of @gboxed_type + +g_boxed_free() to release the resources allocated by this +function + + the newly allocated #GBoxed. Use + + + + + a boxed type + + + + a #JsonNode + + + + + + Registers a deserialization function for a #GBoxed of type @gboxed_type +from a #JsonNode of type @node_type + + + + + + a boxed type + + + + a node type + + + + deserialization function for @boxed_type from a #JsonNode of type @node_type + + + + + + Registers a serialization function for a #GBoxed of type @gboxed_type +to a #JsonNode of type @node_type + + + + + + a boxed type + + + + a node type + + + + serialization function for @boxed_type into a #JsonNode of type @node_type + + + + + + Serializes @boxed, a pointer to a #GBoxed of type @gboxed_type, +into a #JsonNode + +boxed type, or %NULL if serialization either failed or was not possible + + a #JsonNode with the serialization of the + + + + + a boxed type + + + + a pointer to a #GBoxed of type @gboxed_type + + + + + + Deserializes a JSON data stream and creates the corresponding +#GObject class. If @gtype implements the #JsonSerializableIface +interface, it will be asked to deserialize all the JSON members +into the respective properties; otherwise, the default implementation +will be used to translate the compatible JSON native types. + +Note: the JSON data stream must be an object declaration. + + a #GObject or %NULL + + + + + the #GType of object to construct + + + + a JSON data stream + + + + length of the data stream + + + + + + Creates a new #GObject of type @gtype, and constructs it +using the members of the passed #JsonObject + +instance. Use g_object_unref() to free the resources +allocated by this function + + The newly created #GObject + + + + + the type of the #GObject to create + + + + a #JsonNode of type %JSON_NODE_OBJECT describing the instance of type @gtype + + + + + + Deserializes a JSON data stream and creates the corresponding +#GObject class. If @gtype implements the #JsonSerializableIface +interface, it will be asked to deserialize all the JSON members +into the respective properties; otherwise, the default implementation +will be used to translate the compatible JSON native types. + +Note: the JSON data stream must be an object declaration. + + a #GObject or %NULL + + + + + the #GType of object to construct + + + + a JSON data stream + + + + length of the data stream, or -1 if it is NUL-terminated + + + + + + Creates a #JsonNode representing the passed #GObject +instance. Each member of the returned JSON object will +map to a property of the #GObject + +of type %JSON_NODE_OBJECT. Use json_node_free() to free +the resources allocated by this function + + the newly created #JsonNode + + + + + a #GObject + + + + + + Serializes a #GObject into a JSON data stream, iterating recursively +over each property. + +If @gobject implements the #JsonSerializableIface interface, it will +be asked to serialize all its properties; otherwise, the default +implementation will be use to translate the compatible types into +JSON native types. + + a JSON data stream representing the passed #GObject + + + + + a #GObject + + + + return value for the length of the buffer, or %NULL + + + + + + Converts a JSON data structure to a GVariant value using @signature to +resolve ambiguous data types. If no error occurs, the resulting #GVariant +is guaranteed to conform to @signature. + +If @signature is not %NULL but does not represent a valid GVariant type +string, %NULL is returned and error is set to %G_IO_ERROR_INVALID_ARGUMENT. +If a @signature is provided but the JSON structure cannot be mapped to it, +%NULL is returned and error is set to %G_IO_ERROR_INVALID_DATA. +If @signature is %NULL, the conversion is done based strictly on the types +in the JSON nodes. + +@signature, or %NULL on error + + A newly created #GVariant compliant with + + + + + A #JsonNode to convert + + + + A valid #GVariant type string, or %NULL + + + + + + Converts a JSON string to a #GVariant value. This method works exactly +like json_gvariant_deserialize(), but takes a JSON encoded string instead. +The string is first converted to a #JsonNode using #JsonParser, and then +json_gvariant_deserialize() is called. + +@signature, or %NULL on error + + A newly created #GVariant compliant with + + + + + A JSON data string + + + + The length of @json, or -1 if %NULL-terminated + + + + A valid #GVariant type string, or %NULL + + + + + + Converts @variant to a JSON tree. + +JSON data structure obtained from @variant + + A #JsonNode representing the root of the + + + + + A #GVariant to convert + + + + + + Converts @variant to its JSON encoded string representation. This method +is actually a helper function. It uses json_gvariant_serialize() to obtain the +JSON tree, and then #JsonGenerator to stringify it. + +@variant + + The JSON encoded string corresponding to + + + + + A #GVariant to convert + + + + Return location for the length of the returned string, or %NULL + + + + + + Serializes a #GObject into a JSON data stream. If @gobject implements +the #JsonSerializableIface interface, it will be asked to serizalize all +its properties; otherwise, the default implementation will be use to +translate the compatible types into JSON native types. + + a JSON data stream representing the passed #GObject + + + + + a #GObject + + + + return value for the length of the buffer, or %NULL + + + + + + diff --git a/json-glib/json-glib/Makefile.am b/json-glib/json-glib/Makefile.am new file mode 100644 index 0000000..4120276 --- /dev/null +++ b/json-glib/json-glib/Makefile.am @@ -0,0 +1,95 @@ +include $(top_srcdir)/build/autotools/Makefile.am.silent +include $(top_srcdir)/build/autotools/Makefile.am.gtest + +if ENABLE_GLIB_TEST +# build this directory *before* the tests/ +SUBDIRS = . tests +endif + +DIST_SUBDIRS = tests + +NULL = + +INCLUDES = -I$(top_srcdir) + +AM_CPPFLAGS = \ + -DPREFIX=\""$(prefix)"\" \ + -DLIBDIR=\""$(libdir)"\" \ + -DJSON_COMPILATION=1 \ + -DJSON_DISABLE_DEPRECATED \ + -DG_LOG_DOMAIN=\"Json\" \ + $(JSON_DEBUG_CFLAGS) \ + $(NULL) + +AM_CFLAGS = $(JSON_CFLAGS) $(MAINTAINER_CFLAGS) -fPIC + +BUILT_SOURCES = + +CLEANFILES = +DISTCLEANFILES = json-version.h + +source_h = \ + $(top_srcdir)/json-glib/json-builder.h \ + $(top_srcdir)/json-glib/json-generator.h \ + $(top_srcdir)/json-glib/json-gobject.h \ + $(top_srcdir)/json-glib/json-parser.h \ + $(top_srcdir)/json-glib/json-path.h \ + $(top_srcdir)/json-glib/json-reader.h \ + $(top_srcdir)/json-glib/json-types.h \ + $(top_srcdir)/json-glib/json-gvariant.h \ + $(NULL) + +source_h_private = \ + $(top_srcdir)/json-glib/json-debug.h \ + $(top_srcdir)/json-glib/json-gobject-private.h \ + $(top_srcdir)/json-glib/json-scanner.h \ + $(top_srcdir)/json-glib/json-types-private.h \ + $(NULL) + +source_c = \ + $(srcdir)/json-array.c \ + $(srcdir)/json-builder.c \ + $(srcdir)/json-debug.c \ + $(srcdir)/json-gboxed.c \ + $(srcdir)/json-generator.c \ + $(srcdir)/json-gobject.c \ + $(srcdir)/json-node.c \ + $(srcdir)/json-object.c \ + $(srcdir)/json-parser.c \ + $(srcdir)/json-path.c \ + $(srcdir)/json-reader.c \ + $(srcdir)/json-scanner.c \ + $(srcdir)/json-serializable.c \ + $(srcdir)/json-gvariant.c \ + $(NULL) + +# glib-mkenums rules +glib_enum_h = json-enum-types.h +glib_enum_c = json-enum-types.c +glib_enum_headers = $(source_h) +include $(top_srcdir)/build/autotools/Makefile.am.enums + +# glib-genmarshal rules +glib_marshal_list = json-marshal.list +glib_marshal_prefix = _json_marshal +include $(top_srcdir)/build/autotools/Makefile.am.marshal + +noinst_LTLIBRARIES = libjson-glib.la + +libjson_glib_la_LIBADD = $(JSON_LIBS) +libjson_glib_la_SOURCES = $(source_c) $(source_h) $(source_h_private) $(BUILT_SOURCES) +libjson_glib_la_LDFLAGS = $(JSON_LT_LDFLAGS) -export-dynamic -export-symbols-regex "^json.*" -rpath $(libdir) + +jsonincludedir = $(includedir)/searpc/json-glib +jsoninclude_DATA = \ + $(source_h) \ + $(top_builddir)/json-glib/json-enum-types.h \ + $(top_builddir)/json-glib/json-version.h \ + $(top_srcdir)/json-glib/json-glib.h \ + $(NULL) + +EXTRA_DIST += json-version.h.in json-glib.h json-version.h + +TESTS_ENVIRONMENT = srcdir="$(srcdir)" json_all_c_sources="$(source_c)" + +EXTRA_DIST += json-glib.symbols diff --git a/json-glib/json-glib/json-array.c b/json-glib/json-glib/json-array.c new file mode 100644 index 0000000..44caf1e --- /dev/null +++ b/json-glib/json-glib/json-array.c @@ -0,0 +1,709 @@ +/* json-array.c - JSON array implementation + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "json-types-private.h" + +/** + * SECTION:json-array + * @short_description: a JSON array representation + * + * #JsonArray is the representation of the array type inside JSON. It contains + * #JsonNodes, which may contain fundamental types, other arrays or + * objects. + * + * Since arrays can be expensive, they are reference counted. You can control + * the lifetime of a #JsonArray using json_array_ref() and json_array_unref(). + * + * To append an element, use json_array_add_element(). + * To extract an element at a given index, use json_array_get_element(). + * To retrieve the entire array in list form, use json_array_get_elements(). + * To retrieve the length of the array, use json_array_get_length(). + */ + +G_DEFINE_BOXED_TYPE (JsonArray, json_array, json_array_ref, json_array_unref); + +/** + * json_array_new: + * + * Creates a new #JsonArray. + * + * Return value: the newly created #JsonArray + */ +JsonArray * +json_array_new (void) +{ + JsonArray *array; + + array = g_slice_new (JsonArray); + + array->ref_count = 1; + array->elements = g_ptr_array_new (); + + return array; +} + +/** + * json_array_sized_new: + * @n_elements: number of slots to pre-allocate + * + * Creates a new #JsonArray with @n_elements slots already allocated. + * + * Return value: the newly created #JsonArray + */ +JsonArray * +json_array_sized_new (guint n_elements) +{ + JsonArray *array; + + array = g_slice_new (JsonArray); + + array->ref_count = 1; + array->elements = g_ptr_array_sized_new (n_elements); + + return array; +} + +/** + * json_array_ref: + * @array: a #JsonArray + * + * Increase by one the reference count of a #JsonArray. + * + * Return value: the passed #JsonArray, with the reference count + * increased by one. + */ +JsonArray * +json_array_ref (JsonArray *array) +{ + g_return_val_if_fail (array != NULL, NULL); + g_return_val_if_fail (array->ref_count > 0, NULL); + + g_atomic_int_add (&array->ref_count, 1); + + return array; +} + +/** + * json_array_unref: + * @array: a #JsonArray + * + * Decreases by one the reference count of a #JsonArray. If the + * reference count reaches zero, the array is destroyed and all + * its allocated resources are freed. + */ +void +json_array_unref (JsonArray *array) +{ + g_return_if_fail (array != NULL); + g_return_if_fail (array->ref_count > 0); + + if (g_atomic_int_dec_and_test (&array->ref_count)) + { + guint i; + + for (i = 0; i < array->elements->len; i++) + json_node_free (g_ptr_array_index (array->elements, i)); + + g_ptr_array_free (array->elements, TRUE); + array->elements = NULL; + + g_slice_free (JsonArray, array); + } +} + +/** + * json_array_get_elements: + * @array: a #JsonArray + * + * Gets the elements of a #JsonArray as a list of #JsonNodes. + * + * Return value: (element-type JsonNode) (transfer container): a #GList + * containing the elements of the array. The contents of the list are + * owned by the array and should never be modified or freed. Use + * g_list_free() on the returned list when done using it + */ +GList * +json_array_get_elements (JsonArray *array) +{ + GList *retval; + guint i; + + g_return_val_if_fail (array != NULL, NULL); + + retval = NULL; + for (i = 0; i < array->elements->len; i++) + retval = g_list_prepend (retval, + g_ptr_array_index (array->elements, i)); + + return g_list_reverse (retval); +} + +/** + * json_array_dup_element: + * @array: a #JsonArray + * @index_: the index of the element to retrieve + * + * Retrieves a copy of the #JsonNode containing the value of the + * element at @index_ inside a #JsonArray + * + * Return value: (transfer full): a copy of the #JsonNode at the requested + * index. Use json_node_free() when done. + * + * Since: 0.6 + */ +JsonNode * +json_array_dup_element (JsonArray *array, + guint index_) +{ + JsonNode *retval; + + g_return_val_if_fail (array != NULL, NULL); + g_return_val_if_fail (index_ < array->elements->len, NULL); + + retval = json_array_get_element (array, index_); + if (!retval) + return NULL; + + return json_node_copy (retval); +} + +/** + * json_array_get_element: + * @array: a #JsonArray + * @index_: the index of the element to retrieve + * + * Retrieves the #JsonNode containing the value of the element at @index_ + * inside a #JsonArray. + * + * Return value: (transfer none): a pointer to the #JsonNode at the requested index + */ +JsonNode * +json_array_get_element (JsonArray *array, + guint index_) +{ + g_return_val_if_fail (array != NULL, NULL); + g_return_val_if_fail (index_ < array->elements->len, NULL); + + return g_ptr_array_index (array->elements, index_); +} + +/** + * json_array_get_int_element: + * @array: a #JsonArray + * @index_: the index of the element to retrieve + * + * Conveniently retrieves the integer value of the element at @index_ + * inside @array + * + * See also: json_array_get_element(), json_node_get_int() + * + * Return value: the integer value + * + * Since: 0.8 + */ +gint64 +json_array_get_int_element (JsonArray *array, + guint index_) +{ + JsonNode *node; + + g_return_val_if_fail (array != NULL, 0); + g_return_val_if_fail (index_ < array->elements->len, 0); + + node = g_ptr_array_index (array->elements, index_); + g_return_val_if_fail (node != NULL, 0); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0); + + return json_node_get_int (node); +} + +/** + * json_array_get_double_element: + * @array: a #JsonArray + * @index_: the index of the element to retrieve + * + * Conveniently retrieves the floating point value of the element at + * @index_ inside @array + * + * See also: json_array_get_element(), json_node_get_double() + * + * Return value: the floating point value + * + * Since: 0.8 + */ +gdouble +json_array_get_double_element (JsonArray *array, + guint index_) +{ + JsonNode *node; + + g_return_val_if_fail (array != NULL, 0.0); + g_return_val_if_fail (index_ < array->elements->len, 0.0); + + node = g_ptr_array_index (array->elements, index_); + g_return_val_if_fail (node != NULL, 0.0); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0.0); + + return json_node_get_double (node); +} + +/** + * json_array_get_boolean_element: + * @array: a #JsonArray + * @index_: the index of the element to retrieve + * + * Conveniently retrieves the boolean value of the element at @index_ + * inside @array + * + * See also: json_array_get_element(), json_node_get_boolean() + * + * Return value: the integer value + * + * Since: 0.8 + */ +gboolean +json_array_get_boolean_element (JsonArray *array, + guint index_) +{ + JsonNode *node; + + g_return_val_if_fail (array != NULL, FALSE); + g_return_val_if_fail (index_ < array->elements->len, FALSE); + + node = g_ptr_array_index (array->elements, index_); + g_return_val_if_fail (node != NULL, FALSE); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, FALSE); + + return json_node_get_boolean (node); +} + +/** + * json_array_get_string_element: + * @array: a #JsonArray + * @index_: the index of the element to retrieve + * + * Conveniently retrieves the string value of the element at @index_ + * inside @array + * + * See also: json_array_get_element(), json_node_get_string() + * + * Return value: the string value; the returned string is owned by + * the #JsonArray and should not be modified or freed + * + * Since: 0.8 + */ +const gchar * +json_array_get_string_element (JsonArray *array, + guint index_) +{ + JsonNode *node; + + g_return_val_if_fail (array != NULL, NULL); + g_return_val_if_fail (index_ < array->elements->len, NULL); + + node = g_ptr_array_index (array->elements, index_); + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_HOLDS_VALUE (node) || JSON_NODE_HOLDS_NULL (node), NULL); + + if (JSON_NODE_HOLDS_NULL (node)) + return NULL; + + return json_node_get_string (node); +} + +/** + * json_array_get_null_element: + * @array: a #JsonArray + * @index_: the index of the element to retrieve + * + * Conveniently retrieves whether the element at @index_ is set to null + * + * See also: json_array_get_element(), JSON_NODE_TYPE(), %JSON_NODE_NULL + * + * Return value: %TRUE if the element is null + * + * Since: 0.8 + */ +gboolean +json_array_get_null_element (JsonArray *array, + guint index_) +{ + JsonNode *node; + + g_return_val_if_fail (array != NULL, FALSE); + g_return_val_if_fail (index_ < array->elements->len, FALSE); + + node = g_ptr_array_index (array->elements, index_); + g_return_val_if_fail (node != NULL, FALSE); + + return JSON_NODE_TYPE (node) == JSON_NODE_NULL; +} + +/** + * json_array_get_array_element: + * @array: a #JsonArray + * @index_: the index of the element to retrieve + * + * Conveniently retrieves the array from the element at @index_ + * inside @array + * + * See also: json_array_get_element(), json_node_get_array() + * + * Return value: (transfer none): the array + * + * Since: 0.8 + */ +JsonArray * +json_array_get_array_element (JsonArray *array, + guint index_) +{ + JsonNode *node; + + g_return_val_if_fail (array != NULL, NULL); + g_return_val_if_fail (index_ < array->elements->len, NULL); + + node = g_ptr_array_index (array->elements, index_); + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_HOLDS_ARRAY (node) || JSON_NODE_HOLDS_NULL (node), NULL); + + if (JSON_NODE_HOLDS_NULL (node)) + return NULL; + + return json_node_get_array (node); +} + +/** + * json_array_get_object_element: + * @array: a #JsonArray + * @index_: the index of the element to retrieve + * + * Conveniently retrieves the object from the element at @index_ + * inside @array + * + * See also: json_array_get_element(), json_node_get_object() + * + * Return value: (transfer none): the object + * + * Since: 0.8 + */ +JsonObject * +json_array_get_object_element (JsonArray *array, + guint index_) +{ + JsonNode *node; + + g_return_val_if_fail (array != NULL, NULL); + g_return_val_if_fail (index_ < array->elements->len, NULL); + + node = g_ptr_array_index (array->elements, index_); + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (node) || JSON_NODE_HOLDS_NULL (node), NULL); + + if (JSON_NODE_HOLDS_NULL (node)) + return NULL; + + return json_node_get_object (node); +} + +/** + * json_array_get_length: + * @array: a #JsonArray + * + * Retrieves the length of a #JsonArray + * + * Return value: the length of the array + */ +guint +json_array_get_length (JsonArray *array) +{ + g_return_val_if_fail (array != NULL, 0); + + return array->elements->len; +} + +/** + * json_array_add_element: + * @array: a #JsonArray + * @node: (transfer full): a #JsonNode + * + * Appends @node inside @array. The array will take ownership of the + * #JsonNode. + */ +void +json_array_add_element (JsonArray *array, + JsonNode *node) +{ + g_return_if_fail (array != NULL); + g_return_if_fail (node != NULL); + + g_ptr_array_add (array->elements, node); +} + +/** + * json_array_add_int_element: + * @array: a #JsonArray + * @value: an integer value + * + * Conveniently adds an integer @value into @array + * + * See also: json_array_add_element(), json_node_set_int() + * + * Since: 0.8 + */ +void +json_array_add_int_element (JsonArray *array, + gint64 value) +{ + JsonNode *node; + + g_return_if_fail (array != NULL); + + node = json_node_new (JSON_NODE_VALUE); + json_node_set_int (node, value); + + g_ptr_array_add (array->elements, node); +} + +/** + * json_array_add_double_element: + * @array: a #JsonArray + * @value: a floating point value + * + * Conveniently adds a floating point @value into @array + * + * See also: json_array_add_element(), json_node_set_double() + * + * Since: 0.8 + */ +void +json_array_add_double_element (JsonArray *array, + gdouble value) +{ + JsonNode *node; + + g_return_if_fail (array != NULL); + + node = json_node_new (JSON_NODE_VALUE); + json_node_set_double (node, value); + + g_ptr_array_add (array->elements, node); +} + +/** + * json_array_add_boolean_element: + * @array: a #JsonArray + * @value: a boolean value + * + * Conveniently adds a boolean @value into @array + * + * See also: json_array_add_element(), json_node_set_boolean() + * + * Since: 0.8 + */ +void +json_array_add_boolean_element (JsonArray *array, + gboolean value) +{ + JsonNode *node; + + g_return_if_fail (array != NULL); + + node = json_node_new (JSON_NODE_VALUE); + json_node_set_boolean (node, value); + + g_ptr_array_add (array->elements, node); +} + +/** + * json_array_add_string_element: + * @array: a #JsonArray + * @value: a string value + * + * Conveniently adds a string @value into @array + * + * See also: json_array_add_element(), json_node_set_string() + * + * Since: 0.8 + */ +void +json_array_add_string_element (JsonArray *array, + const gchar *value) +{ + JsonNode *node; + + g_return_if_fail (array != NULL); + g_return_if_fail (value != NULL); + + if (value != NULL) + { + node = json_node_new (JSON_NODE_VALUE); + json_node_set_string (node, value); + } + else + node = json_node_new (JSON_NODE_NULL); + + g_ptr_array_add (array->elements, node); +} + +/** + * json_array_add_null_element: + * @array: a #JsonArray + * + * Conveniently adds a null element into @array + * + * See also: json_array_add_element(), %JSON_NODE_NULL + * + * Since: 0.8 + */ +void +json_array_add_null_element (JsonArray *array) +{ + JsonNode *node; + + g_return_if_fail (array != NULL); + + node = json_node_new (JSON_NODE_NULL); + + g_ptr_array_add (array->elements, node); +} + +/** + * json_array_add_array_element: + * @array: a #JsonArray + * @value: (transfer full): a #JsonArray + * + * Conveniently adds an array into @array. The @array takes ownership + * of the newly added #JsonArray + * + * See also: json_array_add_element(), json_node_take_array() + * + * Since: 0.8 + */ +void +json_array_add_array_element (JsonArray *array, + JsonArray *value) +{ + JsonNode *node; + + g_return_if_fail (array != NULL); + g_return_if_fail (value != NULL); + + if (value != NULL) + { + node = json_node_new (JSON_NODE_ARRAY); + json_node_take_array (node, value); + } + else + node = json_node_new (JSON_NODE_NULL); + + g_ptr_array_add (array->elements, node); +} + +/** + * json_array_add_object_element: + * @array: a #JsonArray + * @value: (transfer full): a #JsonObject + * + * Conveniently adds an object into @array. The @array takes ownership + * of the newly added #JsonObject + * + * See also: json_array_add_element(), json_node_take_object() + * + * Since: 0.8 + */ +void +json_array_add_object_element (JsonArray *array, + JsonObject *value) +{ + JsonNode *node; + + g_return_if_fail (array != NULL); + g_return_if_fail (value != NULL); + + if (value != NULL) + { + node = json_node_new (JSON_NODE_OBJECT); + json_node_take_object (node, value); + } + else + node = json_node_new (JSON_NODE_NULL); + + g_ptr_array_add (array->elements, node); +} + +/** + * json_array_remove_element: + * @array: a #JsonArray + * @index_: the position of the element to be removed + * + * Removes the #JsonNode inside @array at @index_ freeing its allocated + * resources. + */ +void +json_array_remove_element (JsonArray *array, + guint index_) +{ + g_return_if_fail (array != NULL); + g_return_if_fail (index_ < array->elements->len); + + json_node_free (g_ptr_array_remove_index (array->elements, index_)); +} + +/** + * json_array_foreach_element: + * @array: a #JsonArray + * @func: (scope call): the function to be called on each element + * @data: (closure): data to be passed to the function + * + * Iterates over all elements of @array and calls @func on + * each one of them. + * + * It is safe to change the value of a #JsonNode of the @array + * from within the iterator @func, but it is not safe to add or + * remove elements from the @array. + * + * Since: 0.8 + */ +void +json_array_foreach_element (JsonArray *array, + JsonArrayForeach func, + gpointer data) +{ + gint i; + + g_return_if_fail (array != NULL); + g_return_if_fail (func != NULL); + + for (i = 0; i < array->elements->len; i++) + { + JsonNode *element_node; + + element_node = g_ptr_array_index (array->elements, i); + + (* func) (array, i, element_node, data); + } +} diff --git a/json-glib/json-glib/json-builder.c b/json-glib/json-glib/json-builder.c new file mode 100644 index 0000000..9fa6859 --- /dev/null +++ b/json-glib/json-glib/json-builder.c @@ -0,0 +1,684 @@ +/* json-generator.c - JSON tree builder + * + * This file is part of JSON-GLib + * Copyright (C) 2010 Luca Bruno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Luca Bruno + */ + +/** + * SECTION:json-builder + * @Title: JsonBuilder + * @short_description: Generates JSON trees + * @See_Also: JsonGenerator + * + * #JsonBuilder provides an object for generating a JSON tree. + * You can generate only one tree with one #JsonBuilder instance. + * + * The root of the JSON tree can be either a #JsonObject or a #JsonArray. + * Thus the first call must necessarily be either + * json_builder_begin_object() or json_builder_begin_array(). + * + * For convenience to language bindings, #JsonBuilder returns itself from + * most of functions, making it easy to chain function calls. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "json-types-private.h" + +#include "json-builder.h" + +#define JSON_BUILDER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), JSON_TYPE_BUILDER, JsonBuilderPrivate)) + +struct _JsonBuilderPrivate +{ + GQueue *stack; + JsonNode *root; +}; + +typedef enum +{ + JSON_BUILDER_MODE_OBJECT, + JSON_BUILDER_MODE_ARRAY, + JSON_BUILDER_MODE_MEMBER +} JsonBuilderMode; + +typedef struct +{ + JsonBuilderMode mode; + + union + { + JsonObject *object; + JsonArray *array; + } data; + gchar *member_name; +} JsonBuilderState; + +static void +json_builder_state_free (JsonBuilderState *state) +{ + if (G_LIKELY (state)) + { + switch (state->mode) + { + case JSON_BUILDER_MODE_OBJECT: + case JSON_BUILDER_MODE_MEMBER: + json_object_unref (state->data.object); + g_free (state->member_name); + state->data.object = NULL; + state->member_name = NULL; + break; + case JSON_BUILDER_MODE_ARRAY: + json_array_unref (state->data.array); + state->data.array = NULL; + break; + default: + g_assert_not_reached (); + } + + g_slice_free (JsonBuilderState, state); + } +} + +G_DEFINE_TYPE (JsonBuilder, json_builder, G_TYPE_OBJECT); + +static void +json_builder_free_all_state (JsonBuilder *builder) +{ + JsonBuilderState *state; + + while (!g_queue_is_empty (builder->priv->stack)) + { + state = g_queue_pop_head (builder->priv->stack); + json_builder_state_free (state); + } + + if (builder->priv->root) + { + json_node_free (builder->priv->root); + builder->priv->root = NULL; + } +} + +static void +json_builder_finalize (GObject *gobject) +{ + JsonBuilderPrivate *priv = JSON_BUILDER_GET_PRIVATE (gobject); + + json_builder_free_all_state (JSON_BUILDER (gobject)); + + g_queue_free (priv->stack); + priv->stack = NULL; + + G_OBJECT_CLASS (json_builder_parent_class)->finalize (gobject); +} + +static void +json_builder_class_init (JsonBuilderClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (JsonBuilderPrivate)); + + gobject_class->finalize = json_builder_finalize; +} + +static void +json_builder_init (JsonBuilder *builder) +{ + JsonBuilderPrivate *priv; + + builder->priv = priv = JSON_BUILDER_GET_PRIVATE (builder); + + priv->stack = g_queue_new (); + priv->root = NULL; +} + +static inline JsonBuilderMode +json_builder_current_mode (JsonBuilder *builder) +{ + JsonBuilderState *state = g_queue_peek_head (builder->priv->stack); + return state->mode; +} + +static inline gboolean +json_builder_is_valid_add_mode (JsonBuilder *builder) +{ + JsonBuilderMode mode = json_builder_current_mode (builder); + return mode == JSON_BUILDER_MODE_MEMBER || mode == JSON_BUILDER_MODE_ARRAY; +} + +/** + * json_builder_new: + * + * Creates a new #JsonBuilder. You can use this object to generate a + * JSON tree and obtain the root #JsonNodes. + * + * Return value: the newly created #JsonBuilder instance + */ +JsonBuilder * +json_builder_new (void) +{ + return g_object_new (JSON_TYPE_BUILDER, NULL); +} + +/** + * json_builder_get_root: + * @builder: a #JsonBuilder + * + * Returns the root of the current constructed tree, if the build is complete + * (ie: all opened objects, object members and arrays are being closed). + * + * Return value: (transfer full): the #JsonNode, or %NULL if the build is not complete. + * Free the returned value with json_node_free(). + */ +JsonNode * +json_builder_get_root (JsonBuilder *builder) +{ + JsonNode *root = NULL; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + + if (builder->priv->root) + root = json_node_copy (builder->priv->root); + + return root; +} + +/** + * json_builder_reset: + * @builder: a #JsonBuilder + * + * Resets the state of the @builder back to its initial state. + */ +void +json_builder_reset (JsonBuilder *builder) +{ + g_return_if_fail (JSON_IS_BUILDER (builder)); + + json_builder_free_all_state (builder); +} + +/** + * json_builder_begin_object: + * @builder: a #JsonBuilder + * + * Opens a subobject inside the given @builder. When done adding members to + * the subobject, json_builder_end_object() must be called. + * + * Can be called for first or only if the call is associated to an object member + * or an array element. + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_begin_object (JsonBuilder *builder) +{ + JsonObject *object; + JsonBuilderState *state; + JsonBuilderState *cur_state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (builder->priv->root == NULL, NULL); + g_return_val_if_fail (g_queue_is_empty (builder->priv->stack) || json_builder_is_valid_add_mode (builder), NULL); + + object = json_object_new (); + cur_state = g_queue_peek_head (builder->priv->stack); + if (cur_state) + { + switch (cur_state->mode) + { + case JSON_BUILDER_MODE_ARRAY: + json_array_add_object_element (cur_state->data.array, json_object_ref (object)); + break; + + case JSON_BUILDER_MODE_MEMBER: + json_object_set_object_member (cur_state->data.object, cur_state->member_name, json_object_ref (object)); + g_free (cur_state->member_name); + cur_state->member_name = NULL; + cur_state->mode = JSON_BUILDER_MODE_OBJECT; + break; + + default: + g_assert_not_reached (); + } + } + + state = g_slice_new (JsonBuilderState); + state->data.object = object; + state->member_name = NULL; + state->mode = JSON_BUILDER_MODE_OBJECT; + g_queue_push_head (builder->priv->stack, state); + + return builder; +} + +/** + * json_builder_end_object: + * @builder: a #JsonBuilder + * + * Closes the subobject inside the given @builder that was opened by the most + * recent call to json_builder_begin_object(). + * + * Cannot be called after json_builder_set_member_name(). + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_end_object (JsonBuilder *builder) +{ + JsonBuilderState *state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL); + g_return_val_if_fail (json_builder_current_mode (builder) == JSON_BUILDER_MODE_OBJECT, NULL); + + state = g_queue_pop_head (builder->priv->stack); + + if (g_queue_is_empty (builder->priv->stack)) + { + builder->priv->root = json_node_new (JSON_NODE_OBJECT); + json_node_take_object (builder->priv->root, json_object_ref (state->data.object)); + } + + json_builder_state_free (state); + + return builder; +} + +/** + * json_builder_begin_array: + * @builder: a #JsonBuilder + * + * Opens a subarray inside the given @builder. When done adding members to + * the subarray, json_builder_end_array() must be called. + * + * Can be called for first or only if the call is associated to an object member + * or an array element. + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_begin_array (JsonBuilder *builder) +{ + JsonArray *array; + JsonBuilderState *state; + JsonBuilderState *cur_state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (builder->priv->root == NULL, NULL); + g_return_val_if_fail (g_queue_is_empty (builder->priv->stack) || json_builder_is_valid_add_mode (builder), NULL); + + array = json_array_new (); + cur_state = g_queue_peek_head (builder->priv->stack); + if (cur_state) + { + switch (cur_state->mode) + { + case JSON_BUILDER_MODE_ARRAY: + json_array_add_array_element (cur_state->data.array, json_array_ref (array)); + break; + + case JSON_BUILDER_MODE_MEMBER: + json_object_set_array_member (cur_state->data.object, cur_state->member_name, json_array_ref (array)); + g_free (cur_state->member_name); + cur_state->member_name = NULL; + cur_state->mode = JSON_BUILDER_MODE_OBJECT; + break; + + default: + g_assert_not_reached (); + } + } + + state = g_slice_new (JsonBuilderState); + state->data.array = array; + state->mode = JSON_BUILDER_MODE_ARRAY; + g_queue_push_head (builder->priv->stack, state); + + return builder; +} + +/** + * json_builder_end_array: + * @builder: a #JsonBuilder + * + * Closes the subarray inside the given @builder that was opened by the most + * recent call to json_builder_begin_array(). + * + * Cannot be called after json_builder_set_member_name(). + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_end_array (JsonBuilder *builder) +{ + JsonBuilderState *state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL); + g_return_val_if_fail (json_builder_current_mode (builder) == JSON_BUILDER_MODE_ARRAY, NULL); + + state = g_queue_pop_head (builder->priv->stack); + + if (g_queue_is_empty (builder->priv->stack)) + { + builder->priv->root = json_node_new (JSON_NODE_ARRAY); + json_node_take_array (builder->priv->root, json_array_ref (state->data.array)); + } + + json_builder_state_free (state); + + return builder; +} + +/** + * json_builder_set_member_name: + * @builder: a #JsonBuilder + * @member_name: the name of the member + * + * Set the name of the next member in an object. The next call must add a value, + * open an object or an array. + * + * Can be called only if the call is associated to an object. + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_set_member_name (JsonBuilder *builder, const gchar *member_name) +{ + JsonBuilderState *state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (member_name != NULL, NULL); + g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL); + g_return_val_if_fail (json_builder_current_mode (builder) == JSON_BUILDER_MODE_OBJECT, NULL); + + state = g_queue_peek_head (builder->priv->stack); + state->member_name = g_strdup (member_name); + state->mode = JSON_BUILDER_MODE_MEMBER; + + return builder; +} + +/** + * json_builder_add_value: + * @builder: a #JsonBuilder + * @node: the value of the member or element + * + * If called after json_builder_set_member_name(), sets @node as member of the + * most recent opened object, otherwise @node is added as element of the most + * recent opened array. + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_add_value (JsonBuilder *builder, JsonNode *node) +{ + JsonBuilderState *state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL); + g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL); + + state = g_queue_peek_head (builder->priv->stack); + switch (state->mode) + { + case JSON_BUILDER_MODE_MEMBER: + json_object_set_member (state->data.object, state->member_name, node); + g_free (state->member_name); + state->member_name = NULL; + state->mode = JSON_BUILDER_MODE_OBJECT; + break; + + case JSON_BUILDER_MODE_ARRAY: + json_array_add_element (state->data.array, node); + break; + + default: + g_assert_not_reached (); + } + + return builder; +} + +/** + * json_builder_add_int_value: + * @builder: a #JsonBuilder + * @value: the value of the member or element + * + * If called after json_builder_set_member_name(), sets @value as member of the + * most recent opened object, otherwise @value is added as element of the most + * recent opened array. + * + * See also: json_builder_add_value() + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_add_int_value (JsonBuilder *builder, gint64 value) +{ + JsonBuilderState *state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL); + g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL); + + state = g_queue_peek_head (builder->priv->stack); + switch (state->mode) + { + case JSON_BUILDER_MODE_MEMBER: + json_object_set_int_member (state->data.object, state->member_name, value); + g_free (state->member_name); + state->member_name = NULL; + state->mode = JSON_BUILDER_MODE_OBJECT; + break; + + case JSON_BUILDER_MODE_ARRAY: + json_array_add_int_element (state->data.array, value); + break; + + default: + g_assert_not_reached (); + } + + return builder; +} + +/** + * json_builder_add_double_value: + * @builder: a #JsonBuilder + * @value: the value of the member or element + * + * If called after json_builder_set_member_name(), sets @value as member of the + * most recent opened object, otherwise @value is added as element of the most + * recent opened array. + * + * See also: json_builder_add_value() + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_add_double_value (JsonBuilder *builder, gdouble value) +{ + JsonBuilderState *state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL); + g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL); + + state = g_queue_peek_head (builder->priv->stack); + + switch (state->mode) + { + case JSON_BUILDER_MODE_MEMBER: + json_object_set_double_member (state->data.object, state->member_name, value); + g_free (state->member_name); + state->member_name = NULL; + state->mode = JSON_BUILDER_MODE_OBJECT; + break; + + case JSON_BUILDER_MODE_ARRAY: + json_array_add_double_element (state->data.array, value); + break; + + default: + g_assert_not_reached (); + } + + return builder; +} + +/** + * json_builder_add_boolean_value: + * @builder: a #JsonBuilder + * @value: the value of the member or element + * + * If called after json_builder_set_member_name(), sets @value as member of the + * most recent opened object, otherwise @value is added as element of the most + * recent opened array. + * + * See also: json_builder_add_value() + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_add_boolean_value (JsonBuilder *builder, gboolean value) +{ + JsonBuilderState *state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL); + g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL); + + state = g_queue_peek_head (builder->priv->stack); + + switch (state->mode) + { + case JSON_BUILDER_MODE_MEMBER: + json_object_set_boolean_member (state->data.object, state->member_name, value); + g_free (state->member_name); + state->member_name = NULL; + state->mode = JSON_BUILDER_MODE_OBJECT; + break; + + case JSON_BUILDER_MODE_ARRAY: + json_array_add_boolean_element (state->data.array, value); + break; + + default: + g_assert_not_reached (); + } + + return builder; +} + +/** + * json_builder_add_string_value: + * @builder: a #JsonBuilder + * @value: the value of the member or element + * + * If called after json_builder_set_member_name(), sets @value as member of the + * most recent opened object, otherwise @value is added as element of the most + * recent opened array. + * + * See also: json_builder_add_value() + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_add_string_value (JsonBuilder *builder, const gchar *value) +{ + JsonBuilderState *state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL); + g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL); + + state = g_queue_peek_head (builder->priv->stack); + + switch (state->mode) + { + case JSON_BUILDER_MODE_MEMBER: + json_object_set_string_member (state->data.object, state->member_name, value); + g_free (state->member_name); + state->member_name = NULL; + state->mode = JSON_BUILDER_MODE_OBJECT; + break; + + case JSON_BUILDER_MODE_ARRAY: + json_array_add_string_element (state->data.array, value); + break; + + default: + g_assert_not_reached (); + } + + return builder; +} + +/** + * json_builder_add_null_value: + * @builder: a #JsonBuilder + * + * If called after json_builder_set_member_name(), sets null as member of the + * most recent opened object, otherwise null is added as element of the most + * recent opened array. + * + * See also: json_builder_add_value() + * + * Return value: (transfer none): the #JsonBuilder, or %NULL if the call was inconsistent + */ +JsonBuilder * +json_builder_add_null_value (JsonBuilder *builder) +{ + JsonBuilderState *state; + + g_return_val_if_fail (JSON_IS_BUILDER (builder), NULL); + g_return_val_if_fail (!g_queue_is_empty (builder->priv->stack), NULL); + g_return_val_if_fail (json_builder_is_valid_add_mode (builder), NULL); + + state = g_queue_peek_head (builder->priv->stack); + + switch (state->mode) + { + case JSON_BUILDER_MODE_MEMBER: + json_object_set_null_member (state->data.object, state->member_name); + g_free (state->member_name); + state->member_name = NULL; + state->mode = JSON_BUILDER_MODE_OBJECT; + break; + + case JSON_BUILDER_MODE_ARRAY: + json_array_add_null_element (state->data.array); + break; + + default: + g_assert_not_reached (); + } + + return builder; +} diff --git a/json-glib/json-glib/json-builder.h b/json-glib/json-glib/json-builder.h new file mode 100644 index 0000000..e034193 --- /dev/null +++ b/json-glib/json-glib/json-builder.h @@ -0,0 +1,106 @@ +/* json-builder.h: JSON tree builder + * + * This file is part of JSON-GLib + * Copyright (C) 2010 Luca Bruno + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Luca Bruno + */ + +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_BUILDER_H__ +#define __JSON_BUILDER_H__ + +#include + +G_BEGIN_DECLS + +#define JSON_TYPE_BUILDER (json_builder_get_type ()) +#define JSON_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_BUILDER, JsonBuilder)) +#define JSON_IS_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_BUILDER)) +#define JSON_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), JSON_TYPE_BUILDER, JsonBuilderClass)) +#define JSON_IS_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), JSON_TYPE_BUILDER)) +#define JSON_BUILDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), JSON_TYPE_BUILDER, JsonBuilderClass)) + +typedef struct _JsonBuilder JsonBuilder; +typedef struct _JsonBuilderPrivate JsonBuilderPrivate; +typedef struct _JsonBuilderClass JsonBuilderClass; + +/** + * JsonBuilder: + * + * The JsonBuilder structure contains only + * private data and shouls be accessed using the provided API + * + * Since: 0.12 + */ +struct _JsonBuilder +{ + /*< private >*/ + GObject parent_instance; + + JsonBuilderPrivate *priv; +}; + +/** + * JsonBuilderClass: + * + * The JsonBuilder structure contains only + * private data + * + * Since: 0.12 + */ +struct _JsonBuilderClass +{ + /*< private >*/ + GObjectClass parent_class; + + /* padding, for future expansion */ + void (* _json_reserved1) (void); + void (* _json_reserved2) (void); +}; + +GType json_builder_get_type (void) G_GNUC_CONST; + +JsonBuilder *json_builder_new (void); +JsonNode *json_builder_get_root (JsonBuilder *builder); +void json_builder_reset (JsonBuilder *builder); + +JsonBuilder *json_builder_begin_array (JsonBuilder *builder); +JsonBuilder *json_builder_end_array (JsonBuilder *builder); +JsonBuilder *json_builder_begin_object (JsonBuilder *builder); +JsonBuilder *json_builder_end_object (JsonBuilder *builder); + +JsonBuilder *json_builder_set_member_name (JsonBuilder *builder, + const gchar *member_name); +JsonBuilder *json_builder_add_value (JsonBuilder *builder, + JsonNode *node); +JsonBuilder *json_builder_add_int_value (JsonBuilder *builder, + gint64 value); +JsonBuilder *json_builder_add_double_value (JsonBuilder *builder, + gdouble value); +JsonBuilder *json_builder_add_boolean_value (JsonBuilder *builder, + gboolean value); +JsonBuilder *json_builder_add_string_value (JsonBuilder *builder, + const gchar *value); +JsonBuilder *json_builder_add_null_value (JsonBuilder *builder); + +G_END_DECLS + +#endif /* __JSON_BUILDER_H__ */ diff --git a/json-glib/json-glib/json-debug.c b/json-glib/json-glib/json-debug.c new file mode 100644 index 0000000..c0dc2e9 --- /dev/null +++ b/json-glib/json-glib/json-debug.c @@ -0,0 +1,39 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "json-debug.h" + +static unsigned int json_debug_flags = 0; +static gboolean json_debug_flags_set = FALSE; + +#ifdef JSON_ENABLE_DEBUG +static const GDebugKey json_debug_keys[] = { + { "parser", JSON_DEBUG_PARSER }, + { "gobject", JSON_DEBUG_GOBJECT }, + { "path", JSON_DEBUG_PATH } +}; +#endif /* JSON_ENABLE_DEBUG */ + +JsonDebugFlags +_json_get_debug_flags (void) +{ +#ifdef JSON_ENABLE_DEBUG + const gchar *env_str; + + if (json_debug_flags_set) + return json_debug_flags; + + env_str = g_getenv ("JSON_DEBUG"); + if (env_str != NULL && *env_str != '\0') + { + json_debug_flags |= g_parse_debug_string (env_str, + json_debug_keys, + G_N_ELEMENTS (json_debug_keys)); + } + + json_debug_flags_set = TRUE; +#endif /* JSON_ENABLE_DEBUG */ + + return json_debug_flags; +} diff --git a/json-glib/json-glib/json-debug.h b/json-glib/json-glib/json-debug.h new file mode 100644 index 0000000..695917f --- /dev/null +++ b/json-glib/json-glib/json-debug.h @@ -0,0 +1,47 @@ +#ifndef __JSON_DEBUG_H__ +#define __JSON_DEBUG_H__ + +#include + +G_BEGIN_DECLS + +typedef enum { + JSON_DEBUG_PARSER = 1 << 0, + JSON_DEBUG_GOBJECT = 1 << 1, + JSON_DEBUG_PATH = 1 << 2 +} JsonDebugFlags; + +#ifdef JSON_ENABLE_DEBUG + +# ifdef __GNUC__ + +# define JSON_NOTE(type,x,a...) G_STMT_START { \ + if (_json_get_debug_flags () & JSON_DEBUG_##type) { \ + g_message ("[" #type "] " G_STRLOC ": " x, ##a); \ + } } G_STMT_END + +# else +/* Try the C99 version; unfortunately, this does not allow us to pass + * empty arguments to the macro, which means we have to + * do an intemediate printf. + */ +# define JSON_NOTE(type,...) G_STMT_START { \ + if (_json_get_debug_flags () & JSON_DEBUG_##type) { \ + gchar * _fmt = g_strdup_printf (__VA_ARGS__); \ + g_message ("[" #type "] " G_STRLOC ": %s",_fmt); \ + g_free (_fmt); \ + } } G_STMT_END + +# endif /* __GNUC__ */ + +#else + +#define JSON_NOTE(type,...) G_STMT_START { } G_STMT_END + +#endif /* JSON_ENABLE_DEBUG */ + +JsonDebugFlags _json_get_debug_flags (void); + +G_END_DECLS + +#endif /* __JSON_DEBUG_H__ */ diff --git a/json-glib/json-glib/json-enum-types.c b/json-glib/json-glib/json-enum-types.c new file mode 100644 index 0000000..cd4b27d --- /dev/null +++ b/json-glib/json-glib/json-enum-types.c @@ -0,0 +1,117 @@ + +/* Generated data (by glib-mkenums) */ + +#include "json-enum-types.h" + +/* enumerations from "../json-glib/json-parser.h" */ +#include "../json-glib/json-parser.h" + +GType +json_parser_error_get_type(void) { + static volatile gsize g_enum_type_id__volatile = 0; + + if (g_once_init_enter (&g_enum_type_id__volatile)) + { + static const GEnumValue values[] = { + { JSON_PARSER_ERROR_PARSE, "JSON_PARSER_ERROR_PARSE", "parse" }, + { JSON_PARSER_ERROR_TRAILING_COMMA, "JSON_PARSER_ERROR_TRAILING_COMMA", "trailing-comma" }, + { JSON_PARSER_ERROR_MISSING_COMMA, "JSON_PARSER_ERROR_MISSING_COMMA", "missing-comma" }, + { JSON_PARSER_ERROR_MISSING_COLON, "JSON_PARSER_ERROR_MISSING_COLON", "missing-colon" }, + { JSON_PARSER_ERROR_INVALID_BAREWORD, "JSON_PARSER_ERROR_INVALID_BAREWORD", "invalid-bareword" }, + { JSON_PARSER_ERROR_UNKNOWN, "JSON_PARSER_ERROR_UNKNOWN", "unknown" }, + { 0, NULL, NULL } + }; + GType g_enum_type_id; + + g_enum_type_id = + g_enum_register_static (g_intern_static_string ("JsonParserError"), values); + + g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id); + } + + return g_enum_type_id__volatile; +} + +/* enumerations from "../json-glib/json-path.h" */ +#include "../json-glib/json-path.h" + +GType +json_path_error_get_type(void) { + static volatile gsize g_enum_type_id__volatile = 0; + + if (g_once_init_enter (&g_enum_type_id__volatile)) + { + static const GEnumValue values[] = { + { JSON_PATH_ERROR_INVALID_QUERY, "JSON_PATH_ERROR_INVALID_QUERY", "query" }, + { 0, NULL, NULL } + }; + GType g_enum_type_id; + + g_enum_type_id = + g_enum_register_static (g_intern_static_string ("JsonPathError"), values); + + g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id); + } + + return g_enum_type_id__volatile; +} + +/* enumerations from "../json-glib/json-reader.h" */ +#include "../json-glib/json-reader.h" + +GType +json_reader_error_get_type(void) { + static volatile gsize g_enum_type_id__volatile = 0; + + if (g_once_init_enter (&g_enum_type_id__volatile)) + { + static const GEnumValue values[] = { + { JSON_READER_ERROR_NO_ARRAY, "JSON_READER_ERROR_NO_ARRAY", "no-array" }, + { JSON_READER_ERROR_INVALID_INDEX, "JSON_READER_ERROR_INVALID_INDEX", "invalid-index" }, + { JSON_READER_ERROR_NO_OBJECT, "JSON_READER_ERROR_NO_OBJECT", "no-object" }, + { JSON_READER_ERROR_INVALID_MEMBER, "JSON_READER_ERROR_INVALID_MEMBER", "invalid-member" }, + { JSON_READER_ERROR_INVALID_NODE, "JSON_READER_ERROR_INVALID_NODE", "invalid-node" }, + { JSON_READER_ERROR_NO_VALUE, "JSON_READER_ERROR_NO_VALUE", "no-value" }, + { JSON_READER_ERROR_INVALID_TYPE, "JSON_READER_ERROR_INVALID_TYPE", "invalid-type" }, + { 0, NULL, NULL } + }; + GType g_enum_type_id; + + g_enum_type_id = + g_enum_register_static (g_intern_static_string ("JsonReaderError"), values); + + g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id); + } + + return g_enum_type_id__volatile; +} + +/* enumerations from "../json-glib/json-types.h" */ +#include "../json-glib/json-types.h" + +GType +json_node_type_get_type(void) { + static volatile gsize g_enum_type_id__volatile = 0; + + if (g_once_init_enter (&g_enum_type_id__volatile)) + { + static const GEnumValue values[] = { + { JSON_NODE_OBJECT, "JSON_NODE_OBJECT", "object" }, + { JSON_NODE_ARRAY, "JSON_NODE_ARRAY", "array" }, + { JSON_NODE_VALUE, "JSON_NODE_VALUE", "value" }, + { JSON_NODE_NULL, "JSON_NODE_NULL", "null" }, + { 0, NULL, NULL } + }; + GType g_enum_type_id; + + g_enum_type_id = + g_enum_register_static (g_intern_static_string ("JsonNodeType"), values); + + g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id); + } + + return g_enum_type_id__volatile; +} + +/* Generated data ends here */ + diff --git a/json-glib/json-glib/json-enum-types.c.in b/json-glib/json-glib/json-enum-types.c.in new file mode 100644 index 0000000..8b82049 --- /dev/null +++ b/json-glib/json-glib/json-enum-types.c.in @@ -0,0 +1,39 @@ +/*** BEGIN file-header ***/ +#include "json-enum-types.h" +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/* enumerations from "@filename@" */ +#include "@filename@" + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type(void) { + static volatile gsize g_enum_type_id__volatile = 0; + + if (g_once_init_enter (&g_enum_type_id__volatile)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType g_enum_type_id; + + g_enum_type_id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + + g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id); + } + + return g_enum_type_id__volatile; +} +/*** END value-tail ***/ diff --git a/json-glib/json-glib/json-enum-types.h b/json-glib/json-glib/json-enum-types.h new file mode 100644 index 0000000..93dc672 --- /dev/null +++ b/json-glib/json-glib/json-enum-types.h @@ -0,0 +1,36 @@ + +/* Generated data (by glib-mkenums) */ + +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_ENUM_TYPES_H__ +#define __JSON_ENUM_TYPES_H__ + +#include + +G_BEGIN_DECLS + +/* enumerations from "../json-glib/json-parser.h" */ +GType json_parser_error_get_type (void) G_GNUC_CONST; +#define JSON_TYPE_PARSER_ERROR (json_parser_error_get_type()) + +/* enumerations from "../json-glib/json-path.h" */ +GType json_path_error_get_type (void) G_GNUC_CONST; +#define JSON_TYPE_PATH_ERROR (json_path_error_get_type()) + +/* enumerations from "../json-glib/json-reader.h" */ +GType json_reader_error_get_type (void) G_GNUC_CONST; +#define JSON_TYPE_READER_ERROR (json_reader_error_get_type()) + +/* enumerations from "../json-glib/json-types.h" */ +GType json_node_type_get_type (void) G_GNUC_CONST; +#define JSON_TYPE_NODE_TYPE (json_node_type_get_type()) + +G_END_DECLS + +#endif /* !__JSON_ENUM_TYPES_H__ */ + +/* Generated data ends here */ + diff --git a/json-glib/json-glib/json-enum-types.h.in b/json-glib/json-glib/json-enum-types.h.in new file mode 100644 index 0000000..96f565e --- /dev/null +++ b/json-glib/json-glib/json-enum-types.h.in @@ -0,0 +1,30 @@ +/*** BEGIN file-header ***/ +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_ENUM_TYPES_H__ +#define __JSON_ENUM_TYPES_H__ + +#include + +G_BEGIN_DECLS + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ +/*** END file-production ***/ + +/*** BEGIN file-tail ***/ +G_END_DECLS + +#endif /* !__JSON_ENUM_TYPES_H__ */ +/*** END file-tail ***/ + +/*** BEGIN value-header ***/ +GType @enum_name@_get_type (void) G_GNUC_CONST; +#define JSON_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) + +/*** END value-header ***/ + diff --git a/json-glib/json-glib/json-gboxed.c b/json-glib/json-glib/json-gboxed.c new file mode 100644 index 0000000..8f21f75 --- /dev/null +++ b/json-glib/json-glib/json-gboxed.c @@ -0,0 +1,354 @@ +/* json-gboxed.c - JSON GBoxed integration + * + * This file is part of JSON-GLib + * + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:json-gboxed + * @short_description: Serialize and deserialize GBoxed types + * + * GLib's #GBoxed type is a generic wrapper for arbitrary C structures. + * + * JSON-GLib allows serialization and deserialization of a #GBoxed type + * by registering functions mapping a #JsonNodeType to a specific + * #GType. + * + * When registering a #GBoxed type you should also register the + * corresponding transformation functions, e.g.: + * + * |[ + * GType + * my_struct_get_type (void) + * { + * static GType boxed_type = 0; + * + * if (boxed_type == 0) + * { + * boxed_type = + * g_boxed_type_register_static (g_intern_static_string ("MyStruct"), + * (GBoxedCopyFunc) my_struct_copy, + * (GBoxedFreeFunc) my_struct_free); + * + * json_boxed_register_serialize_func (boxed_type, JSON_NODE_OBJECT, + * my_struct_serialize); + * json_boxed_register_deserialize_func (boxed_type, JSON_NODE_OBJECT, + * my_struct_deserialize); + * } + * + * return boxed_type; + * } + * ]| + * + * The serialization function will be invoked by json_boxed_serialize(): + * it will be passed a pointer to the C structure and it must return a + * #JsonNode. The deserialization function will be invoked by + * json_boxed_deserialize(): it will be passed a #JsonNode for the + * declared type and it must return a newly allocated C structure. + * + * It is possible to check whether a #GBoxed type can be deserialized + * from a specific #JsonNodeType, and whether a #GBoxed can be serialized + * and to which specific #JsonNodeType. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "json-types-private.h" +#include "json-gobject.h" + +typedef struct _BoxedTransform BoxedTransform; + +struct _BoxedTransform +{ + GType boxed_type; + gint node_type; + + JsonBoxedSerializeFunc serialize; + JsonBoxedDeserializeFunc deserialize; +}; + +G_LOCK_DEFINE_STATIC (boxed_serialize); +static GSList *boxed_serialize = NULL; + +G_LOCK_DEFINE_STATIC (boxed_deserialize); +static GSList *boxed_deserialize = NULL; + +static gint +boxed_transforms_cmp (gconstpointer a, + gconstpointer b) +{ + const BoxedTransform *ta = a; + const BoxedTransform *tb = b; + + return tb->boxed_type - ta->boxed_type; +} + +static gint +boxed_transforms_find (gconstpointer a, + gconstpointer b) +{ + const BoxedTransform *haystack = a; + const BoxedTransform *needle = b; + + if (needle->node_type != -1) + return (haystack->boxed_type == needle->boxed_type && + haystack->node_type == needle->node_type) ? 0 : 1; + else + return (haystack->boxed_type == needle->boxed_type) ? 0 : 1; +} + +static BoxedTransform * +lookup_boxed_transform (GSList *transforms, + GType gboxed_type, + JsonNodeType node_type) +{ + BoxedTransform lookup; + GSList *t; + + lookup.boxed_type = gboxed_type; + lookup.node_type = node_type; + + t = g_slist_find_custom (transforms, &lookup, boxed_transforms_find); + if (t == NULL) + return NULL; + + return t->data; +} + +/** + * json_boxed_register_serialize_func: (skip) + * @gboxed_type: a boxed type + * @node_type: a node type + * @serialize_func: serialization function for @boxed_type into + * a #JsonNode of type @node_type + * + * Registers a serialization function for a #GBoxed of type @gboxed_type + * to a #JsonNode of type @node_type + * + * Since: 0.10 + */ +void +json_boxed_register_serialize_func (GType gboxed_type, + JsonNodeType node_type, + JsonBoxedSerializeFunc serialize_func) +{ + BoxedTransform *t; + + g_return_if_fail (G_TYPE_IS_BOXED (gboxed_type)); + g_return_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE); + + G_LOCK (boxed_serialize); + + t = lookup_boxed_transform (boxed_serialize, gboxed_type, node_type); + if (t == NULL) + { + t = g_slice_new (BoxedTransform); + + t->boxed_type = gboxed_type; + t->node_type = node_type; + t->serialize = serialize_func; + + boxed_serialize = g_slist_insert_sorted (boxed_serialize, t, + boxed_transforms_cmp); + } + else + g_warning ("A serialization function for the boxed type %s into " + "JSON nodes of type %s already exists", + g_type_name (gboxed_type), + json_node_type_get_name (node_type)); + + G_UNLOCK (boxed_serialize); +} + +/** + * json_boxed_register_deserialize_func: (skip) + * @gboxed_type: a boxed type + * @node_type: a node type + * @deserialize_func: deserialization function for @boxed_type from + * a #JsonNode of type @node_type + * + * Registers a deserialization function for a #GBoxed of type @gboxed_type + * from a #JsonNode of type @node_type + * + * Since: 0.10 + */ +void +json_boxed_register_deserialize_func (GType gboxed_type, + JsonNodeType node_type, + JsonBoxedDeserializeFunc deserialize_func) +{ + BoxedTransform *t; + + g_return_if_fail (G_TYPE_IS_BOXED (gboxed_type)); + g_return_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE); + + G_LOCK (boxed_deserialize); + + t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type); + if (t == NULL) + { + t = g_slice_new (BoxedTransform); + + t->boxed_type = gboxed_type; + t->node_type = node_type; + t->deserialize = deserialize_func; + + boxed_deserialize = g_slist_insert_sorted (boxed_deserialize, t, + boxed_transforms_cmp); + } + else + g_warning ("A deserialization function for the boxed type %s from " + "JSON nodes of type %s already exists", + g_type_name (gboxed_type), + json_node_type_get_name (node_type)); + + G_UNLOCK (boxed_deserialize); +} + +/** + * json_boxed_can_serialize: + * @gboxed_type: a boxed type + * @node_type: (out): the #JsonNode type to which the boxed type can be + * serialized into + * + * Checks whether it is possible to serialize a #GBoxed of + * type @gboxed_type into a #JsonNode. The type of the + * #JsonNode is placed inside @node_type if the function + * returns %TRUE and it's undefined otherwise. + * + * Return value: %TRUE if the type can be serialized, + * and %FALSE otherwise. + * + * Since: 0.10 + */ +gboolean +json_boxed_can_serialize (GType gboxed_type, + JsonNodeType *node_type) +{ + BoxedTransform *t; + + g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE); + g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE); + + t = lookup_boxed_transform (boxed_serialize, gboxed_type, -1); + if (t != NULL) + { + if (node_type) + *node_type = t->node_type; + + return TRUE; + } + + return FALSE; +} + +/** + * json_boxed_can_deserialize: + * @gboxed_type: a boxed type + * @node_type: a #JsonNode type + * + * Checks whether it is possible to deserialize a #GBoxed of + * type @gboxed_type from a #JsonNode of type @node_type + * + * Return value: %TRUE if the type can be deserialized, %FALSE otherwise + * + * Since: 0.10 + */ +gboolean +json_boxed_can_deserialize (GType gboxed_type, + JsonNodeType node_type) +{ + BoxedTransform *t; + + g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), FALSE); + g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, FALSE); + + t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type); + if (t != NULL) + return TRUE; + + return FALSE; +} + +/** + * json_boxed_serialize: + * @gboxed_type: a boxed type + * @boxed: a pointer to a #GBoxed of type @gboxed_type + * + * Serializes @boxed, a pointer to a #GBoxed of type @gboxed_type, + * into a #JsonNode + * + * Return value: (transfer full): a #JsonNode with the serialization of the + * boxed type, or %NULL if serialization either failed or was not possible + * + * Since: 0.10 + */ +JsonNode * +json_boxed_serialize (GType gboxed_type, + gconstpointer boxed) +{ + BoxedTransform *t; + + g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL); + g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL); + g_return_val_if_fail (boxed != NULL, NULL); + + t = lookup_boxed_transform (boxed_serialize, gboxed_type, -1); + if (t != NULL && t->serialize != NULL) + return t->serialize (boxed); + + return NULL; +} + +/** + * json_boxed_deserialize: + * @gboxed_type: a boxed type + * @node: a #JsonNode + * + * Deserializes @node into a #GBoxed of @gboxed_type + * + * Return value: (transfer full): the newly allocated #GBoxed. Use + * g_boxed_free() to release the resources allocated by this + * function + * + * Since: 0.10 + */ +gpointer +json_boxed_deserialize (GType gboxed_type, + JsonNode *node) +{ + JsonNodeType node_type; + BoxedTransform *t; + + g_return_val_if_fail (G_TYPE_IS_BOXED (gboxed_type), NULL); + g_return_val_if_fail (G_TYPE_IS_ABSTRACT (gboxed_type) == FALSE, NULL); + g_return_val_if_fail (node != NULL, NULL); + + node_type = json_node_get_node_type (node); + + t = lookup_boxed_transform (boxed_deserialize, gboxed_type, node_type); + if (t != NULL && t->deserialize != NULL) + return t->deserialize (node); + + return NULL; +} diff --git a/json-glib/json-glib/json-generator.c b/json-glib/json-glib/json-generator.c new file mode 100644 index 0000000..6a36bcf --- /dev/null +++ b/json-glib/json-glib/json-generator.c @@ -0,0 +1,870 @@ +/* json-generator.c - JSON streams generator + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:json-generator + * @short_description: Generates JSON data streams + * + * #JsonGenerator provides an object for generating a JSON data stream and + * put it into a buffer or a file. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "json-types-private.h" + +#include "json-marshal.h" +#include "json-generator.h" + +#define JSON_GENERATOR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), JSON_TYPE_GENERATOR, JsonGeneratorPrivate)) + +struct _JsonGeneratorPrivate +{ + JsonNode *root; + + guint indent; + gunichar indent_char; + + guint pretty : 1; +}; + +enum +{ + PROP_0, + + PROP_PRETTY, + PROP_INDENT, + PROP_ROOT, + PROP_INDENT_CHAR, + + PROP_LAST +}; + +static gchar *dump_value (JsonGenerator *generator, + gint level, + const gchar *name, + JsonNode *node, + gsize *length); +static gchar *dump_array (JsonGenerator *generator, + gint level, + const gchar *name, + JsonArray *array, + gsize *length); +static gchar *dump_object (JsonGenerator *generator, + gint level, + const gchar *name, + JsonObject *object, + gsize *length); + +/* non-ASCII characters can't be escaped, otherwise UTF-8 + * chars will break, so we just pregenerate this table of + * high characters and then we feed it to g_strescape() + */ +static const char json_exceptions[] = { + 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, + 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, + 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, + 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, + 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, + 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, + 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, + 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, + 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, + 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, + 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, + 0xff, + '\0' /* g_strescape() expects a NUL-terminated string */ +}; + +static GParamSpec *generator_props[PROP_LAST] = { NULL, }; + +G_DEFINE_TYPE (JsonGenerator, json_generator, G_TYPE_OBJECT); + +static gchar * +json_strescape (const gchar *str) +{ + return g_strescape (str, json_exceptions); +} + +static void +json_generator_finalize (GObject *gobject) +{ + JsonGeneratorPrivate *priv = JSON_GENERATOR_GET_PRIVATE (gobject); + + if (priv->root) + json_node_free (priv->root); + + G_OBJECT_CLASS (json_generator_parent_class)->finalize (gobject); +} + +static void +json_generator_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + JsonGenerator *generator = JSON_GENERATOR (gobject); + + switch (prop_id) + { + case PROP_PRETTY: + json_generator_set_pretty (generator, g_value_get_boolean (value)); + break; + + case PROP_INDENT: + json_generator_set_indent (generator, g_value_get_uint (value)); + break; + + case PROP_INDENT_CHAR: + json_generator_set_indent_char (generator, g_value_get_uint (value)); + break; + + case PROP_ROOT: + json_generator_set_root (generator, g_value_get_boxed (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +json_generator_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + JsonGeneratorPrivate *priv = JSON_GENERATOR (gobject)->priv; + + switch (prop_id) + { + case PROP_PRETTY: + g_value_set_boolean (value, priv->pretty); + break; + case PROP_INDENT: + g_value_set_uint (value, priv->indent); + break; + case PROP_INDENT_CHAR: + g_value_set_uint (value, priv->indent_char); + break; + case PROP_ROOT: + g_value_set_boxed (value, priv->root); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +json_generator_class_init (JsonGeneratorClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (JsonGeneratorPrivate)); + + /** + * JsonGenerator:pretty: + * + * Whether the output should be "pretty-printed", with indentation and + * newlines. The indentation level can be controlled by using the + * JsonGenerator:indent property + */ + generator_props[PROP_PRETTY] = + g_param_spec_boolean ("pretty", + "Pretty", + "Pretty-print the output", + FALSE, + G_PARAM_READWRITE); + + /** + * JsonGenerator:indent: + * + * Number of spaces to be used to indent when pretty printing. + */ + generator_props[PROP_INDENT] = + g_param_spec_uint ("indent", + "Indent", + "Number of indentation spaces", + 0, G_MAXUINT, + 2, + G_PARAM_READWRITE); + + /** + * JsonGenerator:root: + * + * The root #JsonNode to be used when constructing a JSON data + * stream. + * + * Since: 0.4 + */ + generator_props[PROP_ROOT] = + g_param_spec_boxed ("root", + "Root", + "Root of the JSON data tree", + JSON_TYPE_NODE, + G_PARAM_READWRITE); + + /** + * JsonGenerator:indent-char: + * + * The character that should be used when indenting in pretty print. + * + * Since: 0.6 + */ + generator_props[PROP_INDENT_CHAR] = + g_param_spec_unichar ("indent-char", + "Indent Char", + "Character that should be used when indenting", + ' ', + G_PARAM_READWRITE); + + gobject_class->set_property = json_generator_set_property; + gobject_class->get_property = json_generator_get_property; + gobject_class->finalize = json_generator_finalize; + g_object_class_install_properties (gobject_class, PROP_LAST, generator_props); +} + +static void +json_generator_init (JsonGenerator *generator) +{ + JsonGeneratorPrivate *priv; + + generator->priv = priv = JSON_GENERATOR_GET_PRIVATE (generator); + + priv->pretty = FALSE; + priv->indent = 2; + priv->indent_char = ' '; +} + +static gchar * +dump_value (JsonGenerator *generator, + gint level, + const gchar *name, + JsonNode *node, + gsize *length) +{ + JsonGeneratorPrivate *priv = generator->priv; + gboolean pretty = priv->pretty; + guint indent = priv->indent; + GValue value = { 0, }; + GString *buffer; + + buffer = g_string_new (""); + + if (pretty) + { + guint i; + + for (i = 0; i < (level * indent); i++) + g_string_append_c (buffer, priv->indent_char); + } + + if (name && name[0] != '\0') + { + if (pretty) + g_string_append_printf (buffer, "\"%s\" : ", name); + else + g_string_append_printf (buffer, "\"%s\":", name); + } + + json_node_get_value (node, &value); + + switch (G_VALUE_TYPE (&value)) + { + case G_TYPE_INT64: + g_string_append_printf (buffer, "%" G_GINT64_FORMAT, g_value_get_int64 (&value)); + break; + + case G_TYPE_STRING: + { + gchar *tmp; + + tmp = json_strescape (g_value_get_string (&value)); + g_string_append_printf (buffer, "\"%s\"", tmp); + + g_free (tmp); + } + break; + + case G_TYPE_DOUBLE: + { + gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; + + g_string_append (buffer, + g_ascii_dtostr (buf, sizeof (buf), + g_value_get_double (&value))); + } + break; + + case G_TYPE_BOOLEAN: + g_string_append_printf (buffer, "%s", + g_value_get_boolean (&value) ? "true" : "false"); + break; + + default: + break; + } + + g_value_unset (&value); + + if (length) + *length = buffer->len; + + return g_string_free (buffer, FALSE); +} + +static gchar * +dump_array (JsonGenerator *generator, + gint level, + const gchar *name, + JsonArray *array, + gsize *length) +{ + JsonGeneratorPrivate *priv = generator->priv; + guint array_len = json_array_get_length (array); + guint i; + GString *buffer; + gboolean pretty = priv->pretty; + guint indent = priv->indent; + + buffer = g_string_new (""); + + if (pretty) + { + for (i = 0; i < (level * indent); i++) + g_string_append_c (buffer, priv->indent_char); + } + + if (name && name[0] != '\0') + { + if (pretty) + g_string_append_printf (buffer, "\"%s\" : ", name); + else + g_string_append_printf (buffer, "\"%s\":", name); + } + + g_string_append_c (buffer, '['); + + if (pretty) + g_string_append_c (buffer, '\n'); + + for (i = 0; i < array_len; i++) + { + JsonNode *cur = json_array_get_element (array, i); + guint sub_level = level + 1; + guint j; + gchar *value; + + switch (JSON_NODE_TYPE (cur)) + { + case JSON_NODE_NULL: + if (pretty) + { + for (j = 0; j < (sub_level * indent); j++) + g_string_append_c (buffer, priv->indent_char); + } + g_string_append (buffer, "null"); + break; + + case JSON_NODE_VALUE: + value = dump_value (generator, sub_level, NULL, cur, NULL); + g_string_append (buffer, value); + g_free (value); + break; + + case JSON_NODE_ARRAY: + value = dump_array (generator, sub_level, NULL, json_node_get_array (cur), NULL); + g_string_append (buffer, value); + g_free (value); + break; + + case JSON_NODE_OBJECT: + value = dump_object (generator, sub_level, NULL, json_node_get_object (cur), NULL); + g_string_append (buffer, value); + g_free (value); + break; + } + + if ((i + 1) != array_len) + g_string_append_c (buffer, ','); + + if (pretty) + g_string_append_c (buffer, '\n'); + } + + if (pretty) + { + for (i = 0; i < (level * indent); i++) + g_string_append_c (buffer, priv->indent_char); + } + + g_string_append_c (buffer, ']'); + + if (length) + *length = buffer->len; + + return g_string_free (buffer, FALSE); +} + +static gchar * +dump_object (JsonGenerator *generator, + gint level, + const gchar *name, + JsonObject *object, + gsize *length) +{ + JsonGeneratorPrivate *priv = generator->priv; + GList *members, *l; + GString *buffer; + gboolean pretty = priv->pretty; + guint indent = priv->indent; + guint i; + + buffer = g_string_new (""); + + if (pretty) + { + for (i = 0; i < (level * indent); i++) + g_string_append_c (buffer, priv->indent_char); + } + + if (name && name[0] != '\0') + { + if (pretty) + g_string_append_printf (buffer, "\"%s\" : ", name); + else + g_string_append_printf (buffer, "\"%s\":", name); + } + + g_string_append_c (buffer, '{'); + + if (pretty) + g_string_append_c (buffer, '\n'); + + members = json_object_get_members (object); + + for (l = members; l != NULL; l = l->next) + { + const gchar *member_name = l->data; + JsonNode *cur = json_object_get_member (object, member_name); + guint sub_level = level + 1; + guint j; + gchar *value; + + switch (JSON_NODE_TYPE (cur)) + { + case JSON_NODE_NULL: + if (pretty) + { + for (j = 0; j < (sub_level * indent); j++) + g_string_append_c (buffer, priv->indent_char); + g_string_append_printf (buffer, "\"%s\" : null", member_name); + } + else + { + g_string_append_printf (buffer, "\"%s\":null", member_name); + } + break; + + case JSON_NODE_VALUE: + value = dump_value (generator, sub_level, member_name, cur, NULL); + g_string_append (buffer, value); + g_free (value); + break; + + case JSON_NODE_ARRAY: + value = dump_array (generator, sub_level, member_name, + json_node_get_array (cur), NULL); + g_string_append (buffer, value); + g_free (value); + break; + + case JSON_NODE_OBJECT: + value = dump_object (generator, sub_level, member_name, + json_node_get_object (cur), NULL); + g_string_append (buffer, value); + g_free (value); + break; + } + + if (l->next != NULL) + g_string_append_c (buffer, ','); + + if (pretty) + g_string_append_c (buffer, '\n'); + } + + g_list_free (members); + + if (pretty) + { + for (i = 0; i < (level * indent); i++) + g_string_append_c (buffer, priv->indent_char); + } + + g_string_append_c (buffer, '}'); + + if (length) + *length = buffer->len; + + return g_string_free (buffer, FALSE); +} + +/** + * json_generator_new: + * + * Creates a new #JsonGenerator. You can use this object to generate a + * JSON data stream starting from a data object model composed by + * #JsonNodes. + * + * Return value: the newly created #JsonGenerator instance + */ +JsonGenerator * +json_generator_new (void) +{ + return g_object_new (JSON_TYPE_GENERATOR, NULL); +} + +/** + * json_generator_to_data: + * @generator: a #JsonGenerator + * @length: (out): return location for the length of the returned + * buffer, or %NULL + * + * Generates a JSON data stream from @generator and returns it as a + * buffer. + * + * Return value: a newly allocated buffer holding a JSON data stream. + * Use g_free() to free the allocated resources. + */ +gchar * +json_generator_to_data (JsonGenerator *generator, + gsize *length) +{ + JsonNode *root; + gchar *retval = NULL; + + g_return_val_if_fail (JSON_IS_GENERATOR (generator), NULL); + + root = generator->priv->root; + if (!root) + { + if (length) + *length = 0; + + return NULL; + } + + switch (JSON_NODE_TYPE (root)) + { + case JSON_NODE_ARRAY: + retval = dump_array (generator, 0, NULL, json_node_get_array (root), length); + break; + + case JSON_NODE_OBJECT: + retval = dump_object (generator, 0, NULL, json_node_get_object (root), length); + break; + + case JSON_NODE_NULL: + retval = g_strdup ("null"); + if (length) + *length = 4; + break; + + case JSON_NODE_VALUE: + retval = dump_value (generator, 0, NULL, root, length); + break; + } + + return retval; +} + +/** + * json_generator_to_file: + * @generator: a #JsonGenerator + * @filename: path to the target file + * @error: return location for a #GError, or %NULL + * + * Creates a JSON data stream and puts it inside @filename, overwriting the + * current file contents. This operation is atomic. + * + * Return value: %TRUE if saving was successful. + */ +gboolean +json_generator_to_file (JsonGenerator *generator, + const gchar *filename, + GError **error) +{ + gchar *buffer; + gsize len; + gboolean retval; + + g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + buffer = json_generator_to_data (generator, &len); + retval = g_file_set_contents (filename, buffer, len, error); + g_free (buffer); + + return retval; +} + +/** + * json_generator_to_stream: + * @generator: a #JsonGenerator + * @stream: a #GOutputStream + * @cancellable: (allow-none): a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL + * + * Outputs JSON data and streams it (synchronously) to @stream. + * + * Return value: %TRUE if the write operation was successful, and %FALSE + * on failure. In case of error, the #GError will be filled accordingly + * + * Since: 0.12 + */ +gboolean +json_generator_to_stream (JsonGenerator *generator, + GOutputStream *stream, + GCancellable *cancellable, + GError **error) +{ + gboolean retval; + gchar *buffer; + gsize len; + + g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE); + g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + buffer = json_generator_to_data (generator, &len); + retval = g_output_stream_write (stream, buffer, len, cancellable, error); + g_free (buffer); + + return retval; +} + +/** + * json_generator_set_root: + * @generator: a #JsonGenerator + * @node: a #JsonNode + * + * Sets @node as the root of the JSON data stream to be serialized by + * the #JsonGenerator. + * + * The node is copied by the generator object, so it can be safely + * freed after calling this function. + */ +void +json_generator_set_root (JsonGenerator *generator, + JsonNode *node) +{ + g_return_if_fail (JSON_IS_GENERATOR (generator)); + + if (generator->priv->root != NULL) + { + json_node_free (generator->priv->root); + generator->priv->root = NULL; + } + + if (node != NULL) + generator->priv->root = json_node_copy (node); + + g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_ROOT]); +} + +/** + * json_generator_get_root: + * @generator: a #JsonGenerator + * + * Retrieves a pointer to the root #JsonNode set using + * json_generator_set_root(). + * + * Return value: (transfer none): a #JsonNode, or %NULL. The returned node + * is owned by the #JsonGenerator and it should not be freed + * + * Since: 0.14 + */ +JsonNode * +json_generator_get_root (JsonGenerator *generator) +{ + g_return_val_if_fail (JSON_IS_GENERATOR (generator), NULL); + + return generator->priv->root; +} + +/** + * json_generator_set_pretty: + * @generator: a #JsonGenerator + * @is_pretty: whether the generated string should be pretty printed + * + * Sets whether the generated JSON should be pretty printed, using the + * indentation character specified in the #JsonGenerator:indent-char + * property and the spacing specified in #JsonGenerator:indent property. + * + * Since: 0.14 + */ +void +json_generator_set_pretty (JsonGenerator *generator, + gboolean is_pretty) +{ + JsonGeneratorPrivate *priv; + + g_return_if_fail (JSON_IS_GENERATOR (generator)); + + priv = generator->priv; + + is_pretty = !!is_pretty; + + if (priv->pretty != is_pretty) + { + priv->pretty = is_pretty; + + g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_PRETTY]); + } +} + +/** + * json_generator_get_pretty: + * @generator: a #JsonGenerator + * + * Retrieves the value set using json_generator_set_pretty(). + * + * Return value: %TRUE if the generated JSON should be pretty-printed, and + * %FALSE otherwise + * + * Since: 0.14 + */ +gboolean +json_generator_get_pretty (JsonGenerator *generator) +{ + g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE); + + return generator->priv->pretty; +} + +/** + * json_generator_set_indent: + * @generator: a #JsonGenerator + * @indent_level: the number of repetitions of the indentation character + * that should be applied when pretty printing + * + * Sets the number of repetitions for each indentation level. + * + * Since: 0.14 + */ +void +json_generator_set_indent (JsonGenerator *generator, + guint indent_level) +{ + JsonGeneratorPrivate *priv; + + g_return_if_fail (JSON_IS_GENERATOR (generator)); + + priv = generator->priv; + + if (priv->indent != indent_level) + { + priv->indent = indent_level; + + g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_INDENT]); + } +} + +/** + * json_generator_get_indent: + * @generator: a #JsonGenerator + * + * Retrieves the value set using json_generator_set_indent(). + * + * Return value: the number of repetitions per indentation level + * + * Since: 0.14 + */ +guint +json_generator_get_indent (JsonGenerator *generator) +{ + g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE); + + return generator->priv->indent; +} + +/** + * json_generator_set_indent_char: + * @generator: a #JsonGenerator + * @indent_char: a Unicode character to be used when indenting + * + * Sets the character to be used when indenting + * + * Since: 0.14 + */ +void +json_generator_set_indent_char (JsonGenerator *generator, + gunichar indent_char) +{ + JsonGeneratorPrivate *priv; + + g_return_if_fail (JSON_IS_GENERATOR (generator)); + + priv = generator->priv; + + if (priv->indent_char != indent_char) + { + priv->indent_char = indent_char; + + g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_INDENT_CHAR]); + } +} + +/** + * json_generator_get_indent_char: + * @generator: a #JsonGenerator + * + * Retrieves the value set using json_generator_set_indent_char(). + * + * Return value: the character to be used when indenting + * + * Since: 0.14 + */ +gunichar +json_generator_get_indent_char (JsonGenerator *generator) +{ + g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE); + + return generator->priv->indent_char; +} diff --git a/json-glib/json-glib/json-generator.h b/json-glib/json-glib/json-generator.h new file mode 100644 index 0000000..eb7cdba --- /dev/null +++ b/json-glib/json-glib/json-generator.h @@ -0,0 +1,107 @@ +/* json-generator.h - JSON streams generator + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_GENERATOR_H__ +#define __JSON_GENERATOR_H__ + +#include +#include + +G_BEGIN_DECLS + +#define JSON_TYPE_GENERATOR (json_generator_get_type ()) +#define JSON_GENERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_GENERATOR, JsonGenerator)) +#define JSON_IS_GENERATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_GENERATOR)) +#define JSON_GENERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), JSON_TYPE_GENERATOR, JsonGeneratorClass)) +#define JSON_IS_GENERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), JSON_TYPE_GENERATOR)) +#define JSON_GENERATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), JSON_TYPE_GENERATOR, JsonGeneratorClass)) + +typedef struct _JsonGenerator JsonGenerator; +typedef struct _JsonGeneratorPrivate JsonGeneratorPrivate; +typedef struct _JsonGeneratorClass JsonGeneratorClass; + +/** + * JsonGenerator: + * + * JSON data streams generator. The contents of the #JsonGenerator structure + * are private and should only be accessed via the provided API. + */ +struct _JsonGenerator +{ + /*< private >*/ + GObject parent_instance; + + JsonGeneratorPrivate *priv; +}; + +/** + * JsonGeneratorClass: + * + * #JsonGenerator class + */ +struct _JsonGeneratorClass +{ + /*< private >*/ + GObjectClass parent_class; + + /* padding, for future expansion */ + void (* _json_reserved1) (void); + void (* _json_reserved2) (void); + void (* _json_reserved3) (void); + void (* _json_reserved4) (void); +}; + +GType json_generator_get_type (void) G_GNUC_CONST; + +JsonGenerator * json_generator_new (void); + +void json_generator_set_pretty (JsonGenerator *generator, + gboolean is_pretty); +gboolean json_generator_get_pretty (JsonGenerator *generator); +void json_generator_set_indent (JsonGenerator *generator, + guint indent_level); +guint json_generator_get_indent (JsonGenerator *generator); +void json_generator_set_indent_char (JsonGenerator *generator, + gunichar indent_char); +gunichar json_generator_get_indent_char (JsonGenerator *generator); +void json_generator_set_root (JsonGenerator *generator, + JsonNode *node); +JsonNode * json_generator_get_root (JsonGenerator *generator); + +gchar * json_generator_to_data (JsonGenerator *generator, + gsize *length); +gboolean json_generator_to_file (JsonGenerator *generator, + const gchar *filename, + GError **error); +gboolean json_generator_to_stream (JsonGenerator *generator, + GOutputStream *stream, + GCancellable *cancellable, + GError **error); + +G_END_DECLS + +#endif /* __JSON_GENERATOR_H__ */ diff --git a/json-glib/json-glib/json-glib.h b/json-glib/json-glib/json-glib.h new file mode 100644 index 0000000..257bcdf --- /dev/null +++ b/json-glib/json-glib/json-glib.h @@ -0,0 +1,46 @@ +/* json-glib.h: Main header + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#ifndef __JSON_GLIB_H__ +#define __JSON_GLIB_H__ + +#define __JSON_GLIB_INSIDE__ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#undef __JSON_GLIB_INSIDE__ + +#endif /* __JSON_GLIB_H__ */ diff --git a/json-glib/json-glib/json-glib.symbols b/json-glib/json-glib/json-glib.symbols new file mode 100644 index 0000000..8727fa5 --- /dev/null +++ b/json-glib/json-glib/json-glib.symbols @@ -0,0 +1,175 @@ + +json_array_add_array_element +json_array_add_boolean_element +json_array_add_double_element +json_array_add_element +json_array_add_int_element +json_array_add_null_element +json_array_add_object_element +json_array_add_string_element +json_array_dup_element +json_array_foreach_element +json_array_get_array_element +json_array_get_boolean_element +json_array_get_double_element +json_array_get_element +json_array_get_elements +json_array_get_int_element +json_array_get_length +json_array_get_null_element +json_array_get_object_element +json_array_get_string_element +json_array_get_type +json_array_new +json_array_ref +json_array_remove_element +json_array_sized_new +json_array_unref +json_boxed_can_deserialize +json_boxed_can_serialize +json_boxed_deserialize +json_boxed_register_deserialize_func +json_boxed_register_serialize_func +json_boxed_serialize +json_builder_add_boolean_value +json_builder_add_double_value +json_builder_add_int_value +json_builder_add_null_value +json_builder_add_string_value +json_builder_add_value +json_builder_begin_array +json_builder_begin_object +json_builder_end_array +json_builder_end_object +json_builder_get_root +json_builder_get_type +json_builder_new +json_builder_reset +json_builder_set_member_name +#ifndef JSON_DISABLE_DEPRECATED +json_construct_gobject +#endif +json_generator_get_type +json_generator_new +json_generator_set_root +json_generator_to_data +json_generator_to_file +json_generator_to_stream +json_gobject_deserialize +json_gobject_from_data +json_gobject_serialize +json_gobject_to_data +json_gvariant_deserialize +json_gvariant_deserialize_data +json_gvariant_serialize +json_gvariant_serialize_data +json_node_copy +json_node_dup_array +json_node_dup_object +json_node_dup_string +json_node_free +json_node_get_array +json_node_get_boolean +json_node_get_double +json_node_get_int +json_node_get_node_type +json_node_get_object +json_node_get_parent +json_node_get_string +json_node_get_type +json_node_get_value +json_node_get_value_type +json_node_is_null +json_node_new +json_node_set_array +json_node_set_boolean +json_node_set_double +json_node_set_int +json_node_set_object +json_node_set_parent +json_node_set_string +json_node_set_value +json_node_take_array +json_node_take_object +json_node_type_get_type +json_node_type_name +#ifndef JSON_DISABLE_DEPRECATED +json_object_add_member +#endif +json_object_dup_member +json_object_foreach_member +json_object_get_array_member +json_object_get_boolean_member +json_object_get_double_member +json_object_get_int_member +json_object_get_member +json_object_get_members +json_object_get_null_member +json_object_get_object_member +json_object_get_size +json_object_get_string_member +json_object_get_type +json_object_get_values +json_object_has_member +json_object_new +json_object_ref +json_object_remove_member +json_object_set_array_member +json_object_set_boolean_member +json_object_set_double_member +json_object_set_int_member +json_object_set_member +json_object_set_null_member +json_object_set_object_member +json_object_set_string_member +json_object_unref +json_parser_error_get_type +json_parser_error_quark +json_parser_get_current_line +json_parser_get_current_pos +json_parser_get_root +json_parser_get_type +json_parser_has_assignment +json_parser_load_from_data +json_parser_load_from_file +json_parser_load_from_stream +json_parser_load_from_stream_async +json_parser_load_from_stream_finish +json_parser_new +json_path_compile +json_path_error_quark +json_path_get_type +json_path_match +json_path_new +json_path_query +json_reader_count_elements +json_reader_count_members +json_reader_end_element +json_reader_end_member +json_reader_error_get_type +json_reader_error_quark +json_reader_get_boolean_value +json_reader_get_double_value +json_reader_get_error +json_reader_get_int_value +json_reader_get_member_name +json_reader_get_null_value +json_reader_get_string_value +json_reader_get_type +json_reader_get_value +json_reader_list_members +json_reader_is_array +json_reader_is_object +json_reader_is_value +json_reader_new +json_reader_read_element +json_reader_read_member +json_reader_set_root +json_serializable_default_deserialize_property +json_serializable_default_serialize_property +json_serializable_deserialize_property +json_serializable_get_type +#ifndef JSON_DISABLE_DEPRECATED +json_serialize_gobject +#endif +json_serializable_serialize_property diff --git a/json-glib/json-glib/json-gobject-private.h b/json-glib/json-glib/json-gobject-private.h new file mode 100644 index 0000000..5369ebf --- /dev/null +++ b/json-glib/json-glib/json-gobject-private.h @@ -0,0 +1,39 @@ +/* json-gobject-private.h - GObject private + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#ifndef __JSON_GOBJECT_PRIVATE_H__ +#define __JSON_GOBJECT_PRIVATE_H__ + +#include "json-gobject.h" + +G_BEGIN_DECLS + +JsonNode *json_serialize_pspec (const GValue *real_value, + GParamSpec *pspec); +gboolean json_deserialize_pspec (GValue *value, + GParamSpec *pspec, + JsonNode *node); + +G_END_DECLS + +#endif /* __JSON_GOBJECT_PRIVATE_H__ */ diff --git a/json-glib/json-glib/json-gobject.c b/json-glib/json-glib/json-gobject.c new file mode 100644 index 0000000..9403c62 --- /dev/null +++ b/json-glib/json-glib/json-gobject.c @@ -0,0 +1,1010 @@ +/* json-gobject.c - JSON GObject integration + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:json-gobject + * @short_description: Serialize and deserialize GObjects + * + * JSON-GLib provides API for serializing and deserializing #GObjects + * to and from JSON data streams. + * + * Simple #GObject classes can be (de)serialized into JSON objects, if the + * properties have compatible types with the native JSON types (integers, + * booleans, strings, string vectors). If the class to be (de)serialized has + * complex data types for properties (like boxed types or other objects) + * then the class should implement the provided #JsonSerializable interface + * and its virtual functions. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "json-types-private.h" +#include "json-gobject-private.h" + +#include "json-debug.h" +#include "json-parser.h" +#include "json-generator.h" + +static gboolean +enum_from_string (GType type, + const gchar *string, + gint *enum_value) +{ + GEnumClass *eclass; + GEnumValue *ev; + gchar *endptr; + gint value; + gboolean retval = TRUE; + + g_return_val_if_fail (G_TYPE_IS_ENUM (type), 0); + g_return_val_if_fail (string != NULL, 0); + + value = strtoul (string, &endptr, 0); + if (endptr != string) /* parsed a number */ + *enum_value = value; + else + { + eclass = g_type_class_ref (type); + ev = g_enum_get_value_by_name (eclass, string); + if (!ev) + ev = g_enum_get_value_by_nick (eclass, string); + + if (ev) + *enum_value = ev->value; + else + retval = FALSE; + + g_type_class_unref (eclass); + } + + return retval; +} + +static gboolean +flags_from_string (GType type, + const gchar *string, + gint *flags_value) +{ + GFlagsClass *fclass; + gchar *endptr, *prevptr; + guint i, j, ret, value; + gchar *flagstr; + GFlagsValue *fv; + const gchar *flag; + gunichar ch; + gboolean eos; + + g_return_val_if_fail (G_TYPE_IS_FLAGS (type), 0); + g_return_val_if_fail (string != 0, 0); + + ret = TRUE; + + value = strtoul (string, &endptr, 0); + if (endptr != string) /* parsed a number */ + *flags_value = value; + else + { + fclass = g_type_class_ref (type); + + flagstr = g_strdup (string); + for (value = i = j = 0; ; i++) + { + eos = flagstr[i] == '\0'; + + if (!eos && flagstr[i] != '|') + continue; + + flag = &flagstr[j]; + endptr = &flagstr[i]; + + if (!eos) + { + flagstr[i++] = '\0'; + j = i; + } + + /* trim spaces */ + for (;;) + { + ch = g_utf8_get_char (flag); + if (!g_unichar_isspace (ch)) + break; + flag = g_utf8_next_char (flag); + } + + while (endptr > flag) + { + prevptr = g_utf8_prev_char (endptr); + ch = g_utf8_get_char (prevptr); + if (!g_unichar_isspace (ch)) + break; + endptr = prevptr; + } + + if (endptr > flag) + { + *endptr = '\0'; + fv = g_flags_get_value_by_name (fclass, flag); + + if (!fv) + fv = g_flags_get_value_by_nick (fclass, flag); + + if (fv) + value |= fv->value; + else + { + ret = FALSE; + break; + } + } + + if (eos) + { + *flags_value = value; + break; + } + } + + g_free (flagstr); + + g_type_class_unref (fclass); + } + + return ret; +} + +static GObject * +json_gobject_new (GType gtype, + JsonObject *object) +{ + JsonSerializableIface *iface = NULL; + JsonSerializable *serializable = NULL; + gboolean find_property; + gboolean deserialize_property; + gboolean set_property; + GList *members, *members_left, *l; + guint n_members; + GObjectClass *klass; + GObject *retval; + GArray *construct_params; + gint i; + + klass = g_type_class_ref (gtype); + + n_members = json_object_get_size (object); + members = json_object_get_members (object); + members_left = NULL; + + /* first pass: construct-only properties; here we cannot use Serializable + * because we don't have an instance yet; we use the default implementation + * of json_deserialize_pspec() to deserialize known types + * + * FIXME - find a way to allow deserialization for these properties + */ + construct_params = g_array_sized_new (FALSE, FALSE, sizeof (GParameter), n_members); + + for (l = members; l != NULL; l = l->next) + { + const gchar *member_name = l->data; + GParamSpec *pspec; + GParameter param = { NULL, }; + JsonNode *val; + gboolean res = FALSE; + + pspec = g_object_class_find_property (klass, member_name); + if (!pspec) + goto next_member; + + /* we only apply construct-only properties here */ + if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) == 0) + goto next_member; + + if (!(pspec->flags & G_PARAM_WRITABLE)) + goto next_member; + + g_value_init (¶m.value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + val = json_object_get_member (object, member_name); + res = json_deserialize_pspec (¶m.value, pspec, val); + if (!res) + { + g_warning ("Failed to deserialize \"%s\" property of type \"%s\" for an object of type \"%s\"", + pspec->name, G_VALUE_TYPE_NAME (¶m.value), g_type_name (gtype)); + + g_value_unset (¶m.value); + } + else + { + param.name = g_strdup (pspec->name); + + g_array_append_val (construct_params, param); + + continue; + } + + next_member: + members_left = g_list_prepend (members_left, l->data); + } + + retval = g_object_newv (gtype, + construct_params->len, + (GParameter *) construct_params->data); + + /* free the contents of the GArray */ + for (i = 0; i < construct_params->len; i++) + { + GParameter *param = &g_array_index (construct_params, GParameter, i); + + g_free ((gchar *) param->name); + g_value_unset (¶m->value); + } + + g_array_free (construct_params, TRUE); + g_list_free (members); + + /* we use g_list_prepend() above, but we want to maintain + * the ordering of json_object_get_members() here + */ + members = g_list_reverse (members_left); + + /* do the Serializable type check once */ + if (g_type_is_a (gtype, JSON_TYPE_SERIALIZABLE)) + { + serializable = JSON_SERIALIZABLE (retval); + iface = JSON_SERIALIZABLE_GET_IFACE (serializable); + find_property = (iface->find_property != NULL); + deserialize_property = (iface->deserialize_property != NULL); + set_property = (iface->set_property != NULL); + } + else + { + find_property = FALSE; + deserialize_property = FALSE; + set_property = FALSE; + } + + g_object_freeze_notify (retval); + + for (l = members; l != NULL; l = l->next) + { + const gchar *member_name = l->data; + GParamSpec *pspec; + JsonNode *val; + GValue value = { 0, }; + gboolean res = FALSE; + + if (find_property) + pspec = json_serializable_find_property (serializable, member_name); + else + pspec = g_object_class_find_property (klass, member_name); + + if (pspec == NULL) + continue; + + /* we should have dealt with these above */ + if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) + continue; + + if (!(pspec->flags & G_PARAM_WRITABLE)) + continue; + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + val = json_object_get_member (object, member_name); + + if (deserialize_property) + { + JSON_NOTE (GOBJECT, "Using JsonSerializable for property '%s'", pspec->name); + res = iface->deserialize_property (serializable, pspec->name, + &value, + pspec, + val); + } + + if (!res) + { + JSON_NOTE (GOBJECT, "Using json_deserialize_pspec for property '%s'", pspec->name); + res = json_deserialize_pspec (&value, pspec, val); + } + + if (res) + { + JSON_NOTE (GOBJECT, "Calling set_property('%s', '%s')", + pspec->name, + g_type_name (G_VALUE_TYPE (&value))); + + if (set_property) + json_serializable_set_property (serializable, pspec, &value); + else + g_object_set_property (retval, pspec->name, &value); + } + else + g_warning ("Failed to deserialize \"%s\" property of type \"%s\" for an object of type \"%s\"", + pspec->name, g_type_name (G_VALUE_TYPE (&value)), g_type_name (gtype)); + + g_value_unset (&value); + } + + g_list_free (members); + + g_object_thaw_notify (retval); + + g_type_class_unref (klass); + + return retval; +} + +static JsonObject * +json_gobject_dump (GObject *gobject) +{ + JsonSerializableIface *iface = NULL; + JsonSerializable *serializable = NULL; + gboolean list_properties = FALSE; + gboolean serialize_property = FALSE; + gboolean get_property = FALSE; + JsonObject *object; + GParamSpec **pspecs; + guint n_pspecs, i; + + if (JSON_IS_SERIALIZABLE (gobject)) + { + serializable = JSON_SERIALIZABLE (gobject); + iface = JSON_SERIALIZABLE_GET_IFACE (gobject); + list_properties = (iface->list_properties != NULL); + serialize_property = (iface->serialize_property != NULL); + get_property = (iface->get_property != NULL); + } + + object = json_object_new (); + + if (list_properties) + pspecs = json_serializable_list_properties (serializable, &n_pspecs); + else + pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (gobject), &n_pspecs); + + for (i = 0; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + GValue value = { 0, }; + JsonNode *node = NULL; + + /* read only what we can */ + if (!(pspec->flags & G_PARAM_READABLE)) + continue; + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + if (get_property) + json_serializable_get_property (serializable, pspec, &value); + else + g_object_get_property (gobject, pspec->name, &value); + + /* if there is a serialization vfunc, then it is completely responsible + * for serializing the property, possibly by calling the implementation + * of the default JsonSerializable interface through chaining up + */ + if (serialize_property) + { + node = iface->serialize_property (serializable, pspec->name, + &value, + pspec); + } + /* skip if the value is the default for the property */ + /* else if (!g_param_value_defaults (pspec, &value)) */ + else + node = json_serialize_pspec (&value, pspec); + + if (node) + json_object_set_member (object, pspec->name, node); + + g_value_unset (&value); + } + + g_free (pspecs); + + return object; +} + +gboolean +json_deserialize_pspec (GValue *value, + GParamSpec *pspec, + JsonNode *node) +{ + GValue node_value = { 0, }; + gboolean retval = FALSE; + + if (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)) == G_TYPE_BOXED) + { + JsonNodeType node_type = json_node_get_node_type (node); + GType boxed_type = G_VALUE_TYPE (value); + + if (json_boxed_can_deserialize (boxed_type, node_type)) + { + gpointer boxed = json_boxed_deserialize (boxed_type, node); + + g_value_take_boxed (value, boxed); + + return TRUE; + } + } + + switch (JSON_NODE_TYPE (node)) + { + case JSON_NODE_OBJECT: + if (g_type_is_a (G_VALUE_TYPE (value), G_TYPE_OBJECT)) + { + GObject *object; + + object = json_gobject_new (G_VALUE_TYPE (value), json_node_get_object (node)); + if (object != NULL) + g_value_take_object (value, object); + else + g_value_set_object (value, NULL); + + retval = TRUE; + } + break; + + case JSON_NODE_ARRAY: + if (G_VALUE_HOLDS (value, G_TYPE_STRV)) + { + JsonArray *array = json_node_get_array (node); + guint i, array_len = json_array_get_length (array); + GPtrArray *str_array = g_ptr_array_sized_new (array_len + 1); + + for (i = 0; i < array_len; i++) + { + JsonNode *val = json_array_get_element (array, i); + + if (JSON_NODE_TYPE (val) != JSON_NODE_VALUE) + continue; + + if (json_node_get_string (val) != NULL) + g_ptr_array_add (str_array, (gpointer) json_node_get_string (val)); + } + + g_ptr_array_add (str_array, NULL); + + g_value_set_boxed (value, str_array->pdata); + + g_ptr_array_free (str_array, TRUE); + + retval = TRUE; + } + break; + + case JSON_NODE_VALUE: + json_node_get_value (node, &node_value); +#if 0 + { + gchar *node_str = g_strdup_value_contents (&node_value); + g_debug ("%s: value type '%s' := node value type '%s' -> '%s'", + G_STRLOC, + g_type_name (G_VALUE_TYPE (value)), + g_type_name (G_VALUE_TYPE (&node_value)), + node_str); + g_free (node_str); + } +#endif + + switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value))) + { + case G_TYPE_BOOLEAN: + case G_TYPE_INT64: + case G_TYPE_STRING: + if (G_VALUE_HOLDS (&node_value, G_VALUE_TYPE (value))) + { + g_value_copy (&node_value, value); + retval = TRUE; + } + break; + + case G_TYPE_INT: + if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) + { + g_value_set_int (value, (gint) g_value_get_int64 (&node_value)); + retval = TRUE; + } + break; + + case G_TYPE_CHAR: + if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) + { +#if GLIB_CHECK_VERSION (2, 31, 0) + g_value_set_schar (value, (gchar) g_value_get_int64 (&node_value)); +#else + g_value_set_char (value, (gchar) g_value_get_int64 (&node_value)); +#endif + retval = TRUE; + } + break; + + case G_TYPE_UINT: + if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) + { + g_value_set_uint (value, (guint) g_value_get_int64 (&node_value)); + retval = TRUE; + } + break; + + case G_TYPE_UCHAR: + if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) + { + g_value_set_uchar (value, (guchar) g_value_get_int64 (&node_value)); + retval = TRUE; + } + break; + + case G_TYPE_DOUBLE: + + if (G_VALUE_HOLDS (&node_value, G_TYPE_DOUBLE)) + { + g_value_set_double (value, g_value_get_double (&node_value)); + retval = TRUE; + } + else if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) + { + g_value_set_double (value, (gdouble) g_value_get_int64 (&node_value)); + retval = TRUE; + } + + break; + + case G_TYPE_FLOAT: + if (G_VALUE_HOLDS (&node_value, G_TYPE_DOUBLE)) + { + g_value_set_float (value, (gfloat) g_value_get_double (&node_value)); + retval = TRUE; + } + else if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) + { + g_value_set_float (value, (gfloat) g_value_get_int64 (&node_value)); + retval = TRUE; + } + + break; + + case G_TYPE_ENUM: + { + gint enum_value = 0; + + if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) + { + enum_value = g_value_get_int64 (&node_value); + retval = TRUE; + } + else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING)) + { + retval = enum_from_string (G_VALUE_TYPE (value), + g_value_get_string (&node_value), + &enum_value); + } + + if (retval) + g_value_set_enum (value, enum_value); + } + break; + + case G_TYPE_FLAGS: + { + gint flags_value = 0; + + if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) + { + flags_value = g_value_get_int64 (&node_value); + retval = TRUE; + } + else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING)) + { + retval = flags_from_string (G_VALUE_TYPE (value), + g_value_get_string (&node_value), + &flags_value); + } + + if (retval) + g_value_set_flags (value, flags_value); + } + break; + + default: + retval = FALSE; + break; + } + + g_value_unset (&node_value); + break; + + case JSON_NODE_NULL: + if (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)) == G_TYPE_STRING) + { + g_value_set_string (value, NULL); + retval = TRUE; + } + else if (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)) == G_TYPE_OBJECT) + { + g_value_set_object (value, NULL); + retval = TRUE; + } + else + retval = FALSE; + + break; + } + + return retval; +} + +JsonNode * +json_serialize_pspec (const GValue *real_value, + GParamSpec *pspec) +{ + JsonNode *retval = NULL; + GValue value = { 0, }; + JsonNodeType node_type; + + switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (real_value))) + { + case G_TYPE_INT64: + case G_TYPE_BOOLEAN: + case G_TYPE_DOUBLE: + /* JSON native types */ + retval = json_node_new (JSON_NODE_VALUE); + g_value_init (&value, G_VALUE_TYPE (real_value)); + g_value_copy (real_value, &value); + json_node_set_value (retval, &value); + g_value_unset (&value); + break; + + case G_TYPE_STRING: + /* strings might be NULL, so we handle it differently */ + if (!g_value_get_string (real_value)) + retval = json_node_new (JSON_NODE_NULL); + else + { + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_string (retval, g_value_get_string (real_value)); + break; + } + break; + + case G_TYPE_INT: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, g_value_get_int (real_value)); + break; + + case G_TYPE_FLOAT: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_double (retval, g_value_get_float (real_value)); + break; + + case G_TYPE_BOXED: + if (G_VALUE_HOLDS (real_value, G_TYPE_STRV)) + { + gchar **strv = g_value_get_boxed (real_value); + gint i, strv_len; + JsonArray *array; + + strv_len = g_strv_length (strv); + array = json_array_sized_new (strv_len); + + for (i = 0; i < strv_len; i++) + { + JsonNode *str = json_node_new (JSON_NODE_VALUE); + + json_node_set_string (str, strv[i]); + json_array_add_element (array, str); + } + + retval = json_node_new (JSON_NODE_ARRAY); + json_node_take_array (retval, array); + } + else if (json_boxed_can_serialize (G_VALUE_TYPE (real_value), &node_type)) + { + gpointer boxed = g_value_get_boxed (real_value); + + retval = json_boxed_serialize (G_VALUE_TYPE (real_value), boxed); + } + else + g_warning ("Boxed type '%s' is not handled by JSON-GLib", + g_type_name (G_VALUE_TYPE (real_value))); + break; + + case G_TYPE_UINT: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, g_value_get_uint (real_value)); + break; + + case G_TYPE_LONG: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, g_value_get_long (real_value)); + break; + + case G_TYPE_ULONG: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, g_value_get_long (real_value)); + break; + + case G_TYPE_CHAR: + retval = json_node_new (JSON_NODE_VALUE); +#if GLIB_CHECK_VERSION (2, 31, 0) + json_node_set_int (retval, g_value_get_schar (real_value)); +#else + json_node_set_int (retval, g_value_get_char (real_value)); +#endif + break; + + case G_TYPE_UCHAR: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, g_value_get_uchar (real_value)); + break; + + case G_TYPE_ENUM: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, g_value_get_enum (real_value)); + break; + + case G_TYPE_FLAGS: + retval = json_node_new (JSON_NODE_VALUE); + json_node_set_int (retval, g_value_get_flags (real_value)); + break; + + case G_TYPE_OBJECT: + { + GObject *object = g_value_get_object (real_value); + + if (object != NULL) + { + retval = json_node_new (JSON_NODE_OBJECT); + json_node_take_object (retval, json_gobject_dump (object)); + } + else + retval = json_node_new (JSON_NODE_NULL); + } + break; + + case G_TYPE_NONE: + retval = json_node_new (JSON_NODE_NULL); + break; + + default: + g_warning ("Unsupported type `%s'", g_type_name (G_VALUE_TYPE (real_value))); + break; + } + + return retval; +} + +/** + * json_gobject_deserialize: + * @gtype: the type of the #GObject to create + * @node: a #JsonNode of type %JSON_NODE_OBJECT describing the + * instance of type @gtype + * + * Creates a new #GObject of type @gtype, and constructs it + * using the members of the passed #JsonObject + * + * Return value: (transfer full): The newly created #GObject + * instance. Use g_object_unref() to free the resources + * allocated by this function + * + * Since: 0.10 + */ +GObject * +json_gobject_deserialize (GType gtype, + JsonNode *node) +{ + g_return_val_if_fail (g_type_is_a (gtype, G_TYPE_OBJECT), NULL); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT, NULL); + + return json_gobject_new (gtype, json_node_get_object (node)); +} + +/** + * json_gobject_serialize: + * @gobject: a #GObject + * + * Creates a #JsonNode representing the passed #GObject + * instance. Each member of the returned JSON object will + * map to a property of the #GObject + * + * Return value: (transfer full): the newly created #JsonNode + * of type %JSON_NODE_OBJECT. Use json_node_free() to free + * the resources allocated by this function + * + * Since: 0.10 + */ +JsonNode * +json_gobject_serialize (GObject *gobject) +{ + JsonNode *retval; + + g_return_val_if_fail (G_IS_OBJECT (gobject), NULL); + + retval = json_node_new (JSON_NODE_OBJECT); + json_node_take_object (retval, json_gobject_dump (gobject)); + + return retval; +} + +/** + * json_construct_gobject: + * @gtype: the #GType of object to construct + * @data: a JSON data stream + * @length: length of the data stream + * @error: return location for a #GError, or %NULL + * + * Deserializes a JSON data stream and creates the corresponding + * #GObject class. If @gtype implements the #JsonSerializableIface + * interface, it will be asked to deserialize all the JSON members + * into the respective properties; otherwise, the default implementation + * will be used to translate the compatible JSON native types. + * + * Note: the JSON data stream must be an object declaration. + * + * Return value: (transfer full): a #GObject or %NULL + * + * Since: 0.4 + * + * Deprecated: 0.10: Use json_gobject_from_data() instead + */ +GObject * +json_construct_gobject (GType gtype, + const gchar *data, + gsize length, + GError **error) +{ + return json_gobject_from_data (gtype, data, strlen (data), error); +} + +/** + * json_gobject_from_data: + * @gtype: the #GType of object to construct + * @data: a JSON data stream + * @length: length of the data stream, or -1 if it is NUL-terminated + * @error: return location for a #GError, or %NULL + * + * Deserializes a JSON data stream and creates the corresponding + * #GObject class. If @gtype implements the #JsonSerializableIface + * interface, it will be asked to deserialize all the JSON members + * into the respective properties; otherwise, the default implementation + * will be used to translate the compatible JSON native types. + * + * Note: the JSON data stream must be an object declaration. + * + * Return value: (transfer full): a #GObject or %NULL + * + * Since: 0.10 + */ +GObject * +json_gobject_from_data (GType gtype, + const gchar *data, + gssize length, + GError **error) +{ + JsonParser *parser; + JsonNode *root; + GError *parse_error; + GObject *retval; + + g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL); + g_return_val_if_fail (data != NULL, NULL); + + if (length < 0) + length = strlen (data); + + parser = json_parser_new (); + + parse_error = NULL; + json_parser_load_from_data (parser, data, length, &parse_error); + if (parse_error) + { + g_propagate_error (error, parse_error); + g_object_unref (parser); + return NULL; + } + + root = json_parser_get_root (parser); + if (root == NULL || JSON_NODE_TYPE (root) != JSON_NODE_OBJECT) + { + /* translators: the %s is the name of the data structure */ + g_set_error (error, JSON_PARSER_ERROR, + JSON_PARSER_ERROR_PARSE, + _("Expecting a JSON object, but the root node is of type `%s'"), + json_node_type_name (root)); + g_object_unref (parser); + return NULL; + } + + retval = json_gobject_deserialize (gtype, root); + + g_object_unref (parser); + + return retval; +} + +/** + * json_serialize_gobject: + * @gobject: a #GObject + * @length: (out): return value for the length of the buffer, or %NULL + * + * Serializes a #GObject into a JSON data stream. If @gobject implements + * the #JsonSerializableIface interface, it will be asked to serizalize all + * its properties; otherwise, the default implementation will be use to + * translate the compatible types into JSON native types. + * + * Return value: a JSON data stream representing the passed #GObject + * + * Deprecated: 0.10: Use json_gobject_to_data() instead + */ +gchar * +json_serialize_gobject (GObject *gobject, + gsize *length) +{ + return json_gobject_to_data (gobject, length); +} + +/** + * json_gobject_to_data: + * @gobject: a #GObject + * @length: (out): return value for the length of the buffer, or %NULL + * + * Serializes a #GObject into a JSON data stream, iterating recursively + * over each property. + * + * If @gobject implements the #JsonSerializableIface interface, it will + * be asked to serialize all its properties; otherwise, the default + * implementation will be use to translate the compatible types into + * JSON native types. + * + * Return value: a JSON data stream representing the passed #GObject + * + * Since: 0.10 + */ +gchar * +json_gobject_to_data (GObject *gobject, + gsize *length) +{ + JsonGenerator *gen; + JsonNode *root; + gchar *data; + + g_return_val_if_fail (G_OBJECT (gobject), NULL); + + root = json_gobject_serialize (gobject); + + gen = g_object_new (JSON_TYPE_GENERATOR, + "root", root, + "pretty", TRUE, + "indent", 2, + NULL); + + data = json_generator_to_data (gen, length); + g_object_unref (gen); + + json_node_free (root); + + return data; +} diff --git a/json-glib/json-glib/json-gobject.h b/json-glib/json-glib/json-gobject.h new file mode 100644 index 0000000..2e06d88 --- /dev/null +++ b/json-glib/json-glib/json-gobject.h @@ -0,0 +1,182 @@ +/* json-gobject.h - JSON GObject integration + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#ifndef __JSON_GOBJECT_H__ +#define __JSON_GOBJECT_H__ + +#include +#include + +G_BEGIN_DECLS + +#define JSON_TYPE_SERIALIZABLE (json_serializable_get_type ()) +#define JSON_SERIALIZABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_SERIALIZABLE, JsonSerializable)) +#define JSON_IS_SERIALIZABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_SERIALIZABLE)) +#define JSON_SERIALIZABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), JSON_TYPE_SERIALIZABLE, JsonSerializableIface)) + +typedef struct _JsonSerializable JsonSerializable; /* dummy */ +typedef struct _JsonSerializableIface JsonSerializableIface; + +/** + * JsonSerializableIface: + * @serialize_property: virtual function for serializing a #GObject property + * into a #JsonNode + * @deserialize_property: virtual function for deserializing a #JsonNode + * into a #GObject property + * @find_property: virtual function for finding a property definition using + * its name + * @list_properties: virtual function for listing the installed property + * definitions + * @set_property: virtual function for setting a property + * @get_property: virtual function for getting a property + * + * Interface that allows serializing and deserializing #GObjects + * with properties storing complex data types. The json_serialize_gobject() + * function will check if the passed #GObject implements this interface, + * so it can also be used to override the default property serialization + * sequence. + */ +struct _JsonSerializableIface +{ + /*< private >*/ + GTypeInterface g_iface; + + /*< public >*/ + JsonNode *(* serialize_property) (JsonSerializable *serializable, + const gchar *property_name, + const GValue *value, + GParamSpec *pspec); + gboolean (* deserialize_property) (JsonSerializable *serializable, + const gchar *property_name, + GValue *value, + GParamSpec *pspec, + JsonNode *property_node); + + GParamSpec * (* find_property) (JsonSerializable *serializable, + const char *name); + GParamSpec **(* list_properties) (JsonSerializable *serializable, + guint *n_pspecs); + void (* set_property) (JsonSerializable *serializable, + GParamSpec *pspec, + const GValue *value); + void (* get_property) (JsonSerializable *serializable, + GParamSpec *pspec, + GValue *value); +}; + +GType json_serializable_get_type (void) G_GNUC_CONST; + +JsonNode *json_serializable_serialize_property (JsonSerializable *serializable, + const gchar *property_name, + const GValue *value, + GParamSpec *pspec); +gboolean json_serializable_deserialize_property (JsonSerializable *serializable, + const gchar *property_name, + GValue *value, + GParamSpec *pspec, + JsonNode *property_node); + +GParamSpec * json_serializable_find_property (JsonSerializable *serializable, + const char *name); +GParamSpec ** json_serializable_list_properties (JsonSerializable *serializable, + guint *n_pspecs); +void json_serializable_set_property (JsonSerializable *serializable, + GParamSpec *pspec, + const GValue *value); +void json_serializable_get_property (JsonSerializable *serializable, + GParamSpec *pspec, + GValue *value); + +JsonNode *json_serializable_default_serialize_property (JsonSerializable *serializable, + const gchar *property_name, + const GValue *value, + GParamSpec *pspec); +gboolean json_serializable_default_deserialize_property (JsonSerializable *serializable, + const gchar *property_name, + GValue *value, + GParamSpec *pspec, + JsonNode *property_node); + +/** + * JsonBoxedSerializeFunc: + * @boxed: a #GBoxed + * + * Serializes the passed #GBoxed and stores it inside a #JsonNode + * + * Return value: the newly created #JsonNode + * + * Since: 0.10 + */ +typedef JsonNode *(* JsonBoxedSerializeFunc) (gconstpointer boxed); + +/** + * JsonBoxedDeserializeFunc: + * @node: a #JsonNode + * + * Deserializes the contents of the passed #JsonNode into a #GBoxed + * + * Return value: the newly created boxed type + * + * Since: 0.10 + */ +typedef gpointer (* JsonBoxedDeserializeFunc) (JsonNode *node); + +void json_boxed_register_serialize_func (GType gboxed_type, + JsonNodeType node_type, + JsonBoxedSerializeFunc serialize_func); +void json_boxed_register_deserialize_func (GType gboxed_type, + JsonNodeType node_type, + JsonBoxedDeserializeFunc deserialize_func); +gboolean json_boxed_can_serialize (GType gboxed_type, + JsonNodeType *node_type); +gboolean json_boxed_can_deserialize (GType gboxed_type, + JsonNodeType node_type); +JsonNode *json_boxed_serialize (GType gboxed_type, + gconstpointer boxed); +gpointer json_boxed_deserialize (GType gboxed_type, + JsonNode *node); + +JsonNode *json_gobject_serialize (GObject *gobject); +GObject * json_gobject_deserialize (GType gtype, + JsonNode *node); + +GObject * json_gobject_from_data (GType gtype, + const gchar *data, + gssize length, + GError **error); +gchar * json_gobject_to_data (GObject *gobject, + gsize *length); + +#ifndef JSON_DISABLE_DEPRECATED +GObject * json_construct_gobject (GType gtype, + const gchar *data, + gsize length, + GError **error) G_GNUC_DEPRECATED; +gchar * json_serialize_gobject (GObject *gobject, + gsize *length) G_GNUC_MALLOC G_GNUC_DEPRECATED; +#endif /* JSON_DISABLE_DEPRECATED */ + + +G_END_DECLS + +#endif /* __JSON_GOBJECT_H__ */ diff --git a/json-glib/json-glib/json-gvariant.c b/json-glib/json-glib/json-gvariant.c new file mode 100644 index 0000000..cae43e0 --- /dev/null +++ b/json-glib/json-glib/json-gvariant.c @@ -0,0 +1,1337 @@ +/* json-gvariant.c - JSON GVariant integration + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Author: + * Eduardo Lima Mitev + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include + +#include "json-gvariant.h" + +/** + * SECTION:json-gvariant + * @short_description: Serialize and deserialize GVariant types + * @Title: JSON GVariant Integration + * + * Use json_gvariant_serialize() and json_gvariant_serialize_data() to + * convert from any #GVariant value to a #JsonNode tree or its string + * representation. + * + * Use json_gvariant_deserialize() and json_gvariant_deserialize_data() to + * obtain the #GVariant value from a #JsonNode tree or directly from a JSON + * string. + * Since many #GVariant data types cannot be directly represented as + * JSON, a #GVariant type string (signature) should be provided to these + * methods in order to obtain a correct, type-contrained result. + * If no signature is provided, conversion can still be done, but the + * resulting #GVariant value will be "guessed" from the JSON data types, + * according to the following table: + * + * Default JSON to GVariant conversion (without signature constrains) + * + * + * + * JSON + * GVariant + * + * + * + * + * string + * string (s) + * + * + * int64 + * int64 (x) + * + * + * boolean + * boolean (b) + * + * + * double + * double (d) + * + * + * array + * array of variants (av) + * + * + * object + * dictionary of string-variant (a{sv}) + * + * + * null + * maybe variant (mv) + * + * + * + *
+ */ + +/* custom extension to the GVariantClass enumeration to differentiate + * a single dictionary entry from an array of dictionary entries + */ +#define JSON_G_VARIANT_CLASS_DICTIONARY 'c' + +typedef void (* GVariantForeachFunc) (GVariant *variant_child, + gpointer user_data); + +static GVariant * json_to_gvariant_recurse (JsonNode *json_node, + const gchar **signature, + GError **error); + +/* ========================================================================== */ +/* GVariant to JSON */ +/* ========================================================================== */ + +static void +gvariant_foreach (GVariant *variant, + GVariantForeachFunc func, + gpointer user_data) +{ + GVariantIter iter; + GVariant *variant_child; + + g_variant_iter_init (&iter, variant); + while ((variant_child = g_variant_iter_next_value (&iter)) != NULL) + { + func (variant_child, user_data); + g_variant_unref (variant_child); + } +} + +static void +gvariant_to_json_array_foreach (GVariant *variant_child, + gpointer user_data) +{ + JsonArray *array = user_data; + JsonNode *json_child; + + json_child = json_gvariant_serialize (variant_child); + json_array_add_element (array, json_child); +} + +static JsonNode * +gvariant_to_json_array (GVariant *variant) +{ + JsonArray *array; + JsonNode *json_node; + + array = json_array_new (); + json_node = json_node_new (JSON_NODE_ARRAY); + json_node_set_array (json_node, array); + json_array_unref (array); + + gvariant_foreach (variant, + gvariant_to_json_array_foreach, + array); + + return json_node; +} + +static gchar * +gvariant_simple_to_string (GVariant *variant) +{ + GVariantClass class; + gchar *str; + + class = g_variant_classify (variant); + switch (class) + { + case G_VARIANT_CLASS_BOOLEAN: + if (g_variant_get_boolean (variant)) + str = g_strdup ("true"); + else + str = g_strdup ("false"); + break; + + case G_VARIANT_CLASS_BYTE: + str = g_strdup_printf ("%u", g_variant_get_byte (variant)); + break; + case G_VARIANT_CLASS_INT16: + str = g_strdup_printf ("%d", g_variant_get_int16 (variant)); + break; + case G_VARIANT_CLASS_UINT16: + str = g_strdup_printf ("%u", g_variant_get_uint16 (variant)); + break; + case G_VARIANT_CLASS_INT32: + str = g_strdup_printf ("%d", g_variant_get_int32 (variant)); + break; + case G_VARIANT_CLASS_UINT32: + str = g_strdup_printf ("%u", g_variant_get_uint32 (variant)); + break; + case G_VARIANT_CLASS_INT64: + str = g_strdup_printf ("%" G_GINT64_FORMAT, + g_variant_get_int64 (variant)); + break; + case G_VARIANT_CLASS_UINT64: + str = g_strdup_printf ("%" G_GUINT64_FORMAT, + g_variant_get_uint64 (variant)); + break; + case G_VARIANT_CLASS_HANDLE: + str = g_strdup_printf ("%d", g_variant_get_handle (variant)); + break; + + case G_VARIANT_CLASS_DOUBLE: + { + gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; + + g_ascii_formatd (buf, + G_ASCII_DTOSTR_BUF_SIZE, + "%f", + g_variant_get_double (variant)); + + str = g_strdup (buf); + break; + } + + case G_VARIANT_CLASS_STRING: + case G_VARIANT_CLASS_OBJECT_PATH: + case G_VARIANT_CLASS_SIGNATURE: + str = g_strdup (g_variant_get_string (variant, NULL)); + break; + + default: + g_assert_not_reached (); + break; + } + + return str; +} + +static JsonNode * +gvariant_dict_entry_to_json (GVariant *variant, gchar **member_name) +{ + GVariant *member; + GVariant *value; + JsonNode *json_node; + + member = g_variant_get_child_value (variant, 0); + *member_name = gvariant_simple_to_string (member); + + value = g_variant_get_child_value (variant, 1); + json_node = json_gvariant_serialize (value); + + g_variant_unref (member); + g_variant_unref (value); + + return json_node; +} + +static void +gvariant_to_json_object_foreach (GVariant *variant_child, gpointer user_data) +{ + gchar *member_name; + JsonNode *json_child; + JsonObject *object = (JsonObject *) user_data; + + json_child = gvariant_dict_entry_to_json (variant_child, &member_name); + json_object_set_member (object, member_name, json_child); + g_free (member_name); +} + +static JsonNode * +gvariant_to_json_object (GVariant *variant) +{ + JsonNode *json_node; + JsonObject *object; + + json_node = json_node_new (JSON_NODE_OBJECT); + object = json_object_new (); + json_node_set_object (json_node, object); + json_object_unref (object); + + gvariant_foreach (variant, + gvariant_to_json_object_foreach, + object); + + return json_node; +} + +/** + * json_gvariant_serialize: + * @variant: A #GVariant to convert + * + * Converts @variant to a JSON tree. + * + * Return value: (transfer full): A #JsonNode representing the root of the + * JSON data structure obtained from @variant + * + * Since: 0.14 + */ +JsonNode * +json_gvariant_serialize (GVariant *variant) +{ + JsonNode *json_node = NULL; + GVariantClass class; + + g_return_val_if_fail (variant != NULL, NULL); + + class = g_variant_classify (variant); + + if (! g_variant_is_container (variant)) + { + json_node = json_node_new (JSON_NODE_VALUE); + + switch (class) + { + case G_VARIANT_CLASS_BOOLEAN: + json_node_set_boolean (json_node, g_variant_get_boolean (variant)); + break; + + case G_VARIANT_CLASS_BYTE: + json_node_set_int (json_node, g_variant_get_byte (variant)); + break; + case G_VARIANT_CLASS_INT16: + json_node_set_int (json_node, g_variant_get_int16 (variant)); + break; + case G_VARIANT_CLASS_UINT16: + json_node_set_int (json_node, g_variant_get_uint16 (variant)); + break; + case G_VARIANT_CLASS_INT32: + json_node_set_int (json_node, g_variant_get_int32 (variant)); + break; + case G_VARIANT_CLASS_UINT32: + json_node_set_int (json_node, g_variant_get_uint32 (variant)); + break; + case G_VARIANT_CLASS_INT64: + json_node_set_int (json_node, g_variant_get_int64 (variant)); + break; + case G_VARIANT_CLASS_UINT64: + json_node_set_int (json_node, g_variant_get_uint64 (variant)); + break; + case G_VARIANT_CLASS_HANDLE: + json_node_set_int (json_node, g_variant_get_handle (variant)); + break; + + case G_VARIANT_CLASS_DOUBLE: + json_node_set_double (json_node, g_variant_get_double (variant)); + break; + + case G_VARIANT_CLASS_STRING: + case G_VARIANT_CLASS_OBJECT_PATH: + case G_VARIANT_CLASS_SIGNATURE: + json_node_set_string (json_node, g_variant_get_string (variant, NULL)); + break; + + default: + break; + } + } + else + { + switch (class) + { + case G_VARIANT_CLASS_MAYBE: + { + GVariant *value; + + value = g_variant_get_maybe (variant); + if (value == NULL) + { + json_node = json_node_new (JSON_NODE_NULL); + } + else + { + json_node = json_gvariant_serialize (value); + g_variant_unref (value); + } + + break; + } + + case G_VARIANT_CLASS_VARIANT: + { + GVariant *value; + + value = g_variant_get_variant (variant); + json_node = json_gvariant_serialize (value); + g_variant_unref (value); + + break; + } + + case G_VARIANT_CLASS_ARRAY: + { + const gchar *type; + + type = g_variant_get_type_string (variant); + + if (type[1] == G_VARIANT_CLASS_DICT_ENTRY) + { + /* array of dictionary entries => JsonObject */ + json_node = gvariant_to_json_object (variant); + } + else + { + /* array of anything else => JsonArray */ + json_node = gvariant_to_json_array (variant); + } + + break; + } + + case G_VARIANT_CLASS_DICT_ENTRY: + { + gchar *member_name; + JsonObject *object; + JsonNode *child; + + /* a single dictionary entry => JsonObject */ + json_node = json_node_new (JSON_NODE_OBJECT); + object = json_object_new (); + json_node_set_object (json_node, object); + json_object_unref (object); + + child = gvariant_dict_entry_to_json (variant, &member_name); + + json_object_set_member (object, member_name, child); + g_free (member_name); + + break; + } + + case G_VARIANT_CLASS_TUPLE: + json_node = gvariant_to_json_array (variant); + break; + + default: + break; + } + } + + return json_node; +} + +/** + * json_gvariant_serialize_data: + * @variant: A #GVariant to convert + * @length: (out) (allow-none): Return location for the length of the returned + * string, or %NULL + * + * Converts @variant to its JSON encoded string representation. This method + * is actually a helper function. It uses json_gvariant_serialize() to obtain the + * JSON tree, and then #JsonGenerator to stringify it. + * + * Return value: (transfer full): The JSON encoded string corresponding to + * @variant + * + * Since: 0.14 + */ +gchar * +json_gvariant_serialize_data (GVariant *variant, gsize *length) +{ + JsonNode *json_node; + JsonGenerator *generator; + gchar *json; + + json_node = json_gvariant_serialize (variant); + + generator = json_generator_new (); + + json_generator_set_root (generator, json_node); + json = json_generator_to_data (generator, length); + + g_object_unref (generator); + + json_node_free (json_node); + + return json; +} + +/* ========================================================================== */ +/* JSON to GVariant */ +/* ========================================================================== */ + +static GVariantClass +json_to_gvariant_get_next_class (JsonNode *json_node, + const gchar **signature) +{ + if (signature == NULL) + { + GVariantClass class = 0; + + switch (json_node_get_node_type (json_node)) + { + case JSON_NODE_VALUE: + switch (json_node_get_value_type (json_node)) + { + case G_TYPE_BOOLEAN: + class = G_VARIANT_CLASS_BOOLEAN; + break; + + case G_TYPE_INT64: + class = G_VARIANT_CLASS_INT64; + break; + + case G_TYPE_DOUBLE: + class = G_VARIANT_CLASS_DOUBLE; + break; + + case G_TYPE_STRING: + class = G_VARIANT_CLASS_STRING; + break; + } + + break; + + case JSON_NODE_ARRAY: + class = G_VARIANT_CLASS_ARRAY; + break; + + case JSON_NODE_OBJECT: + class = JSON_G_VARIANT_CLASS_DICTIONARY; + break; + + case JSON_NODE_NULL: + class = G_VARIANT_CLASS_MAYBE; + break; + } + + return class; + } + else + { + if ((*signature)[0] == G_VARIANT_CLASS_ARRAY && + (*signature)[1] == G_VARIANT_CLASS_DICT_ENTRY) + return JSON_G_VARIANT_CLASS_DICTIONARY; + else + return (*signature)[0]; + } +} + +static gboolean +json_node_assert_type (JsonNode *json_node, + JsonNodeType type, + GType sub_type, + GError **error) +{ + if (JSON_NODE_TYPE (json_node) != type || + (type == JSON_NODE_VALUE && + (json_node_get_value_type (json_node) != sub_type))) + { + /* translators: the '%s' is the type name */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + _("Unexpected type '%s' in JSON node"), + g_type_name (json_node_get_value_type (json_node))); + return FALSE; + } + else + { + return TRUE; + } +} + +static void +json_to_gvariant_foreach_add (gpointer data, gpointer user_data) +{ + GVariantBuilder *builder = (GVariantBuilder *) user_data; + GVariant *child = (GVariant *) data; + + g_variant_builder_add_value (builder, child); +} + +static void +json_to_gvariant_foreach_free (gpointer data, gpointer user_data) +{ + GVariant *child = (GVariant *) data; + + g_variant_unref (child); +} + +static GVariant * +json_to_gvariant_build_from_glist (GList *list, const gchar *signature) +{ + GVariantBuilder *builder; + GVariant *result; + + builder = g_variant_builder_new (G_VARIANT_TYPE (signature)); + + g_list_foreach (list, json_to_gvariant_foreach_add, builder); + result = g_variant_builder_end (builder); + + g_variant_builder_unref (builder); + + return result; +} + +static GVariant * +json_to_gvariant_tuple (JsonNode *json_node, + const gchar **signature, + GError **error) +{ + GVariant *variant = NULL; + JsonArray *array; + gint i; + GList *children = NULL; + gboolean roll_back = FALSE; + const gchar *initial_signature; + + array = json_node_get_array (json_node); + + initial_signature = *signature; + (*signature)++; + i = 1; + while ((*signature)[0] != ')' && (*signature)[0] != '\0') + { + JsonNode *json_child; + GVariant *variant_child; + + if (i - 1 >= json_array_get_length (array)) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + _("Missing elements in JSON array to conform to a tuple")); + roll_back = TRUE; + break; + } + + json_child = json_array_get_element (array, i - 1); + + variant_child = json_to_gvariant_recurse (json_child, signature, error); + if (variant_child != NULL) + { + children = g_list_append (children, variant_child); + } + else + { + roll_back = TRUE; + break; + } + + i++; + } + + if (! roll_back) + { + if ( (*signature)[0] != ')') + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + _("Missing closing symbol ')' in the GVariant tuple type")); + roll_back = TRUE; + } + else if (json_array_get_length (array) >= i) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + _("Unexpected extra elements in JSON array")); + roll_back = TRUE; + } + else + { + gchar *tuple_type; + + tuple_type = g_strndup (initial_signature, + (*signature) - initial_signature + 1); + + variant = json_to_gvariant_build_from_glist (children, tuple_type); + + g_free (tuple_type); + } + } + + if (roll_back) + g_list_foreach (children, json_to_gvariant_foreach_free, NULL); + + g_list_free (children); + + return variant; +} + +static gchar * +signature_get_next_complete_type (const gchar **signature) +{ + GVariantClass class; + const gchar *initial_signature; + gchar *result; + + /* here it is assumed that 'signature' is a valid type string */ + + initial_signature = *signature; + class = (*signature)[0]; + + if (class == G_VARIANT_CLASS_TUPLE || class == G_VARIANT_CLASS_DICT_ENTRY) + { + gchar stack[256] = {0}; + guint stack_len = 0; + + do + { + if ( (*signature)[0] == G_VARIANT_CLASS_TUPLE) + { + stack[stack_len] = ')'; + stack_len++; + } + else if ( (*signature)[0] == G_VARIANT_CLASS_DICT_ENTRY) + { + stack[stack_len] = '}'; + stack_len++; + } + + (*signature)++; + + if ( (*signature)[0] == stack[stack_len - 1]) + stack_len--; + } + while (stack_len > 0); + + (*signature)++; + } + else if (class == G_VARIANT_CLASS_ARRAY || class == G_VARIANT_CLASS_MAYBE) + { + gchar *tmp_sig; + + (*signature)++; + tmp_sig = signature_get_next_complete_type (signature); + g_free (tmp_sig); + } + else + { + (*signature)++; + } + + result = g_strndup (initial_signature, (*signature) - initial_signature); + + return result; +} + +static GVariant * +json_to_gvariant_maybe (JsonNode *json_node, + const gchar **signature, + GError **error) +{ + GVariant *variant = NULL; + GVariant *value; + gchar *maybe_signature; + + if (signature) + { + (*signature)++; + maybe_signature = signature_get_next_complete_type (signature); + } + else + { + maybe_signature = g_strdup ("v"); + } + + if (json_node_get_node_type (json_node) == JSON_NODE_NULL) + { + variant = g_variant_new_maybe (G_VARIANT_TYPE (maybe_signature), NULL); + } + else + { + const gchar *tmp_signature; + + tmp_signature = maybe_signature; + value = json_to_gvariant_recurse (json_node, + &tmp_signature, + error); + + if (value != NULL) + variant = g_variant_new_maybe (G_VARIANT_TYPE (maybe_signature), value); + } + + g_free (maybe_signature); + + /* compensate the (*signature)++ call at the end of 'recurse()' */ + if (signature) + (*signature)--; + + return variant; +} + +static GVariant * +json_to_gvariant_array (JsonNode *json_node, + const gchar **signature, + GError **error) +{ + GVariant *variant = NULL; + JsonArray *array; + GList *children = NULL; + gboolean roll_back = FALSE; + const gchar *orig_signature; + gchar *child_signature; + + array = json_node_get_array (json_node); + + if (signature != NULL) + { + orig_signature = *signature; + + (*signature)++; + child_signature = signature_get_next_complete_type (signature); + } + else + child_signature = g_strdup ("v"); + + if (json_array_get_length (array) > 0) + { + gint i; + guint len; + + len = json_array_get_length (array); + for (i = 0; i < len; i++) + { + JsonNode *json_child; + GVariant *variant_child; + const gchar *tmp_signature; + + json_child = json_array_get_element (array, i); + + tmp_signature = child_signature; + variant_child = json_to_gvariant_recurse (json_child, + &tmp_signature, + error); + if (variant_child != NULL) + { + children = g_list_append (children, variant_child); + } + else + { + roll_back = TRUE; + break; + } + } + } + + if (!roll_back) + { + gchar *array_signature; + + if (signature) + array_signature = g_strndup (orig_signature, (*signature) - orig_signature); + else + array_signature = g_strdup ("av"); + + variant = json_to_gvariant_build_from_glist (children, array_signature); + + g_free (array_signature); + + /* compensate the (*signature)++ call at the end of 'recurse()' */ + if (signature) + (*signature)--; + } + else + g_list_foreach (children, json_to_gvariant_foreach_free, NULL); + + g_list_free (children); + g_free (child_signature); + + return variant; +} + +static GVariant * +gvariant_simple_from_string (const gchar *st, + GVariantClass class, + GError **error) +{ + GVariant *variant = NULL; + gchar *nptr = NULL; + + errno = 0; + + switch (class) + { + case G_VARIANT_CLASS_BOOLEAN: + if (g_strcmp0 (st, "true") == 0) + variant = g_variant_new_boolean (TRUE); + else if (g_strcmp0 (st, "false") == 0) + variant = g_variant_new_boolean (FALSE); + else + errno = 1; + break; + + case G_VARIANT_CLASS_BYTE: + variant = g_variant_new_byte (g_ascii_strtoll (st, &nptr, 10)); + break; + + case G_VARIANT_CLASS_INT16: + variant = g_variant_new_int16 (g_ascii_strtoll (st, &nptr, 10)); + break; + + case G_VARIANT_CLASS_UINT16: + variant = g_variant_new_uint16 (g_ascii_strtoll (st, &nptr, 10)); + break; + + case G_VARIANT_CLASS_INT32: + variant = g_variant_new_int32 (g_ascii_strtoll (st, &nptr, 10)); + break; + + case G_VARIANT_CLASS_UINT32: + variant = g_variant_new_uint32 (g_ascii_strtoull (st, &nptr, 10)); + break; + + case G_VARIANT_CLASS_INT64: + variant = g_variant_new_int64 (g_ascii_strtoll (st, &nptr, 10)); + break; + + case G_VARIANT_CLASS_UINT64: + variant = g_variant_new_uint64 (g_ascii_strtoull (st, &nptr, 10)); + break; + + case G_VARIANT_CLASS_HANDLE: + variant = g_variant_new_handle (strtol (st, &nptr, 10)); + break; + + case G_VARIANT_CLASS_DOUBLE: + variant = g_variant_new_double (g_ascii_strtod (st, &nptr)); + break; + + case G_VARIANT_CLASS_STRING: + case G_VARIANT_CLASS_OBJECT_PATH: + case G_VARIANT_CLASS_SIGNATURE: + variant = g_variant_new_string (st); + break; + + default: + g_assert_not_reached (); + break; + } + + if (errno != 0 || nptr == st) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + _("Invalid string value converting to GVariant")); + if (variant != NULL) + { + g_variant_unref (variant); + variant = NULL; + } + } + + return variant; +} + +static void +parse_dict_entry_signature (const gchar **signature, + gchar **entry_signature, + gchar **key_signature, + gchar **value_signature) +{ + const gchar *tmp_sig; + + if (signature != NULL) + *entry_signature = signature_get_next_complete_type (signature); + else + *entry_signature = g_strdup ("{sv}"); + + tmp_sig = (*entry_signature) + 1; + *key_signature = signature_get_next_complete_type (&tmp_sig); + *value_signature = signature_get_next_complete_type (&tmp_sig); +} + +static GVariant * +json_to_gvariant_dict_entry (JsonNode *json_node, + const gchar **signature, + GError **error) +{ + GVariant *variant = NULL; + JsonObject *obj; + + gchar *entry_signature; + gchar *key_signature; + gchar *value_signature; + const gchar *tmp_signature; + + GList *member; + + const gchar *json_member; + JsonNode *json_value; + GVariant *variant_member; + GVariant *variant_value; + + obj = json_node_get_object (json_node); + + if (json_object_get_size (obj) != 1) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + _("A GVariant dictionary entry expects a JSON object with exactly one member")); + return NULL; + } + + parse_dict_entry_signature (signature, + &entry_signature, + &key_signature, + &value_signature); + + member = json_object_get_members (obj); + + json_member = (const gchar *) member->data; + variant_member = gvariant_simple_from_string (json_member, + key_signature[0], + error); + if (variant_member != NULL) + { + json_value = json_object_get_member (obj, json_member); + + tmp_signature = value_signature; + variant_value = json_to_gvariant_recurse (json_value, + &tmp_signature, + error); + + if (variant_value != NULL) + { + GVariantBuilder *builder; + + builder = g_variant_builder_new (G_VARIANT_TYPE (entry_signature)); + g_variant_builder_add_value (builder, variant_member); + g_variant_builder_add_value (builder, variant_value); + variant = g_variant_builder_end (builder); + + g_variant_builder_unref (builder); + } + } + + g_list_free (member); + g_free (value_signature); + g_free (key_signature); + g_free (entry_signature); + + /* compensate the (*signature)++ call at the end of 'recurse()' */ + if (signature) + (*signature)--; + + return variant; +} + +static GVariant * +json_to_gvariant_dictionary (JsonNode *json_node, + const gchar **signature, + GError **error) +{ + GVariant *variant = NULL; + JsonObject *obj; + gboolean roll_back = FALSE; + + gchar *dict_signature; + gchar *entry_signature; + gchar *key_signature; + gchar *value_signature; + const gchar *tmp_signature; + + GVariantBuilder *builder; + GList *members; + GList *member; + + obj = json_node_get_object (json_node); + + if (signature != NULL) + (*signature)++; + + parse_dict_entry_signature (signature, + &entry_signature, + &key_signature, + &value_signature); + + dict_signature = g_strdup_printf ("a%s", entry_signature); + + builder = g_variant_builder_new (G_VARIANT_TYPE (dict_signature)); + + members = json_object_get_members (obj); + + member = members; + while (member != NULL) + { + const gchar *json_member; + JsonNode *json_value; + GVariant *variant_member; + GVariant *variant_value; + + json_member = (const gchar *) member->data; + variant_member = gvariant_simple_from_string (json_member, + key_signature[0], + error); + if (variant_member == NULL) + { + roll_back = TRUE; + break; + } + + json_value = json_object_get_member (obj, json_member); + + tmp_signature = value_signature; + variant_value = json_to_gvariant_recurse (json_value, + &tmp_signature, + error); + + if (variant_value != NULL) + { + g_variant_builder_open (builder, G_VARIANT_TYPE (entry_signature)); + g_variant_builder_add_value (builder, variant_member); + g_variant_builder_add_value (builder, variant_value); + g_variant_builder_close (builder); + } + else + { + roll_back = TRUE; + break; + } + + member = member->next; + } + + if (! roll_back) + variant = g_variant_builder_end (builder); + + g_variant_builder_unref (builder); + g_list_free (members); + g_free (value_signature); + g_free (key_signature); + g_free (entry_signature); + g_free (dict_signature); + + /* compensate the (*signature)++ call at the end of 'recurse()' */ + if (signature != NULL) + (*signature)--; + + return variant; +} + +static GVariant * +json_to_gvariant_recurse (JsonNode *json_node, + const gchar **signature, + GError **error) +{ + GVariant *variant = NULL; + GVariantClass class; + + class = json_to_gvariant_get_next_class (json_node, signature); + + if (class == JSON_G_VARIANT_CLASS_DICTIONARY) + { + if (json_node_assert_type (json_node, JSON_NODE_OBJECT, 0, error)) + variant = json_to_gvariant_dictionary (json_node, signature, error); + + goto out; + } + + switch (class) + { + case G_VARIANT_CLASS_BOOLEAN: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_BOOLEAN, error)) + variant = g_variant_new_boolean (json_node_get_boolean (json_node)); + break; + + case G_VARIANT_CLASS_BYTE: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) + variant = g_variant_new_byte (json_node_get_int (json_node)); + break; + + case G_VARIANT_CLASS_INT16: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) + variant = g_variant_new_int16 (json_node_get_int (json_node)); + break; + + case G_VARIANT_CLASS_UINT16: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) + variant = g_variant_new_uint16 (json_node_get_int (json_node)); + break; + + case G_VARIANT_CLASS_INT32: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) + variant = g_variant_new_int32 (json_node_get_int (json_node)); + break; + + case G_VARIANT_CLASS_UINT32: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) + variant = g_variant_new_uint32 (json_node_get_int (json_node)); + break; + + case G_VARIANT_CLASS_INT64: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) + variant = g_variant_new_int64 (json_node_get_int (json_node)); + break; + + case G_VARIANT_CLASS_UINT64: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) + variant = g_variant_new_uint64 (json_node_get_int (json_node)); + break; + + case G_VARIANT_CLASS_HANDLE: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_INT64, error)) + variant = g_variant_new_handle (json_node_get_int (json_node)); + break; + + case G_VARIANT_CLASS_DOUBLE: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_DOUBLE, error)) + variant = g_variant_new_double (json_node_get_double (json_node)); + break; + + case G_VARIANT_CLASS_STRING: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_STRING, error)) + variant = g_variant_new_string (json_node_get_string (json_node)); + break; + + case G_VARIANT_CLASS_OBJECT_PATH: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_STRING, error)) + variant = g_variant_new_object_path (json_node_get_string (json_node)); + break; + + case G_VARIANT_CLASS_SIGNATURE: + if (json_node_assert_type (json_node, JSON_NODE_VALUE, G_TYPE_STRING, error)) + variant = g_variant_new_signature (json_node_get_string (json_node)); + break; + + case G_VARIANT_CLASS_VARIANT: + variant = g_variant_new_variant (json_to_gvariant_recurse (json_node, + NULL, + error)); + break; + + case G_VARIANT_CLASS_MAYBE: + variant = json_to_gvariant_maybe (json_node, signature, error); + break; + + case G_VARIANT_CLASS_ARRAY: + if (json_node_assert_type (json_node, JSON_NODE_ARRAY, 0, error)) + variant = json_to_gvariant_array (json_node, signature, error); + break; + + case G_VARIANT_CLASS_TUPLE: + if (json_node_assert_type (json_node, JSON_NODE_ARRAY, 0, error)) + variant = json_to_gvariant_tuple (json_node, signature, error); + break; + + case G_VARIANT_CLASS_DICT_ENTRY: + if (json_node_assert_type (json_node, JSON_NODE_OBJECT, 0, error)) + variant = json_to_gvariant_dict_entry (json_node, signature, error); + break; + + default: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + _("GVariant class '%c' not supported"), class); + break; + } + +out: + if (signature) + (*signature)++; + + return variant; +} + +/** + * json_gvariant_deserialize: + * @json_node: A #JsonNode to convert + * @signature: (allow-none): A valid #GVariant type string, or %NULL + * @error: A pointer to a #GError + * + * Converts a JSON data structure to a GVariant value using @signature to + * resolve ambiguous data types. If no error occurs, the resulting #GVariant + * is guaranteed to conform to @signature. + * + * If @signature is not %NULL but does not represent a valid GVariant type + * string, %NULL is returned and error is set to %G_IO_ERROR_INVALID_ARGUMENT. + * If a @signature is provided but the JSON structure cannot be mapped to it, + * %NULL is returned and error is set to %G_IO_ERROR_INVALID_DATA. + * If @signature is %NULL, the conversion is done based strictly on the types + * in the JSON nodes. + * + * Return value: (transfer full): A newly created #GVariant compliant with + * @signature, or %NULL on error + * + * Since: 0.14 + */ +GVariant * +json_gvariant_deserialize (JsonNode *json_node, + const gchar *signature, + GError **error) +{ + g_return_val_if_fail (json_node != NULL, NULL); + + if (signature != NULL && ! g_variant_type_string_is_valid (signature)) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Invalid GVariant signature")); + return NULL; + } + + return json_to_gvariant_recurse (json_node, signature ? &signature : NULL, error); +} + +/** + * json_gvariant_deserialize_data: + * @json: A JSON data string + * @length: The length of @json, or -1 if %NULL-terminated + * @signature: (allow-none): A valid #GVariant type string, or %NULL + * @error: A pointer to a #GError + * + * Converts a JSON string to a #GVariant value. This method works exactly + * like json_gvariant_deserialize(), but takes a JSON encoded string instead. + * The string is first converted to a #JsonNode using #JsonParser, and then + * json_gvariant_deserialize() is called. + * + * Returns: (transfer full): A newly created #GVariant compliant with + * @signature, or %NULL on error + * + * Since: 0.14 + */ +GVariant * +json_gvariant_deserialize_data (const gchar *json, + gssize length, + const gchar *signature, + GError **error) +{ + JsonParser *parser; + GVariant *variant = NULL; + JsonNode *root; + + parser = json_parser_new (); + + if (! json_parser_load_from_data (parser, json, length, error)) + return NULL; + + root = json_parser_get_root (parser); + if (root == NULL) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + _("JSON data is empty")); + } + else + { + variant = + json_gvariant_deserialize (json_parser_get_root (parser), signature, error); + } + + g_object_unref (parser); + + return variant; +} diff --git a/json-glib/json-glib/json-gvariant.h b/json-glib/json-glib/json-gvariant.h new file mode 100644 index 0000000..6644b21 --- /dev/null +++ b/json-glib/json-glib/json-gvariant.h @@ -0,0 +1,46 @@ +/* json-gvariant.h - JSON GVariant integration + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Eduardo Lima Mitev + */ + +#ifndef __JSON_GVARIANT_H__ +#define __JSON_GVARIANT_H__ + +#include +#include + +G_BEGIN_DECLS + +JsonNode * json_gvariant_serialize (GVariant *variant); +gchar * json_gvariant_serialize_data (GVariant *variant, + gsize *length); + +GVariant * json_gvariant_deserialize (JsonNode *json_node, + const gchar *signature, + GError **error); +GVariant * json_gvariant_deserialize_data (const gchar *json, + gssize length, + const gchar *signature, + GError **error); + +G_END_DECLS + +#endif /* __JSON_GVARIANT_H__ */ diff --git a/json-glib/json-glib/json-marshal.c b/json-glib/json-glib/json-marshal.c new file mode 100644 index 0000000..dae8b56 --- /dev/null +++ b/json-glib/json-glib/json-marshal.c @@ -0,0 +1,132 @@ +#include "json-marshal.h" + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#define g_marshal_value_peek_variant(v) g_value_get_variant (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:VOID (./json-marshal.list:1) */ + +/* VOID:BOXED (./json-marshal.list:2) */ + +/* VOID:BOXED,STRING (./json-marshal.list:3) */ +void +_json_marshal_VOID__BOXED_STRING (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__BOXED_STRING) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__BOXED_STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__BOXED_STRING) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + data2); +} + +/* VOID:BOXED,INT (./json-marshal.list:4) */ +void +_json_marshal_VOID__BOXED_INT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__BOXED_INT) (gpointer data1, + gpointer arg_1, + gint arg_2, + gpointer data2); + register GMarshalFunc_VOID__BOXED_INT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__BOXED_INT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + data2); +} + +/* VOID:POINTER (./json-marshal.list:5) */ + diff --git a/json-glib/json-glib/json-marshal.h b/json-glib/json-glib/json-marshal.h new file mode 100644 index 0000000..f4f7658 --- /dev/null +++ b/json-glib/json-glib/json-marshal.h @@ -0,0 +1,37 @@ + +#ifndef ___json_marshal_MARSHAL_H__ +#define ___json_marshal_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* VOID:VOID (./json-marshal.list:1) */ +#define _json_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID + +/* VOID:BOXED (./json-marshal.list:2) */ +#define _json_marshal_VOID__BOXED g_cclosure_marshal_VOID__BOXED + +/* VOID:BOXED,STRING (./json-marshal.list:3) */ +extern void _json_marshal_VOID__BOXED_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:BOXED,INT (./json-marshal.list:4) */ +extern void _json_marshal_VOID__BOXED_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:POINTER (./json-marshal.list:5) */ +#define _json_marshal_VOID__POINTER g_cclosure_marshal_VOID__POINTER + +G_END_DECLS + +#endif /* ___json_marshal_MARSHAL_H__ */ + diff --git a/json-glib/json-glib/json-marshal.list b/json-glib/json-glib/json-marshal.list new file mode 100644 index 0000000..6eaa904 --- /dev/null +++ b/json-glib/json-glib/json-marshal.list @@ -0,0 +1,5 @@ +VOID:VOID +VOID:BOXED +VOID:BOXED,STRING +VOID:BOXED,INT +VOID:POINTER diff --git a/json-glib/json-glib/json-node.c b/json-glib/json-glib/json-node.c new file mode 100644 index 0000000..67cdc95 --- /dev/null +++ b/json-glib/json-glib/json-node.c @@ -0,0 +1,800 @@ +/* json-node.c - JSON object model node + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "json-types-private.h" + +/** + * SECTION:json-node + * @short_description: Node in a JSON object model + * + * A #JsonNode is a generic container of elements inside a JSON stream. + * It can contain fundamental types (integers, booleans, floating point + * numbers, strings) and complex types (arrays and objects). + * + * When parsing a JSON data stream you extract the root node and walk + * the node tree by retrieving the type of data contained inside the + * node with the %JSON_NODE_TYPE macro. If the node contains a fundamental + * type you can retrieve a copy of the #GValue holding it with the + * json_node_get_value() function, and then use the #GValue API to extract + * the data; if the node contains a complex type you can retrieve the + * #JsonObject or the #JsonArray using json_node_get_object() or + * json_node_get_array() respectively, and then retrieve the nodes + * they contain. + */ + +G_DEFINE_BOXED_TYPE (JsonNode, json_node, json_node_copy, json_node_free); + +/** + * json_node_get_value_type: + * @node: a #JsonNode + * + * Returns the #GType of the payload of the node. + * + * Return value: a #GType for the payload. + * + * Since: 0.4 + */ +GType +json_node_get_value_type (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, G_TYPE_INVALID); + + switch (node->type) + { + case JSON_NODE_OBJECT: + return JSON_TYPE_OBJECT; + + case JSON_NODE_ARRAY: + return JSON_TYPE_ARRAY; + + case JSON_NODE_NULL: + return G_TYPE_INVALID; + + case JSON_NODE_VALUE: + return G_VALUE_TYPE (&(node->data.value)); + + default: + g_assert_not_reached (); + return G_TYPE_INVALID; + } +} + +/** + * json_node_new: + * @type: a #JsonNodeType + * + * Creates a new #JsonNode of @type. + * + * Return value: the newly created #JsonNode + */ +JsonNode * +json_node_new (JsonNodeType type) +{ + JsonNode *data; + + g_return_val_if_fail (type >= JSON_NODE_OBJECT && + type <= JSON_NODE_NULL, NULL); + + data = g_slice_new0 (JsonNode); + data->type = type; + + return data; +} + +/** + * json_node_copy: + * @node: a #JsonNode + * + * Copies @node. If the node contains complex data types then the reference + * count of the objects is increased. + * + * Return value: (transfer full): the copied #JsonNode + */ +JsonNode * +json_node_copy (JsonNode *node) +{ + JsonNode *copy; + + g_return_val_if_fail (node != NULL, NULL); + + copy = g_slice_new0 (JsonNode); + copy->type = node->type; + + switch (copy->type) + { + case JSON_NODE_OBJECT: + if (node->data.object) + copy->data.object = json_object_ref (node->data.object); + break; + + case JSON_NODE_ARRAY: + if (node->data.array) + copy->data.array = json_array_ref (node->data.array); + break; + + case JSON_NODE_VALUE: + if (G_VALUE_TYPE (&(node->data.value)) != G_TYPE_INVALID) + { + g_value_init (&(copy->data.value), G_VALUE_TYPE (&(node->data.value))); + g_value_copy (&(node->data.value), &(copy->data.value)); + } + break; + + case JSON_NODE_NULL: + break; + + default: + g_assert_not_reached (); + } + + return copy; +} + +/** + * json_node_set_object: + * @node: a #JsonNode + * @object: a #JsonObject + * + * Sets @objects inside @node. The reference count of @object is increased. + */ +void +json_node_set_object (JsonNode *node, + JsonObject *object) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT); + + if (node->data.object) + json_object_unref (node->data.object); + + if (object) + node->data.object = json_object_ref (object); + else + node->data.object = NULL; +} + +/** + * json_node_take_object: + * @node: a #JsonNode + * @object: (transfer full): a #JsonObject + * + * Sets @object inside @node. The reference count of @object is not increased. + */ +void +json_node_take_object (JsonNode *node, + JsonObject *object) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT); + + if (node->data.object) + { + json_object_unref (node->data.object); + node->data.object = NULL; + } + + if (object) + node->data.object = object; +} + +/** + * json_node_get_object: + * @node: a #JsonNode + * + * Retrieves the #JsonObject stored inside a #JsonNode + * + * Return value: (transfer none): the #JsonObject + */ +JsonObject * +json_node_get_object (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT, NULL); + + return node->data.object; +} + +/** + * json_node_dup_object: + * @node: a #JsonNode + * + * Retrieves the #JsonObject inside @node. The reference count of + * the returned object is increased. + * + * Return value: (transfer full): the #JsonObject + */ +JsonObject * +json_node_dup_object (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT, NULL); + + if (node->data.object) + return json_object_ref (node->data.object); + + return NULL; +} + +/** + * json_node_set_array: + * @node: a #JsonNode + * @array: a #JsonArray + * + * Sets @array inside @node and increases the #JsonArray reference count + */ +void +json_node_set_array (JsonNode *node, + JsonArray *array) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY); + + if (node->data.array) + json_array_unref (node->data.array); + + if (array) + node->data.array = json_array_ref (array); + else + node->data.array = NULL; +} + +/** + * json_node_take_array: + * @node: a #JsonNode + * @array: (transfer full): a #JsonArray + * + * Sets @array into @node without increasing the #JsonArray reference count. + */ +void +json_node_take_array (JsonNode *node, + JsonArray *array) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY); + + if (node->data.array) + { + json_array_unref (node->data.array); + node->data.array = NULL; + } + + if (array) + node->data.array = array; +} + +/** + * json_node_get_array: + * @node: a #JsonNode + * + * Retrieves the #JsonArray stored inside a #JsonNode + * + * Return value: (transfer none): the #JsonArray + */ +JsonArray * +json_node_get_array (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY, NULL); + + return node->data.array; +} + +/** + * json_node_dup_array + * @node: a #JsonNode + * + * Retrieves the #JsonArray stored inside a #JsonNode and returns it + * with its reference count increased by one. + * + * Return value: (transfer full): the #JsonArray with its reference + * count increased. + */ +JsonArray * +json_node_dup_array (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY, NULL); + + if (node->data.array) + return json_array_ref (node->data.array); + + return NULL; +} + +/** + * json_node_get_value: + * @node: a #JsonNode + * @value: (out caller-allocates): return location for an uninitialized value + * + * Retrieves a value from a #JsonNode and copies into @value. When done + * using it, call g_value_unset() on the #GValue. + */ +void +json_node_get_value (JsonNode *node, + GValue *value) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); + + if (G_VALUE_TYPE (&(node->data.value)) != G_TYPE_INVALID) + { + g_value_init (value, G_VALUE_TYPE (&(node->data.value))); + g_value_copy (&(node->data.value), value); + } +} + +static void inline +node_value_unset (JsonNode *node) +{ + if (G_VALUE_TYPE (&(node->data.value)) != G_TYPE_INVALID) + g_value_unset (&(node->data.value)); +} + +/** + * json_node_set_value: + * @node: a #JsonNode + * @value: the #GValue to set + * + * Sets @value inside @node. The passed #GValue is copied into the #JsonNode + */ +void +json_node_set_value (JsonNode *node, + const GValue *value) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); + g_return_if_fail (G_VALUE_TYPE (value) != G_TYPE_INVALID); + + switch (G_VALUE_TYPE (value)) + { + /* direct copy for the types we use */ + case G_TYPE_INT64: + case G_TYPE_BOOLEAN: + case G_TYPE_DOUBLE: + case G_TYPE_STRING: + node_value_unset (node); + g_value_init (&(node->data.value), G_VALUE_TYPE (value)); + g_value_copy (value, &(node->data.value)); + break; + + /* auto-promote ints to long longs */ + case G_TYPE_INT: + node_value_unset (node); + g_value_init (&(node->data.value), G_TYPE_INT64); + g_value_set_int64 (&(node->data.value), + g_value_get_int (value)); + break; + + /* auto-promote single precision to double precision */ + case G_TYPE_FLOAT: + node_value_unset (node); + g_value_init (&(node->data.value), G_TYPE_DOUBLE); + g_value_set_double (&(node->data.value), + g_value_get_float (value)); + break; + + default: + g_warning ("Invalid value of type '%s'", + g_type_name (G_VALUE_TYPE (value))); + return; + } + +} + +/** + * json_node_free: + * @node: a #JsonNode + * + * Frees the resources allocated by @node. + */ +void +json_node_free (JsonNode *node) +{ + if (G_LIKELY (node)) + { + switch (node->type) + { + case JSON_NODE_OBJECT: + if (node->data.object) + json_object_unref (node->data.object); + break; + + case JSON_NODE_ARRAY: + if (node->data.array) + json_array_unref (node->data.array); + break; + + case JSON_NODE_VALUE: + g_value_unset (&(node->data.value)); + break; + + case JSON_NODE_NULL: + break; + } + + g_slice_free (JsonNode, node); + } +} + +/** + * json_node_type_name: + * @node: a #JsonNode + * + * Retrieves the user readable name of the data type contained by @node. + * + * Return value: a string containing the name of the type. The returned string + * is owned by the node and should never be modified or freed + */ +const gchar * +json_node_type_name (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, "(null)"); + + switch (node->type) + { + case JSON_NODE_OBJECT: + case JSON_NODE_ARRAY: + case JSON_NODE_NULL: + return json_node_type_get_name (node->type); + + case JSON_NODE_VALUE: + return g_type_name (G_VALUE_TYPE (&(node->data.value))); + } + + return "unknown"; +} + +const gchar * +json_node_type_get_name (JsonNodeType node_type) +{ + switch (node_type) + { + case JSON_NODE_OBJECT: + return "JsonObject"; + + case JSON_NODE_ARRAY: + return "JsonArray"; + + case JSON_NODE_NULL: + return "NULL"; + + case JSON_NODE_VALUE: + return "Value"; + + default: + g_assert_not_reached (); + break; + } + + return "unknown"; +} + +/** + * json_node_set_parent: + * @node: a #JsonNode + * @parent: (transfer none): the parent #JsonNode of @node + * + * Sets the parent #JsonNode of @node + * + * Since: 0.8 + */ +void +json_node_set_parent (JsonNode *node, + JsonNode *parent) +{ + g_return_if_fail (node != NULL); + + node->parent = parent; +} + +/** + * json_node_get_parent: + * @node: a #JsonNode + * + * Retrieves the parent #JsonNode of @node. + * + * Return value: (transfer none): the parent node, or %NULL if @node is + * the root node + */ +JsonNode * +json_node_get_parent (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + return node->parent; +} + +/** + * json_node_set_string: + * @node: a #JsonNode of type %JSON_NODE_VALUE + * @value: a string value + * + * Sets @value as the string content of the @node, replacing any existing + * content. + */ +void +json_node_set_string (JsonNode *node, + const gchar *value) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); + + if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING) + g_value_set_string (&(node->data.value), value); + else + { + GValue copy = { 0, }; + + g_value_init (©, G_TYPE_STRING); + g_value_set_string (©, value); + + json_node_set_value (node, ©); + + g_value_unset (©); + } +} + +/** + * json_node_get_string: + * @node: a #JsonNode of type %JSON_NODE_VALUE + * + * Gets the string value stored inside a #JsonNode + * + * Return value: a string value. + */ +const gchar * +json_node_get_string (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + if (JSON_NODE_TYPE (node) == JSON_NODE_NULL) + return NULL; + + if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING) + return g_value_get_string (&(node->data.value)); + + return NULL; +} + +/** + * json_node_dup_string: + * @node: a #JsonNode of type %JSON_NODE_VALUE + * + * Gets a copy of the string value stored inside a #JsonNode + * + * Return value: (transfer full): a newly allocated string containing a copy + * of the #JsonNode contents. Use g_free() to free the allocated resources + */ +gchar * +json_node_dup_string (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + if (JSON_NODE_TYPE (node) == JSON_NODE_NULL) + return NULL; + + if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING) + return g_value_dup_string (&(node->data.value)); + + return NULL; +} + +/** + * json_node_set_int: + * @node: a #JsonNode of type %JSON_NODE_VALUE + * @value: an integer value + * + * Sets @value as the integer content of the @node, replacing any existing + * content. + */ +void +json_node_set_int (JsonNode *node, + gint64 value) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); + + if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_INT64) + g_value_set_int64 (&(node->data.value), value); + else + { + GValue copy = { 0, }; + + g_value_init (©, G_TYPE_INT64); + g_value_set_int64 (©, value); + + json_node_set_value (node, ©); + + g_value_unset (©); + } +} + +/** + * json_node_get_int: + * @node: a #JsonNode of type %JSON_NODE_VALUE + * + * Gets the integer value stored inside a #JsonNode + * + * Return value: an integer value. + */ +gint64 +json_node_get_int (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, 0); + + if (JSON_NODE_TYPE (node) == JSON_NODE_NULL) + return 0; + + if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_INT64) + return g_value_get_int64 (&(node->data.value)); + + return 0; +} + +/** + * json_node_set_double: + * @node: a #JsonNode of type %JSON_NODE_VALUE + * @value: a double value + * + * Sets @value as the double content of the @node, replacing any existing + * content. + */ +void +json_node_set_double (JsonNode *node, + gdouble value) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); + + if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_DOUBLE) + g_value_set_double (&(node->data.value), value); + else + { + GValue copy = { 0, }; + + g_value_init (©, G_TYPE_DOUBLE); + g_value_set_double (©, value); + + json_node_set_value (node, ©); + + g_value_unset (©); + } +} + +/** + * json_node_get_double: + * @node: a #JsonNode of type %JSON_NODE_VALUE + * + * Gets the double value stored inside a #JsonNode + * + * Return value: a double value. + */ +gdouble +json_node_get_double (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, 0.0); + + if (JSON_NODE_TYPE (node) == JSON_NODE_NULL) + return 0; + + if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_DOUBLE) + return g_value_get_double (&(node->data.value)); + + return 0.0; +} + +/** + * json_node_set_boolean: + * @node: a #JsonNode of type %JSON_NODE_VALUE + * @value: a boolean value + * + * Sets @value as the boolean content of the @node, replacing any existing + * content. + */ +void +json_node_set_boolean (JsonNode *node, + gboolean value) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); + + if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_BOOLEAN) + g_value_set_boolean (&(node->data.value), value); + else + { + GValue copy = { 0, }; + + g_value_init (©, G_TYPE_BOOLEAN); + g_value_set_boolean (©, value); + + json_node_set_value (node, ©); + + g_value_unset (©); + } +} + +/** + * json_node_get_boolean: + * @node: a #JsonNode of type %JSON_NODE_VALUE + * + * Gets the boolean value stored inside a #JsonNode + * + * Return value: a boolean value. + */ +gboolean +json_node_get_boolean (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, FALSE); + + if (JSON_NODE_TYPE (node) == JSON_NODE_NULL) + return FALSE; + + if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_BOOLEAN) + return g_value_get_boolean (&(node->data.value)); + + return FALSE; +} + +/** + * json_node_get_node_type: + * @node: a #JsonNode + * + * Retrieves the #JsonNodeType of @node + * + * Return value: the type of the node + * + * Since: 0.8 + */ +JsonNodeType +json_node_get_node_type (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, JSON_NODE_NULL); + + return node->type; +} + +/** + * json_node_is_null: + * @node: a #JsonNode + * + * Checks whether @node is a %JSON_NODE_NULL + * + * A null node is not the same as a %NULL #JsonNode + * + * Return value: %TRUE if the node is null + * + * Since: 0.8 + */ +gboolean +json_node_is_null (JsonNode *node) +{ + g_return_val_if_fail (node != NULL, TRUE); + + return node->type == JSON_NODE_NULL; +} diff --git a/json-glib/json-glib/json-object.c b/json-glib/json-glib/json-object.c new file mode 100644 index 0000000..8f55098 --- /dev/null +++ b/json-glib/json-glib/json-object.c @@ -0,0 +1,856 @@ +/* json-object.c - JSON object implementation + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "json-types-private.h" + +/** + * SECTION:json-object + * @short_description: a JSON object representation + * + * #JsonObject is the representation of the object type inside JSON. It contains + * #JsonNodes, which may contain fundamental types, arrays or other + * objects. Each member of an object is accessed using its name. + * + * Since objects can be expensive, they are reference counted. You can control + * the lifetime of a #JsonObject using json_object_ref() and json_object_unref(). + * + * To add or overwrite a member with a given name, use json_object_set_member(). + * To extract a member with a given name, use json_object_get_member(). + * To retrieve the list of members, use json_object_get_members(). + * To retrieve the size of the object (that is, the number of members it has), + * use json_object_get_size(). + */ + +G_DEFINE_BOXED_TYPE (JsonObject, json_object, json_object_ref, json_object_unref); + +/** + * json_object_new: + * + * Creates a new #JsonObject, an JSON object type representation. + * + * Return value: the newly created #JsonObject + */ +JsonObject * +json_object_new (void) +{ + JsonObject *object; + + object = g_slice_new (JsonObject); + + object->ref_count = 1; + object->members = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + (GDestroyNotify) json_node_free); + object->members_ordered = NULL; + + return object; +} + +/** + * json_object_ref: + * @object: a #JsonObject + * + * Increase by one the reference count of a #JsonObject. + * + * Return value: the passed #JsonObject, with the reference count + * increased by one. + */ +JsonObject * +json_object_ref (JsonObject *object) +{ + g_return_val_if_fail (object != NULL, NULL); + g_return_val_if_fail (object->ref_count > 0, NULL); + + g_atomic_int_add (&object->ref_count, 1); + + return object; +} + +/** + * json_object_unref: + * @object: a #JsonObject + * + * Decreases by one the reference count of a #JsonObject. If the + * reference count reaches zero, the object is destroyed and all + * its allocated resources are freed. + */ +void +json_object_unref (JsonObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (object->ref_count > 0); + + if (g_atomic_int_dec_and_test (&object->ref_count)) + { + g_list_free (object->members_ordered); + g_hash_table_destroy (object->members); + object->members_ordered = NULL; + object->members = NULL; + + g_slice_free (JsonObject, object); + } +} + +static inline void +object_set_member_internal (JsonObject *object, + const gchar *member_name, + JsonNode *node) +{ + gchar *name = g_strdup (member_name); + + if (g_hash_table_lookup (object->members, name) == NULL) + object->members_ordered = g_list_prepend (object->members_ordered, name); + else + { + GList *l; + + /* if the member already exists then we need to replace the + * pointer to its name, to avoid keeping invalid pointers + * once we replace the key in the hash table + */ + l = g_list_find_custom (object->members_ordered, name, (GCompareFunc) strcmp); + if (l != NULL) + l->data = name; + } + + g_hash_table_replace (object->members, name, node); +} + +/** + * json_object_add_member: + * @object: a #JsonObject + * @member_name: the name of the member + * @node: (transfer full): the value of the member + * + * Adds a member named @member_name and containing @node into a #JsonObject. + * The object will take ownership of the #JsonNode. + * + * This function will return if the @object already contains a member + * @member_name. + * + * Deprecated: 0.8: Use json_object_set_member() instead + */ +void +json_object_add_member (JsonObject *object, + const gchar *member_name, + JsonNode *node) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + g_return_if_fail (node != NULL); + + if (json_object_has_member (object, member_name)) + { + g_warning ("JsonObject already has a `%s' member of type `%s'", + member_name, + json_node_type_name (node)); + return; + } + + object_set_member_internal (object, member_name, node); +} + +/** + * json_object_set_member: + * @object: a #JsonObject + * @member_name: the name of the member + * @node: (transfer full): the value of the member + * + * Sets @node as the value of @member_name inside @object. + * + * If @object already contains a member called @member_name then + * the member's current value is overwritten. Otherwise, a new + * member is added to @object. + * + * Since: 0.8 + */ +void +json_object_set_member (JsonObject *object, + const gchar *member_name, + JsonNode *node) +{ + JsonNode *old_node; + + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + g_return_if_fail (node != NULL); + + old_node = g_hash_table_lookup (object->members, member_name); + if (old_node == NULL) + goto set_member; + + if (old_node == node) + return; + +set_member: + object_set_member_internal (object, member_name, node); +} + +/** + * json_object_set_int_member: + * @object: a #JsonObject + * @member_name: the name of the member + * @value: the value of the member + * + * Convenience function for setting an integer @value of + * @member_name inside @object. + * + * See also: json_object_set_member() + * + * Since: 0.8 + */ +void +json_object_set_int_member (JsonObject *object, + const gchar *member_name, + gint64 value) +{ + JsonNode *node; + + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + + node = json_node_new (JSON_NODE_VALUE); + json_node_set_int (node, value); + object_set_member_internal (object, member_name, node); +} + +/** + * json_object_set_double_member: + * @object: a #JsonObject + * @member_name: the name of the member + * @value: the value of the member + * + * Convenience function for setting a floating point @value + * of @member_name inside @object. + * + * See also: json_object_set_member() + * + * Since: 0.8 + */ +void +json_object_set_double_member (JsonObject *object, + const gchar *member_name, + gdouble value) +{ + JsonNode *node; + + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + + node = json_node_new (JSON_NODE_VALUE); + json_node_set_double (node, value); + object_set_member_internal (object, member_name, node); +} + +/** + * json_object_set_boolean_member: + * @object: a #JsonObject + * @member_name: the name of the member + * @value: the value of the member + * + * Convenience function for setting a boolean @value of + * @member_name inside @object. + * + * See also: json_object_set_member() + * + * Since: 0.8 + */ +void +json_object_set_boolean_member (JsonObject *object, + const gchar *member_name, + gboolean value) +{ + JsonNode *node; + + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + + node = json_node_new (JSON_NODE_VALUE); + json_node_set_boolean (node, value); + object_set_member_internal (object, member_name, node); +} + +/** + * json_object_set_string_member: + * @object: a #JsonObject + * @member_name: the name of the member + * @value: the value of the member + * + * Convenience function for setting a string @value of + * @member_name inside @object. + * + * See also: json_object_set_member() + * + * Since: 0.8 + */ +void +json_object_set_string_member (JsonObject *object, + const gchar *member_name, + const gchar *value) +{ + JsonNode *node; + + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + + if (value != NULL) + { + node = json_node_new (JSON_NODE_VALUE); + json_node_set_string (node, value); + } + else + node = json_node_new (JSON_NODE_NULL); + + object_set_member_internal (object, member_name, node); +} + +/** + * json_object_set_null_member: + * @object: a #JsonObject + * @member_name: the name of the member + * + * Convenience function for setting a null @value of + * @member_name inside @object. + * + * See also: json_object_set_member() + * + * Since: 0.8 + */ +void +json_object_set_null_member (JsonObject *object, + const gchar *member_name) +{ + JsonNode *node; + + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + + node = json_node_new (JSON_NODE_NULL); + object_set_member_internal (object, member_name, node); +} + +/** + * json_object_set_array_member: + * @object: a #JsonObject + * @member_name: the name of the member + * @value: (transfer full): the value of the member + * + * Convenience function for setting an array @value of + * @member_name inside @object. + * + * The @object will take ownership of the passed #JsonArray + * + * See also: json_object_set_member() + * + * Since: 0.8 + */ +void +json_object_set_array_member (JsonObject *object, + const gchar *member_name, + JsonArray *value) +{ + JsonNode *node; + + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + + if (value != NULL) + { + node = json_node_new (JSON_NODE_ARRAY); + json_node_take_array (node, value); + } + else + node = json_node_new (JSON_NODE_NULL); + + object_set_member_internal (object, member_name, node); +} + +/** + * json_object_set_object_member: + * @object: a #JsonObject + * @member_name: the name of the member + * @value: (transfer full): the value of the member + * + * Convenience function for setting an object @value of + * @member_name inside @object. + * + * The @object will take ownership of the passed #JsonObject + * + * See also: json_object_set_member() + * + * Since: 0.8 + */ +void +json_object_set_object_member (JsonObject *object, + const gchar *member_name, + JsonObject *value) +{ + JsonNode *node; + + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + + if (value != NULL) + { + node = json_node_new (JSON_NODE_OBJECT); + json_node_take_object (node, value); + } + else + node = json_node_new (JSON_NODE_NULL); + + object_set_member_internal (object, member_name, node); +} + +/** + * json_object_get_members: + * @object: a #JsonObject + * + * Retrieves all the names of the members of a #JsonObject. You can + * obtain the value for each member using json_object_get_member(). + * + * Return value: (element-type utf8) (transfer container): a #GList + * of member names. The content of the list is owned by the #JsonObject + * and should never be modified or freed. When you have finished using + * the returned list, use g_list_free() to free the resources it has + * allocated. + */ +GList * +json_object_get_members (JsonObject *object) +{ + GList *copy; + + g_return_val_if_fail (object != NULL, NULL); + + copy = g_list_copy (object->members_ordered); + + return g_list_reverse (copy); +} + +/** + * json_object_get_values: + * @object: a #JsonObject + * + * Retrieves all the values of the members of a #JsonObject. + * + * Return value: (element-type JsonNode) (transfer container): a #GList of + * #JsonNodes. The content of the list is owned by the #JsonObject + * and should never be modified or freed. When you have finished using the + * returned list, use g_list_free() to free the resources it has allocated. + */ +GList * +json_object_get_values (JsonObject *object) +{ + GList *values, *l; + + g_return_val_if_fail (object != NULL, NULL); + + values = NULL; + for (l = object->members_ordered; l != NULL; l = l->next) + values = g_list_prepend (values, g_hash_table_lookup (object->members, l->data)); + + return values; +} + +/** + * json_object_dup_member: + * @object: a #JsonObject + * @member_name: the name of the JSON object member to access + * + * Retrieves a copy of the #JsonNode containing the value of @member_name + * inside a #JsonObject + * + * Return value: (transfer full): a copy of the node for the requested + * object member or %NULL. Use json_node_free() when done. + * + * Since: 0.6 + */ +JsonNode * +json_object_dup_member (JsonObject *object, + const gchar *member_name) +{ + JsonNode *retval; + + g_return_val_if_fail (object != NULL, NULL); + g_return_val_if_fail (member_name != NULL, NULL); + + retval = json_object_get_member (object, member_name); + if (!retval) + return NULL; + + return json_node_copy (retval); +} + +static inline JsonNode * +object_get_member_internal (JsonObject *object, + const gchar *member_name) +{ + return g_hash_table_lookup (object->members, member_name); +} + +/** + * json_object_get_member: + * @object: a #JsonObject + * @member_name: the name of the JSON object member to access + * + * Retrieves the #JsonNode containing the value of @member_name inside + * a #JsonObject. + * + * Return value: (transfer none): a pointer to the node for the requested object + * member, or %NULL + */ +JsonNode * +json_object_get_member (JsonObject *object, + const gchar *member_name) +{ + g_return_val_if_fail (object != NULL, NULL); + g_return_val_if_fail (member_name != NULL, NULL); + + return object_get_member_internal (object, member_name); +} + +/** + * json_object_get_int_member: + * @object: a #JsonObject + * @member_name: the name of the member + * + * Convenience function that retrieves the integer value + * stored in @member_name of @object + * + * See also: json_object_get_member() + * + * Return value: the integer value of the object's member + * + * Since: 0.8 + */ +gint64 +json_object_get_int_member (JsonObject *object, + const gchar *member_name) +{ + JsonNode *node; + + g_return_val_if_fail (object != NULL, 0); + g_return_val_if_fail (member_name != NULL, 0); + + node = object_get_member_internal (object, member_name); + g_return_val_if_fail (node != NULL, 0); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0); + + return json_node_get_int (node); +} + +/** + * json_object_get_double_member: + * @object: a #JsonObject + * @member_name: the name of the member + * + * Convenience function that retrieves the floating point value + * stored in @member_name of @object + * + * See also: json_object_get_member() + * + * Return value: the floating point value of the object's member + * + * Since: 0.8 + */ +gdouble +json_object_get_double_member (JsonObject *object, + const gchar *member_name) +{ + JsonNode *node; + + g_return_val_if_fail (object != NULL, 0.0); + g_return_val_if_fail (member_name != NULL, 0.0); + + node = object_get_member_internal (object, member_name); + g_return_val_if_fail (node != NULL, 0.0); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0.0); + + return json_node_get_double (node); +} + +/** + * json_object_get_boolean_member: + * @object: a #JsonObject + * @member_name: the name of the member + * + * Convenience function that retrieves the boolean value + * stored in @member_name of @object + * + * See also: json_object_get_member() + * + * Return value: the boolean value of the object's member + * + * Since: 0.8 + */ +gboolean +json_object_get_boolean_member (JsonObject *object, + const gchar *member_name) +{ + JsonNode *node; + + g_return_val_if_fail (object != NULL, FALSE); + g_return_val_if_fail (member_name != NULL, FALSE); + + node = object_get_member_internal (object, member_name); + g_return_val_if_fail (node != NULL, FALSE); + g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, FALSE); + + return json_node_get_boolean (node); +} + +/** + * json_object_get_null_member: + * @object: a #JsonObject + * @member_name: the name of the member + * + * Convenience function that checks whether the value + * stored in @member_name of @object is null + * + * See also: json_object_get_member() + * + * Return value: %TRUE if the value is null + * + * Since: 0.8 + */ +gboolean +json_object_get_null_member (JsonObject *object, + const gchar *member_name) +{ + JsonNode *node; + + g_return_val_if_fail (object != NULL, FALSE); + g_return_val_if_fail (member_name != NULL, FALSE); + + node = object_get_member_internal (object, member_name); + g_return_val_if_fail (node != NULL, FALSE); + + return JSON_NODE_TYPE (node) == JSON_NODE_NULL; +} + +/** + * json_object_get_string_member: + * @object: a #JsonObject + * @member_name: the name of the member + * + * Convenience function that retrieves the string value + * stored in @member_name of @object + * + * See also: json_object_get_member() + * + * Return value: the string value of the object's member + * + * Since: 0.8 + */ +const gchar * +json_object_get_string_member (JsonObject *object, + const gchar *member_name) +{ + JsonNode *node; + + g_return_val_if_fail (object != NULL, NULL); + g_return_val_if_fail (member_name != NULL, NULL); + + node = object_get_member_internal (object, member_name); + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_HOLDS_VALUE (node) || JSON_NODE_HOLDS_NULL (node), NULL); + + if (JSON_NODE_HOLDS_NULL (node)) + return NULL; + + return json_node_get_string (node); +} + +/** + * json_object_get_array_member: + * @object: a #JsonObject + * @member_name: the name of the member + * + * Convenience function that retrieves the array + * stored in @member_name of @object + * + * See also: json_object_get_member() + * + * Return value: (transfer none): the array inside the object's member + * + * Since: 0.8 + */ +JsonArray * +json_object_get_array_member (JsonObject *object, + const gchar *member_name) +{ + JsonNode *node; + + g_return_val_if_fail (object != NULL, NULL); + g_return_val_if_fail (member_name != NULL, NULL); + + node = object_get_member_internal (object, member_name); + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_HOLDS_ARRAY (node) || JSON_NODE_HOLDS_NULL (node), NULL); + + if (JSON_NODE_HOLDS_NULL (node)) + return NULL; + + return json_node_get_array (node); +} + +/** + * json_object_get_object_member: + * @object: a #JsonObject + * @member_name: the name of the member + * + * Convenience function that retrieves the object + * stored in @member_name of @object + * + * See also: json_object_get_member() + * + * Return value: (transfer none): the object inside the object's member + * + * Since: 0.8 + */ +JsonObject * +json_object_get_object_member (JsonObject *object, + const gchar *member_name) +{ + JsonNode *node; + + g_return_val_if_fail (object != NULL, NULL); + g_return_val_if_fail (member_name != NULL, NULL); + + node = object_get_member_internal (object, member_name); + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (node) || JSON_NODE_HOLDS_NULL (node), NULL); + + if (JSON_NODE_HOLDS_NULL (node)) + return NULL; + + return json_node_get_object (node); +} + +/** + * json_object_has_member: + * @object: a #JsonObject + * @member_name: the name of a JSON object member + * + * Checks whether @object has a member named @member_name. + * + * Return value: %TRUE if the JSON object has the requested member + */ +gboolean +json_object_has_member (JsonObject *object, + const gchar *member_name) +{ + g_return_val_if_fail (object != NULL, FALSE); + g_return_val_if_fail (member_name != NULL, FALSE); + + return (g_hash_table_lookup (object->members, member_name) != NULL); +} + +/** + * json_object_get_size: + * @object: a #JsonObject + * + * Retrieves the number of members of a #JsonObject. + * + * Return value: the number of members + */ +guint +json_object_get_size (JsonObject *object) +{ + g_return_val_if_fail (object != NULL, 0); + + return g_hash_table_size (object->members); +} + +/** + * json_object_remove_member: + * @object: a #JsonObject + * @member_name: the name of the member to remove + * + * Removes @member_name from @object, freeing its allocated resources. + */ +void +json_object_remove_member (JsonObject *object, + const gchar *member_name) +{ + GList *l; + + g_return_if_fail (object != NULL); + g_return_if_fail (member_name != NULL); + + for (l = object->members_ordered; l != NULL; l = l->next) + { + const gchar *name = l->data; + + if (g_strcmp0 (name, member_name) == 0) + { + object->members_ordered = g_list_delete_link (object->members_ordered, l); + break; + } + } + + g_hash_table_remove (object->members, member_name); +} + +/** + * json_object_foreach_member: + * @object: a #JsonObject + * @func: (scope call): the function to be called on each member + * @data: (closure): data to be passed to the function + * + * Iterates over all members of @object and calls @func on + * each one of them. + * + * It is safe to change the value of a #JsonNode of the @object + * from within the iterator @func, but it is not safe to add or + * remove members from the @object. + * + * Since: 0.8 + */ +void +json_object_foreach_member (JsonObject *object, + JsonObjectForeach func, + gpointer data) +{ + GList *members, *l; + + g_return_if_fail (object != NULL); + g_return_if_fail (func != NULL); + + /* the list is stored in reverse order to have constant time additions */ + members = g_list_last (object->members_ordered); + for (l = members; l != NULL; l = l->prev) + { + const gchar *member_name = l->data; + JsonNode *member_node = g_hash_table_lookup (object->members, member_name); + + func (object, member_name, member_node, data); + } +} diff --git a/json-glib/json-glib/json-parser.c b/json-glib/json-glib/json-parser.c new file mode 100644 index 0000000..526e2de --- /dev/null +++ b/json-glib/json-glib/json-parser.c @@ -0,0 +1,1430 @@ +/* json-parser.c - JSON streams parser + * + * This file is part of JSON-GLib + * + * Copyright © 2007, 2008, 2009 OpenedHand Ltd + * Copyright © 2009, 2010 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:json-parser + * @short_description: Parse JSON data streams + * + * #JsonParser provides an object for parsing a JSON data stream, either + * inside a file or inside a static buffer. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "json-types-private.h" + +#include "json-debug.h" +#include "json-marshal.h" +#include "json-parser.h" +#include "json-scanner.h" + +GQuark +json_parser_error_quark (void) +{ + return g_quark_from_static_string ("json-parser-error"); +} + +#define JSON_PARSER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), JSON_TYPE_PARSER, JsonParserPrivate)) + +struct _JsonParserPrivate +{ + JsonNode *root; + JsonNode *current_node; + + JsonScanner *scanner; + + JsonParserError error_code; + GError *last_error; + + gchar *variable_name; + gchar *filename; + + guint has_assignment : 1; + guint is_filename : 1; +}; + +static const gchar symbol_names[] = + "true\0" + "false\0" + "null\0" + "var\0"; + +static const struct +{ + guint name_offset; + guint token; +} symbols[] = { + { 0, JSON_TOKEN_TRUE }, + { 5, JSON_TOKEN_FALSE }, + { 11, JSON_TOKEN_NULL }, + { 16, JSON_TOKEN_VAR } +}; + +static const guint n_symbols = G_N_ELEMENTS (symbols); + +enum +{ + PARSE_START, + OBJECT_START, + OBJECT_MEMBER, + OBJECT_END, + ARRAY_START, + ARRAY_ELEMENT, + ARRAY_END, + PARSE_END, + ERROR, + + LAST_SIGNAL +}; + +static guint parser_signals[LAST_SIGNAL] = { 0, }; + +G_DEFINE_TYPE (JsonParser, json_parser, G_TYPE_OBJECT); + +static guint json_parse_array (JsonParser *parser, + JsonScanner *scanner, + JsonNode **node); +static guint json_parse_object (JsonParser *parser, + JsonScanner *scanner, + JsonNode **node); + +static inline void +json_parser_clear (JsonParser *parser) +{ + JsonParserPrivate *priv = parser->priv; + + g_free (priv->variable_name); + priv->variable_name = NULL; + + if (priv->last_error) + { + g_error_free (priv->last_error); + priv->last_error = NULL; + } + + if (priv->root) + { + json_node_free (priv->root); + priv->root = NULL; + } +} + +static void +json_parser_dispose (GObject *gobject) +{ + json_parser_clear (JSON_PARSER (gobject)); + + G_OBJECT_CLASS (json_parser_parent_class)->dispose (gobject); +} + +static void +json_parser_finalize (GObject *gobject) +{ + JsonParserPrivate *priv = JSON_PARSER (gobject)->priv; + + g_free (priv->variable_name); + g_free (priv->filename); + + G_OBJECT_CLASS (json_parser_parent_class)->finalize (gobject); +} + +static void +json_parser_class_init (JsonParserClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (JsonParserPrivate)); + + gobject_class->dispose = json_parser_dispose; + gobject_class->finalize = json_parser_finalize; + + /** + * JsonParser::parse-start: + * @parser: the #JsonParser that received the signal + * + * The ::parse-start signal is emitted when the parser began parsing + * a JSON data stream. + */ + parser_signals[PARSE_START] = + g_signal_new ("parse-start", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (JsonParserClass, parse_start), + NULL, NULL, + _json_marshal_VOID__VOID, + G_TYPE_NONE, 0); + /** + * JsonParser::parse-end: + * @parser: the #JsonParser that received the signal + * + * The ::parse-end signal is emitted when the parser successfully + * finished parsing a JSON data stream + */ + parser_signals[PARSE_END] = + g_signal_new ("parse-end", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (JsonParserClass, parse_end), + NULL, NULL, + _json_marshal_VOID__VOID, + G_TYPE_NONE, 0); + /** + * JsonParser::object-start: + * @parser: the #JsonParser that received the signal + * + * The ::object-start signal is emitted each time the #JsonParser + * starts parsing a #JsonObject. + */ + parser_signals[OBJECT_START] = + g_signal_new ("object-start", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (JsonParserClass, object_start), + NULL, NULL, + _json_marshal_VOID__VOID, + G_TYPE_NONE, 0); + /** + * JsonParser::object-member: + * @parser: the #JsonParser that received the signal + * @object: a #JsonObject + * @member_name: the name of the newly parsed member + * + * The ::object-member signal is emitted each time the #JsonParser + * has successfully parsed a single member of a #JsonObject. The + * object and member are passed to the signal handlers. + */ + parser_signals[OBJECT_MEMBER] = + g_signal_new ("object-member", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (JsonParserClass, object_member), + NULL, NULL, + _json_marshal_VOID__BOXED_STRING, + G_TYPE_NONE, 2, + JSON_TYPE_OBJECT, + G_TYPE_STRING); + /** + * JsonParser::object-end: + * @parser: the #JsonParser that received the signal + * @object: the parsed #JsonObject + * + * The ::object-end signal is emitted each time the #JsonParser + * has successfully parsed an entire #JsonObject. + */ + parser_signals[OBJECT_END] = + g_signal_new ("object-end", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (JsonParserClass, object_end), + NULL, NULL, + _json_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + JSON_TYPE_OBJECT); + /** + * JsonParser::array-start: + * @parser: the #JsonParser that received the signal + * + * The ::array-start signal is emitted each time the #JsonParser + * starts parsing a #JsonArray + */ + parser_signals[ARRAY_START] = + g_signal_new ("array-start", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (JsonParserClass, array_start), + NULL, NULL, + _json_marshal_VOID__VOID, + G_TYPE_NONE, 0); + /** + * JsonParser::array-element: + * @parser: the #JsonParser that received the signal + * @array: a #JsonArray + * @index_: the index of the newly parsed element + * + * The ::array-element signal is emitted each time the #JsonParser + * has successfully parsed a single element of a #JsonArray. The + * array and element index are passed to the signal handlers. + */ + parser_signals[ARRAY_ELEMENT] = + g_signal_new ("array-element", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (JsonParserClass, array_element), + NULL, NULL, + _json_marshal_VOID__BOXED_INT, + G_TYPE_NONE, 2, + JSON_TYPE_ARRAY, + G_TYPE_INT); + /** + * JsonParser::array-end: + * @parser: the #JsonParser that received the signal + * @array: the parsed #JsonArray + * + * The ::array-end signal is emitted each time the #JsonParser + * has successfully parsed an entire #JsonArray + */ + parser_signals[ARRAY_END] = + g_signal_new ("array-end", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (JsonParserClass, array_end), + NULL, NULL, + _json_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + JSON_TYPE_ARRAY); + /** + * JsonParser::error: + * @parser: the parser instance that received the signal + * @error: a pointer to the #GError + * + * The ::error signal is emitted each time a #JsonParser encounters + * an error in a JSON stream. + */ + parser_signals[ERROR] = + g_signal_new ("error", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (JsonParserClass, error), + NULL, NULL, + _json_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); +} + +static void +json_parser_init (JsonParser *parser) +{ + JsonParserPrivate *priv; + + parser->priv = priv = JSON_PARSER_GET_PRIVATE (parser); + + priv->root = NULL; + priv->current_node = NULL; + + priv->error_code = JSON_PARSER_ERROR_PARSE; + priv->last_error = NULL; + + priv->has_assignment = FALSE; + priv->variable_name = NULL; + + priv->is_filename = FALSE; + priv->filename = FALSE; +} + +static guint +json_parse_value (JsonParser *parser, + JsonScanner *scanner, + guint token, + JsonNode **node) +{ + JsonParserPrivate *priv = parser->priv; + JsonNode *current_node = priv->current_node; + gboolean is_negative = FALSE; + + if (token == '-') + { + guint next_token = json_scanner_peek_next_token (scanner); + + if (next_token == G_TOKEN_INT || + next_token == G_TOKEN_FLOAT) + { + is_negative = TRUE; + token = json_scanner_get_next_token (scanner); + } + else + return G_TOKEN_INT; + } + + switch (token) + { + case G_TOKEN_INT: + *node = json_node_new (JSON_NODE_VALUE); + JSON_NOTE (PARSER, "abs(node): %" G_GINT64_FORMAT " (sign: %s)", + scanner->value.v_int64, + is_negative ? "negative" : "positive"); + json_node_set_int (*node, is_negative ? scanner->value.v_int64 * -1 + : scanner->value.v_int64); + break; + + case G_TOKEN_FLOAT: + *node = json_node_new (JSON_NODE_VALUE); + JSON_NOTE (PARSER, "abs(node): %.6f (sign: %s)", + scanner->value.v_float, + is_negative ? "negative" : "positive"); + json_node_set_double (*node, is_negative ? scanner->value.v_float * -1.0 + : scanner->value.v_float); + break; + + case G_TOKEN_STRING: + *node = json_node_new (JSON_NODE_VALUE); + JSON_NOTE (PARSER, "node: '%s'", + scanner->value.v_string); + json_node_set_string (*node, scanner->value.v_string); + break; + + case JSON_TOKEN_TRUE: + case JSON_TOKEN_FALSE: + *node = json_node_new (JSON_NODE_VALUE); + JSON_NOTE (PARSER, "node: '%s'", + JSON_TOKEN_TRUE ? "" : ""); + json_node_set_boolean (*node, token == JSON_TOKEN_TRUE ? TRUE : FALSE); + break; + + case JSON_TOKEN_NULL: + *node = json_node_new (JSON_NODE_NULL); + JSON_NOTE (PARSER, "node: "); + break; + + default: + { + JsonNodeType cur_type; + + *node = NULL; + + cur_type = json_node_get_node_type (current_node); + if (cur_type == JSON_NODE_ARRAY) + return G_TOKEN_RIGHT_BRACE; + else if (cur_type == JSON_NODE_OBJECT) + return G_TOKEN_RIGHT_CURLY; + else + { + priv->error_code = JSON_PARSER_ERROR_INVALID_BAREWORD; + return G_TOKEN_SYMBOL; + } + } + } + + return G_TOKEN_NONE; +} + +static guint +json_parse_array (JsonParser *parser, + JsonScanner *scanner, + JsonNode **node) +{ + JsonParserPrivate *priv = parser->priv; + JsonNode *old_current; + JsonArray *array; + guint token; + gint idx; + + old_current = priv->current_node; + priv->current_node = json_node_new (JSON_NODE_ARRAY); + + array = json_array_new (); + + token = json_scanner_get_next_token (scanner); + g_assert (token == G_TOKEN_LEFT_BRACE); + + g_signal_emit (parser, parser_signals[ARRAY_START], 0); + + idx = 0; + while (token != G_TOKEN_RIGHT_BRACE) + { + guint next_token = json_scanner_peek_next_token (scanner); + JsonNode *element = NULL; + + /* parse the element */ + switch (next_token) + { + case G_TOKEN_LEFT_BRACE: + JSON_NOTE (PARSER, "Nested array at index %d", idx); + token = json_parse_array (parser, scanner, &element); + break; + + case G_TOKEN_LEFT_CURLY: + JSON_NOTE (PARSER, "Nested object at index %d", idx); + token = json_parse_object (parser, scanner, &element); + break; + + case G_TOKEN_INT: + case G_TOKEN_FLOAT: + case G_TOKEN_STRING: + case '-': + case JSON_TOKEN_TRUE: + case JSON_TOKEN_FALSE: + case JSON_TOKEN_NULL: + token = json_scanner_get_next_token (scanner); + token = json_parse_value (parser, scanner, token, &element); + break; + + case G_TOKEN_RIGHT_BRACE: + goto array_done; + + default: + if (next_token != G_TOKEN_RIGHT_BRACE) + token = G_TOKEN_RIGHT_BRACE; + break; + } + + if (token != G_TOKEN_NONE || element == NULL) + { + /* the json_parse_* functions will have set the error code */ + json_array_unref (array); + json_node_free (priv->current_node); + priv->current_node = old_current; + + return token; + } + + next_token = json_scanner_peek_next_token (scanner); + + if (next_token == G_TOKEN_COMMA) + { + token = json_scanner_get_next_token (scanner); + next_token = json_scanner_peek_next_token (scanner); + + /* look for trailing commas */ + if (next_token == G_TOKEN_RIGHT_BRACE) + { + priv->error_code = JSON_PARSER_ERROR_TRAILING_COMMA; + + json_array_unref (array); + json_node_free (priv->current_node); + json_node_free (element); + priv->current_node = old_current; + + return G_TOKEN_RIGHT_BRACE; + } + } + + JSON_NOTE (PARSER, "Array element %d completed", idx + 1); + json_node_set_parent (element, priv->current_node); + json_array_add_element (array, element); + + g_signal_emit (parser, parser_signals[ARRAY_ELEMENT], 0, + array, + idx); + + token = next_token; + } + +array_done: + json_scanner_get_next_token (scanner); + + json_node_take_array (priv->current_node, array); + json_node_set_parent (priv->current_node, old_current); + + g_signal_emit (parser, parser_signals[ARRAY_END], 0, array); + + if (node != NULL && *node == NULL) + *node = priv->current_node; + + priv->current_node = old_current; + + return G_TOKEN_NONE; +} + +static guint +json_parse_object (JsonParser *parser, + JsonScanner *scanner, + JsonNode **node) +{ + JsonParserPrivate *priv = parser->priv; + JsonObject *object; + JsonNode *old_current; + guint token; + + old_current = priv->current_node; + priv->current_node = json_node_new (JSON_NODE_OBJECT); + + object = json_object_new (); + + token = json_scanner_get_next_token (scanner); + g_assert (token == G_TOKEN_LEFT_CURLY); + + g_signal_emit (parser, parser_signals[OBJECT_START], 0); + + while (token != G_TOKEN_RIGHT_CURLY) + { + guint next_token = json_scanner_peek_next_token (scanner); + JsonNode *member = NULL; + gchar *name; + + /* we need to abort here because empty objects do not + * have member names + */ + if (next_token == G_TOKEN_RIGHT_CURLY) + break; + + /* parse the member's name */ + if (next_token != G_TOKEN_STRING) + { + JSON_NOTE (PARSER, "Missing object member name"); + + priv->error_code = JSON_PARSER_ERROR_PARSE; + + json_object_unref (object); + json_node_free (priv->current_node); + priv->current_node = old_current; + + return G_TOKEN_STRING; + } + + /* member name */ + token = json_scanner_get_next_token (scanner); + name = g_strdup (scanner->value.v_string); + JSON_NOTE (PARSER, "Object member '%s'", name); + + /* a colon separates names from values */ + next_token = json_scanner_peek_next_token (scanner); + if (next_token != ':') + { + JSON_NOTE (PARSER, "Missing object member name separator"); + + priv->error_code = JSON_PARSER_ERROR_MISSING_COLON; + + g_free (name); + json_object_unref (object); + json_node_free (priv->current_node); + priv->current_node = old_current; + + return ':'; + } + + /* we swallow the ':' */ + token = json_scanner_get_next_token (scanner); + g_assert (token == ':'); + next_token = json_scanner_peek_next_token (scanner); + + /* parse the member's value */ + switch (next_token) + { + case G_TOKEN_LEFT_BRACE: + JSON_NOTE (PARSER, "Nested array at member %s", name); + token = json_parse_array (parser, scanner, &member); + break; + + case G_TOKEN_LEFT_CURLY: + JSON_NOTE (PARSER, "Nested object at member %s", name); + token = json_parse_object (parser, scanner, &member); + break; + + case G_TOKEN_INT: + case G_TOKEN_FLOAT: + case G_TOKEN_STRING: + case '-': + case JSON_TOKEN_TRUE: + case JSON_TOKEN_FALSE: + case JSON_TOKEN_NULL: + token = json_scanner_get_next_token (scanner); + token = json_parse_value (parser, scanner, token, &member); + break; + + default: + /* once a member name is defined we need a value */ + token = G_TOKEN_SYMBOL; + break; + } + + if (token != G_TOKEN_NONE || member == NULL) + { + /* the json_parse_* functions will have set the error code */ + g_free (name); + json_object_unref (object); + json_node_free (priv->current_node); + priv->current_node = old_current; + + return token; + } + + next_token = json_scanner_peek_next_token (scanner); + if (next_token == G_TOKEN_COMMA) + { + token = json_scanner_get_next_token (scanner); + next_token = json_scanner_peek_next_token (scanner); + + /* look for trailing commas */ + if (next_token == G_TOKEN_RIGHT_CURLY) + { + priv->error_code = JSON_PARSER_ERROR_TRAILING_COMMA; + + json_object_unref (object); + json_node_free (member); + json_node_free (priv->current_node); + priv->current_node = old_current; + + return G_TOKEN_RIGHT_BRACE; + } + } + else if (next_token == G_TOKEN_STRING) + { + priv->error_code = JSON_PARSER_ERROR_MISSING_COMMA; + + json_object_unref (object); + json_node_free (member); + json_node_free (priv->current_node); + priv->current_node = old_current; + + return G_TOKEN_COMMA; + } + + JSON_NOTE (PARSER, "Object member '%s' completed", name); + json_node_set_parent (member, priv->current_node); + json_object_set_member (object, name, member); + + g_signal_emit (parser, parser_signals[OBJECT_MEMBER], 0, + object, + name); + + g_free (name); + + token = next_token; + } + + json_scanner_get_next_token (scanner); + + json_node_take_object (priv->current_node, object); + json_node_set_parent (priv->current_node, old_current); + + g_signal_emit (parser, parser_signals[OBJECT_END], 0, object); + + if (node != NULL && *node == NULL) + *node = priv->current_node; + + priv->current_node = old_current; + + return G_TOKEN_NONE; +} + +static guint +json_parse_statement (JsonParser *parser, + JsonScanner *scanner) +{ + JsonParserPrivate *priv = parser->priv; + guint token; + + token = json_scanner_peek_next_token (scanner); + switch (token) + { + case G_TOKEN_LEFT_CURLY: + JSON_NOTE (PARSER, "Statement is object declaration"); + return json_parse_object (parser, scanner, &priv->root); + + case G_TOKEN_LEFT_BRACE: + JSON_NOTE (PARSER, "Statement is array declaration"); + return json_parse_array (parser, scanner, &priv->root); + + /* some web APIs are not only passing the data structures: they are + * also passing an assigment, which makes parsing horribly complicated + * only because web developers are lazy, and writing "var foo = " is + * evidently too much to request from them. + */ + case JSON_TOKEN_VAR: + { + guint next_token; + gchar *name; + + JSON_NOTE (PARSER, "Statement is an assignment"); + + /* swallow the 'var' token... */ + token = json_scanner_get_next_token (scanner); + + /* ... swallow the variable name... */ + next_token = json_scanner_get_next_token (scanner); + if (next_token != G_TOKEN_IDENTIFIER) + { + priv->error_code = JSON_PARSER_ERROR_INVALID_BAREWORD; + return G_TOKEN_IDENTIFIER; + } + + name = g_strdup (scanner->value.v_identifier); + + /* ... and finally swallow the '=' */ + next_token = json_scanner_get_next_token (scanner); + if (next_token != '=') + return '='; + + priv->has_assignment = TRUE; + priv->variable_name = name; + + token = json_parse_statement (parser, scanner); + + /* remove the trailing semi-colon */ + next_token = json_scanner_peek_next_token (scanner); + if (next_token == ';') + { + token = json_scanner_get_next_token (scanner); + return G_TOKEN_NONE; + } + + return token; + } + break; + + case JSON_TOKEN_NULL: + case JSON_TOKEN_TRUE: + case JSON_TOKEN_FALSE: + case '-': + case G_TOKEN_INT: + case G_TOKEN_FLOAT: + case G_TOKEN_STRING: + JSON_NOTE (PARSER, "Statement is a value"); + token = json_scanner_get_next_token (scanner); + return json_parse_value (parser, scanner, token, &priv->root); + + default: + JSON_NOTE (PARSER, "Unknown statement"); + json_scanner_get_next_token (scanner); + priv->error_code = JSON_PARSER_ERROR_INVALID_BAREWORD; + return G_TOKEN_SYMBOL; + } +} + +static void +json_scanner_msg_handler (JsonScanner *scanner, + gchar *message, + gboolean is_error) +{ + JsonParser *parser = scanner->user_data; + JsonParserPrivate *priv = parser->priv; + + if (is_error) + { + GError *error = NULL; + + /* translators: %s: is the file name, %d is the line number + * and %s is the error message + */ + g_set_error (&error, JSON_PARSER_ERROR, + priv->error_code, + _("%s:%d: Parse error: %s"), + priv->is_filename ? priv->filename : "", + scanner->line, + message); + + parser->priv->last_error = error; + g_signal_emit (parser, parser_signals[ERROR], 0, error); + } + else + g_warning ("%s:%d: Parse error: %s", + priv->is_filename ? priv->filename : "", + scanner->line, + message); +} + +static JsonScanner * +json_scanner_create (JsonParser *parser) +{ + JsonScanner *scanner; + gint i; + + scanner = json_scanner_new (); + scanner->msg_handler = json_scanner_msg_handler; + scanner->user_data = parser; + + for (i = 0; i < n_symbols; i++) + { + json_scanner_scope_add_symbol (scanner, 0, + symbol_names + symbols[i].name_offset, + GINT_TO_POINTER (symbols[i].token)); + } + + return scanner; +} + +/** + * json_parser_new: + * + * Creates a new #JsonParser instance. You can use the #JsonParser to + * load a JSON stream from either a file or a buffer and then walk the + * hierarchy using the data types API. + * + * Return value: the newly created #JsonParser. Use g_object_unref() + * to release all the memory it allocates. + */ +JsonParser * +json_parser_new (void) +{ + return g_object_new (JSON_TYPE_PARSER, NULL); +} + +static gboolean +json_parser_load (JsonParser *parser, + const gchar *data, + gsize length, + GError **error) +{ + JsonParserPrivate *priv = parser->priv; + JsonScanner *scanner; + gboolean done; + gboolean retval = TRUE; + gint i; + + json_parser_clear (parser); + + scanner = json_scanner_create (parser); + json_scanner_input_text (scanner, data, length); + + priv->scanner = scanner; + + g_signal_emit (parser, parser_signals[PARSE_START], 0); + + done = FALSE; + while (!done) + { + if (json_scanner_peek_next_token (scanner) == G_TOKEN_EOF) + done = TRUE; + else + { + guint expected_token; + gint cur_token; + + /* we try to show the expected token, if possible */ + expected_token = json_parse_statement (parser, scanner); + if (expected_token != G_TOKEN_NONE) + { + const gchar *symbol_name; + gchar *msg; + + cur_token = scanner->token; + msg = NULL; + symbol_name = NULL; + + if (scanner->scope_id == 0) + { + if (expected_token > JSON_TOKEN_INVALID && + expected_token < JSON_TOKEN_LAST) + { + for (i = 0; i < n_symbols; i++) + if (symbols[i].token == expected_token) + symbol_name = symbol_names + symbols[i].name_offset; + + if (!msg) + msg = g_strconcat ("e.g. '", symbol_name, "'", NULL); + } + + if (cur_token > JSON_TOKEN_INVALID && + cur_token < JSON_TOKEN_LAST) + { + symbol_name = "???"; + + for (i = 0; i < n_symbols; i++) + if (symbols[i].token == cur_token) + symbol_name = symbol_names + symbols[i].name_offset; + } + } + + /* this will emit the ::error signal via the custom + * message handler we install + */ + json_scanner_unexp_token (scanner, expected_token, + NULL, "value", + symbol_name, msg, + TRUE); + + /* and this will propagate the error we create in the + * same message handler + */ + if (priv->last_error) + { + g_propagate_error (error, priv->last_error); + priv->last_error = NULL; + } + + retval = FALSE; + + g_free (msg); + done = TRUE; + } + } + } + + g_signal_emit (parser, parser_signals[PARSE_END], 0); + + /* remove the scanner */ + json_scanner_destroy (scanner); + priv->scanner = NULL; + priv->current_node = NULL; + + return retval; +} + +/** + * json_parser_load_from_file: + * @parser: a #JsonParser + * @filename: the path for the file to parse + * @error: return location for a #GError, or %NULL + * + * Loads a JSON stream from the content of @filename and parses it. See + * json_parser_load_from_data(). + * + * Return value: %TRUE if the file was successfully loaded and parsed. + * In case of error, @error is set accordingly and %FALSE is returned + */ +gboolean +json_parser_load_from_file (JsonParser *parser, + const gchar *filename, + GError **error) +{ + JsonParserPrivate *priv; + GError *internal_error; + gchar *data; + gsize length; + gboolean retval = TRUE; + + g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + priv = parser->priv; + + internal_error = NULL; + if (!g_file_get_contents (filename, &data, &length, &internal_error)) + { + g_propagate_error (error, internal_error); + return FALSE; + } + + g_free (priv->filename); + + priv->is_filename = TRUE; + priv->filename = g_strdup (filename); + + if (!json_parser_load (parser, data, length, &internal_error)) + { + g_propagate_error (error, internal_error); + retval = FALSE; + } + + g_free (data); + + return retval; +} + +/** + * json_parser_load_from_data: + * @parser: a #JsonParser + * @data: the buffer to parse + * @length: the length of the buffer, or -1 + * @error: return location for a #GError, or %NULL + * + * Loads a JSON stream from a buffer and parses it. You can call this function + * multiple times with the same #JsonParser object, but the contents of the + * parser will be destroyed each time. + * + * Return value: %TRUE if the buffer was succesfully parser. In case + * of error, @error is set accordingly and %FALSE is returned + */ +gboolean +json_parser_load_from_data (JsonParser *parser, + const gchar *data, + gssize length, + GError **error) +{ + JsonParserPrivate *priv; + GError *internal_error; + gboolean retval = TRUE; + + g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + priv = parser->priv; + + if (length < 0) + length = strlen (data); + + priv->is_filename = FALSE; + g_free (priv->filename); + priv->filename = NULL; + + internal_error = NULL; + if (!json_parser_load (parser, data, length, &internal_error)) + { + g_propagate_error (error, internal_error); + retval = FALSE; + } + + return retval; +} + +/** + * json_parser_get_root: + * @parser: a #JsonParser + * + * Retrieves the top level node from the parsed JSON stream. + * + * Return value: (transfer none): the root #JsonNode . The returned + * node is owned by the #JsonParser and should never be modified + * or freed. + */ +JsonNode * +json_parser_get_root (JsonParser *parser) +{ + g_return_val_if_fail (JSON_IS_PARSER (parser), NULL); + + return parser->priv->root; +} + +/** + * json_parser_get_current_line: + * @parser: a #JsonParser + * + * Retrieves the line currently parsed, starting from 1. + * + * This function has defined behaviour only while parsing; calling this + * function from outside the signal handlers emitted by #JsonParser will + * yield 0. + * + * Return value: the currently parsed line, or 0. + */ +guint +json_parser_get_current_line (JsonParser *parser) +{ + g_return_val_if_fail (JSON_IS_PARSER (parser), 0); + + if (parser->priv->scanner) + return json_scanner_cur_line (parser->priv->scanner); + + return 0; +} + +/** + * json_parser_get_current_pos: + * @parser: a #JsonParser + * + * Retrieves the current position inside the current line, starting + * from 0. + * + * This function has defined behaviour only while parsing; calling this + * function from outside the signal handlers emitted by #JsonParser will + * yield 0. + * + * Return value: the position in the current line, or 0. + */ +guint +json_parser_get_current_pos (JsonParser *parser) +{ + g_return_val_if_fail (JSON_IS_PARSER (parser), 0); + + if (parser->priv->scanner) + return json_scanner_cur_line (parser->priv->scanner); + + return 0; +} + +/** + * json_parser_has_assignment: + * @parser: a #JsonParser + * @variable_name: (out) (allow-none) (transfer none): Return location for the variable + * name, or %NULL + * + * A JSON data stream might sometimes contain an assignment, like: + * + * |[ + * var _json_data = { "member_name" : [ ... + * ]| + * + * even though it would technically constitute a violation of the RFC. + * + * #JsonParser will ignore the left hand identifier and parse the right + * hand value of the assignment. #JsonParser will record, though, the + * existence of the assignment in the data stream and the variable name + * used. + * + * Return value: %TRUE if there was an assignment, %FALSE otherwise. If + * @variable_name is not %NULL it will be set to the name of the variable + * used in the assignment. The string is owned by #JsonParser and should + * never be modified or freed. + * + * Since: 0.4 + */ +gboolean +json_parser_has_assignment (JsonParser *parser, + gchar **variable_name) +{ + JsonParserPrivate *priv; + + g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE); + + priv = parser->priv; + + if (priv->has_assignment && variable_name) + *variable_name = priv->variable_name; + + return priv->has_assignment; +} + +#define GET_DATA_BLOCK_SIZE 8192 + +/** + * json_parser_load_from_stream: + * @parser: a #JsonParser + * @stream: an open #GInputStream + * @cancellable: (allow-none): a #GCancellable, or %NULL + * @error: the return location for a #GError, or %NULL + * + * Loads the contents of an input stream and parses them. + * + * If @cancellable is not %NULL, then the operation can be cancelled by + * triggering the @cancellable object from another thread. If the + * operation was cancelled, the error %G_IO_ERROR_CANCELLED will be set + * on the passed @error. + * + * Return value: %TRUE if the data stream was successfully read and + * parsed, and %FALSE otherwise + * + * Since: 0.12 + */ +gboolean +json_parser_load_from_stream (JsonParser *parser, + GInputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GByteArray *content; + gsize pos; + gssize res; + gboolean retval = FALSE; + + g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE); + g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + content = g_byte_array_new (); + pos = 0; + + g_byte_array_set_size (content, pos + GET_DATA_BLOCK_SIZE + 1); + while ((res = g_input_stream_read (stream, content->data + pos, + GET_DATA_BLOCK_SIZE, + cancellable, error)) > 0) + { + pos += res; + g_byte_array_set_size (content, pos + GET_DATA_BLOCK_SIZE + 1); + } + + if (res < 0) + { + /* error has already been set */ + retval = FALSE; + goto out; + } + + /* zero-terminate the content; we allocated an extra byte for this */ + content->data[pos] = 0; + + retval = json_parser_load (parser, (const gchar *) content->data, content->len, error); + +out: + g_byte_array_free (content, TRUE); + + return retval; +} + +typedef struct _LoadStreamData +{ + JsonParser *parser; + GError *error; + GCancellable *cancellable; + GAsyncReadyCallback callback; + gpointer user_data; + GByteArray *content; + gsize pos; +} LoadStreamData; + +static void +load_stream_data_free (gpointer data) +{ + LoadStreamData *closure; + + if (G_UNLIKELY (data == NULL)) + return; + + closure = data; + + if (closure->error) + g_error_free (closure->error); + + if (closure->cancellable) + g_object_unref (closure->cancellable); + + if (closure->content) + g_byte_array_free (closure->content, TRUE); + + g_object_unref (closure->parser); + + g_free (closure); +} + +static void +load_stream_data_read_callback (GObject *object, + GAsyncResult *read_res, + gpointer user_data) +{ + GInputStream *stream = G_INPUT_STREAM (object); + LoadStreamData *data = user_data; + GError *error = NULL; + gssize read_size; + + read_size = g_input_stream_read_finish (stream, read_res, &error); + if (read_size < 0) + { + if (error != NULL) + data->error = error; + else + { + GSimpleAsyncResult *res; + + /* EOF */ + res = g_simple_async_result_new (G_OBJECT (data->parser), + data->callback, + data->user_data, + json_parser_load_from_stream_async); + g_simple_async_result_set_op_res_gpointer (res, data, load_stream_data_free); + g_simple_async_result_complete (res); + g_object_unref (res); + } + } + else if (read_size > 0) + { + data->pos += read_size; + + g_byte_array_set_size (data->content, data->pos + GET_DATA_BLOCK_SIZE); + + g_input_stream_read_async (stream, data->content->data + data->pos, + GET_DATA_BLOCK_SIZE, + 0, + data->cancellable, + load_stream_data_read_callback, + data); + } + else + { + GSimpleAsyncResult *res; + + res = g_simple_async_result_new (G_OBJECT (data->parser), + data->callback, + data->user_data, + json_parser_load_from_stream_async); + g_simple_async_result_set_op_res_gpointer (res, data, load_stream_data_free); + g_simple_async_result_complete (res); + g_object_unref (res); + } +} + +/** + * json_parser_load_from_stream_finish: + * @parser: a #JsonParser + * @result: a #GAsyncResult + * @error: the return location for a #GError or %NULL + * + * Finishes an asynchronous stream loading started with + * json_parser_load_from_stream_async(). + * + * Return value: %TRUE if the content of the stream was successfully retrieves + * and parsed, and %FALSE otherwise. In case of error, the #GError will be + * filled accordingly. + * + * Since: 0.12 + */ +gboolean +json_parser_load_from_stream_finish (JsonParser *parser, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + LoadStreamData *data; + + g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == json_parser_load_from_stream_async); + + data = g_simple_async_result_get_op_res_gpointer (simple); + + if (data->error) + { + g_propagate_error (error, data->error); + data->error = NULL; + return FALSE; + } + + g_byte_array_set_size (data->content, data->pos + 1); + data->content->data[data->pos] = 0; + + return json_parser_load (parser, (const gchar *) data->content->data, data->content->len, error); +} + +/** + * json_parser_load_from_stream_async: + * @parser: a #JsonParser + * @stream: a #GInputStream + * @cancellable: (allow-none): a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback to call when the request is satisfied + * @user_data: the data to pass to @callback + * + * Asynchronously reads the contents of @stream. + * + * For more details, see json_parser_load_from_stream() which is the + * synchronous version of this call. + * + * When the operation is finished, @callback will be called. You should + * then call json_parser_load_from_stream_finish() to get the result + * of the operation. + * + * Since: 0.12 + */ +void +json_parser_load_from_stream_async (JsonParser *parser, + GInputStream *stream, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + LoadStreamData *data; + + g_return_if_fail (JSON_IS_PARSER (parser)); + g_return_if_fail (G_IS_INPUT_STREAM (stream)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + data = g_new0 (LoadStreamData, 1); + + if (cancellable != NULL) + data->cancellable = g_object_ref (cancellable); + + data->callback = callback; + data->user_data = user_data; + data->content = g_byte_array_new (); + data->parser = g_object_ref (parser); + + g_byte_array_set_size (data->content, data->pos + GET_DATA_BLOCK_SIZE); + g_input_stream_read_async (stream, data->content->data + data->pos, + GET_DATA_BLOCK_SIZE, 0, + data->cancellable, + load_stream_data_read_callback, + data); +} diff --git a/json-glib/json-glib/json-parser.h b/json-glib/json-glib/json-parser.h new file mode 100644 index 0000000..36107f4 --- /dev/null +++ b/json-glib/json-glib/json-parser.h @@ -0,0 +1,173 @@ +/* json-parser.h - JSON streams parser + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_PARSER_H__ +#define __JSON_PARSER_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define JSON_TYPE_PARSER (json_parser_get_type ()) +#define JSON_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_PARSER, JsonParser)) +#define JSON_IS_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_PARSER)) +#define JSON_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), JSON_TYPE_PARSER, JsonParserClass)) +#define JSON_IS_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), JSON_TYPE_PARSER)) +#define JSON_PARSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), JSON_TYPE_PARSER, JsonParserClass)) + +#define JSON_PARSER_ERROR (json_parser_error_quark ()) + +typedef struct _JsonParser JsonParser; +typedef struct _JsonParserPrivate JsonParserPrivate; +typedef struct _JsonParserClass JsonParserClass; + +/** + * JsonParserError: + * @JSON_PARSER_ERROR_PARSE: parse error + * @JSON_PARSER_ERROR_TRAILING_COMMA: unexpected trailing comma + * @JSON_PARSER_ERROR_MISSING_COMMA: expected comma + * @JSON_PARSER_ERROR_MISSING_COLON: expected colon + * @JSON_PARSER_ERROR_INVALID_BAREWORD: invalid bareword + * @JSON_PARSER_ERROR_UNKNOWN: unknown error + * + * Error enumeration for #JsonParser + * + * This enumeration can be extended at later date + */ +typedef enum { + JSON_PARSER_ERROR_PARSE, + JSON_PARSER_ERROR_TRAILING_COMMA, + JSON_PARSER_ERROR_MISSING_COMMA, + JSON_PARSER_ERROR_MISSING_COLON, + JSON_PARSER_ERROR_INVALID_BAREWORD, + + JSON_PARSER_ERROR_UNKNOWN +} JsonParserError; + +/** + * JsonParser: + * + * JSON data streams parser. The contents of the #JsonParser structure are + * private and should only be accessed via the provided API. + */ +struct _JsonParser +{ + /*< private >*/ + GObject parent_instance; + + JsonParserPrivate *priv; +}; + +/** + * JsonParserClass: + * @parse_start: class handler for the JsonParser::parse-start signal + * @object_start: class handler for the JsonParser::object-start signal + * @object_member: class handler for the JsonParser::object-member signal + * @object_end: class handler for the JsonParser::object-end signal + * @array_start: class handler for the JsonParser::array-start signal + * @array_element: class handler for the JsonParser::array-element signal + * @array_end: class handler for the JsonParser::array-end signal + * @parse_end: class handler for the JsonParser::parse-end signal + * @error: class handler for the JsonParser::error signal + * + * #JsonParser class. + */ +struct _JsonParserClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< public >*/ + void (* parse_start) (JsonParser *parser); + + void (* object_start) (JsonParser *parser); + void (* object_member) (JsonParser *parser, + JsonObject *object, + const gchar *member_name); + void (* object_end) (JsonParser *parser, + JsonObject *object); + + void (* array_start) (JsonParser *parser); + void (* array_element) (JsonParser *parser, + JsonArray *array, + gint index_); + void (* array_end) (JsonParser *parser, + JsonArray *array); + + void (* parse_end) (JsonParser *parser); + + void (* error) (JsonParser *parser, + const GError *error); + + /*< private >*/ + /* padding for future expansion */ + void (* _json_reserved1) (void); + void (* _json_reserved2) (void); + void (* _json_reserved3) (void); + void (* _json_reserved4) (void); + void (* _json_reserved5) (void); + void (* _json_reserved6) (void); + void (* _json_reserved7) (void); + void (* _json_reserved8) (void); +}; + +GQuark json_parser_error_quark (void); +GType json_parser_get_type (void) G_GNUC_CONST; + +JsonParser *json_parser_new (void); +gboolean json_parser_load_from_file (JsonParser *parser, + const gchar *filename, + GError **error); +gboolean json_parser_load_from_data (JsonParser *parser, + const gchar *data, + gssize length, + GError **error); +gboolean json_parser_load_from_stream (JsonParser *parser, + GInputStream *stream, + GCancellable *cancellable, + GError **error); +void json_parser_load_from_stream_async (JsonParser *parser, + GInputStream *stream, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean json_parser_load_from_stream_finish (JsonParser *parser, + GAsyncResult *result, + GError **error); + +JsonNode * json_parser_get_root (JsonParser *parser); + +guint json_parser_get_current_line (JsonParser *parser); +guint json_parser_get_current_pos (JsonParser *parser); +gboolean json_parser_has_assignment (JsonParser *parser, + gchar **variable_name); + +G_END_DECLS + +#endif /* __JSON_PARSER_H__ */ diff --git a/json-glib/json-glib/json-path.c b/json-glib/json-glib/json-path.c new file mode 100644 index 0000000..abcc505 --- /dev/null +++ b/json-glib/json-glib/json-path.c @@ -0,0 +1,1046 @@ +/* json-path.h - JSONPath implementation + * + * This file is part of JSON-GLib + * Copyright © 2011 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:json-path + * @Title: JsonPath + * @short_description: JSONPath implementation + * + * #JsonPath is a simple class implementing the JSONPath syntax for extracting + * data out of a JSON tree. While the semantics of the JSONPath expressions are + * heavily borrowed by the XPath specification for XML, the syntax follows the + * ECMAScript origins of JSON. + * + * Once a #JsonPath instance has been created, it has to compile a JSONPath + * expression using json_path_compile() before being able to match it to a + * JSON tree; the same #JsonPath instance can be used to match multiple JSON + * trees. It it also possible to compile a new JSONPath expression using the + * same #JsonPath instance; the previous expression will be discarded only if + * the compilation of the new expression is successful. + * + * The simple convenience function json_path_query() can be used for one-off + * matching. + * + * + * Syntax of the JSONPath expressions + * A JSONPath expression is composed by path indices and operators. + * Each path index can either be a member name or an element index inside + * a JSON tree. A JSONPath expression must start with the '$' operator; each + * path index is separated using either the dot notation or the bracket + * notation, e.g.: + * |[ + * /* dot notation */ + * $.store.book[0].title + * /* bracket notation */ + * $['store']['book'][0]['title'] + * ]| + * The available operators are: + * + * Operators + * + * + * + * + * + * + * + * Operator + * Description + * Example + * Results + * + * + * + * + * $ + * The root node + * $ + * The whole document + * + * + * . or [] + * The child member or element + * $.store.book + * The contents of the book member of the store object + * + * + * .. + * Recursive descent + * $..author + * The content of the author member in every object + * + * + * * + * Wildcard + * $.store.book[*].author + * The content of the author member of any object of the + * array contained in the book member of the store object + * + * + * [] + * Subscript + * $.store.book[0] + * The first element of the array contained in the book + * member of the store object + * + * + * [,] + * Set + * $.store.book[0,1] + * The first two elements of the array contained in the + * book member of the store object + * + * + * [start:end:step] + * Slice + * $.store.book[:2] + * The first two elements of the array contained in the + * book member of the store object; the start and step are omitted + * and implied to be 0 and 1, respectively + * + * + * + *
+ * More information about JSONPath is available on Stefan Gössner's + * website. + *
+ * + * + * Example of JsonPath usage + * The following example shows some of the results of using #JsonPath + * on a JSON tree. We use the following JSON description of a + * bookstore: + * + * We can parse the JSON using #JsonParser: + * + * JsonParser *parser = json_parser_new (); + * json_parser_load_from_data (parser, json_data, -1, NULL); + * + * If we run the following code: + * + * JsonNode *result; + * JsonPath *path = json_path_new (); + * json_path_compile (path, "$.store..author", NULL); + * result = json_path_match (path, json_parser_get_root (parser)); + * + * The result #JsonNode will contain an array + * with all values of the author member of the objects + * in the JSON tree. If we use a #JsonGenerator to convert the #JsonNode + * to a string and print it: + * + * JsonGenerator *generator = json_generator_new (); + * char *str; + * json_generator_set_pretty (generator, TRUE); + * json_generator_set_root (generator, result); + * str = json_generator_to_data (generator, NULL); + * g_print ("Results: %s\n", str); + * + * The output will be: + * + * + * + * #JsonPath is available since JSON-GLib 0.14 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "json-path.h" + +#include "json-debug.h" +#include "json-types-private.h" + +typedef enum { + JSON_PATH_NODE_ROOT, + JSON_PATH_NODE_CHILD_MEMBER, + JSON_PATH_NODE_CHILD_ELEMENT, + JSON_PATH_NODE_RECURSIVE_DESCENT, + JSON_PATH_NODE_WILDCARD_MEMBER, + JSON_PATH_NODE_WILDCARD_ELEMENT, + JSON_PATH_NODE_ELEMENT_SET, + JSON_PATH_NODE_ELEMENT_SLICE +} PathNodeType; + +typedef struct _PathNode PathNode; + +struct _JsonPath +{ + GObject parent_instance; + + /* the compiled path */ + GList *nodes; + + guint is_compiled : 1; +}; + +struct _JsonPathClass +{ + GObjectClass parent_class; +}; + +struct _PathNode +{ + PathNodeType node_type; + + union { + /* JSON_PATH_NODE_CHILD_ELEMENT */ + int element_index; + + /* JSON_PATH_NODE_CHILD_MEMBER */ + char *member_name; + + /* JSON_PATH_NODE_ELEMENT_SET */ + struct { int n_indices; int *indices; } set; + + /* JSON_PATH_NODE_ELEMENT_SLICE */ + struct { int start, end, step; } slice; + } data; +}; + +G_DEFINE_TYPE (JsonPath, json_path, G_TYPE_OBJECT) + +static void +path_node_free (gpointer data) +{ + if (data != NULL) + { + PathNode *node = data; + + switch (node->node_type) + { + case JSON_PATH_NODE_CHILD_MEMBER: + g_free (node->data.member_name); + break; + + case JSON_PATH_NODE_ELEMENT_SET: + g_free (node->data.set.indices); + break; + + default: + break; + } + + g_free (node); + } +} + +static void +json_path_finalize (GObject *gobject) +{ + JsonPath *self = JSON_PATH (gobject); + +#if GLIB_CHECK_VERSION (2, 28, 0) + g_list_free_full (self->nodes, path_node_free); +#else + g_list_foreach (self->nodes, (GFunc) path_node_free, NULL); + g_list_free (self->nodes); +#endif + + G_OBJECT_CLASS (json_path_parent_class)->finalize (gobject); +} + +static void +json_path_class_init (JsonPathClass *klass) +{ + G_OBJECT_CLASS (klass)->finalize = json_path_finalize; +} + +static void +json_path_init (JsonPath *self) +{ +} + +GQuark +json_path_error_quark (void) +{ + return g_quark_from_static_string ("json-path-error"); +} + +/** + * json_path_new: + * + * Creates a new #JsonPath instance. + * + * Once created, the #JsonPath object should be used with json_path_compile() + * and json_path_match(). + * + * Return value: (transfer full): the newly created #JsonPath instance. Use + * g_object_unref() to free the allocated resources when done + * + * Since: 0.14 + */ +JsonPath * +json_path_new (void) +{ + return g_object_new (JSON_TYPE_PATH, NULL); +} + +/** + * json_path_compile: + * @path: a #JsonPath + * @expression: a JSONPath expression + * @error: return location for a #GError, or %NULL + * + * Validates and decomposes @expression. + * + * A JSONPath expression must be compiled before calling json_path_match(). + * + * Return value: %TRUE on success; on error, @error will be set with + * the %JSON_PATH_ERROR domain and a code from the #JsonPathError + * enumeration, and %FALSE will be returned + * + * Since: 0.14 + */ +gboolean +json_path_compile (JsonPath *path, + const char *expression, + GError **error) +{ + const char *p, *end_p; + PathNode *root = NULL; + GList *nodes, *l; + + p = expression; + + while (*p != '\0') + { + switch (*p) + { + case '$': + { + PathNode *node; + + if (root != NULL) + { + g_set_error_literal (error, JSON_PATH_ERROR, + JSON_PATH_ERROR_INVALID_QUERY, + _("Only one root node is allowed in a JSONPath expression")); + return FALSE; + } + + if (!(*(p + 1) == '.' || *(p + 1) == '[')) + { + /* translators: the %c is the invalid character */ + g_set_error (error, JSON_PATH_ERROR, + JSON_PATH_ERROR_INVALID_QUERY, + _("Root node followed by invalid character '%c'"), + *(p + 1)); + return FALSE; + } + + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_ROOT; + + root = node; + nodes = g_list_prepend (NULL, root); + } + break; + + case '.': + case '[': + { + PathNode *node = NULL; + + if (*p == '.' && *(p + 1) == '.') + { + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_RECURSIVE_DESCENT; + } + else if (*p == '.' && *(p + 1) == '*') + { + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_WILDCARD_MEMBER; + + p += 1; + } + else if (*p == '.') + { + end_p = p + 1; + while (!(*end_p == '.' || *end_p == '[' || *end_p == '\0')) + end_p += 1; + + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_CHILD_MEMBER; + node->data.member_name = g_strndup (p + 1, end_p - p - 1); + + p = end_p - 1; + } + else if (*p == '[' && *(p + 1) == '\'') + { + if (*(p + 2) == '*' && *(p + 3) == '\'' && *(p + 4) == ']') + { + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_WILDCARD_MEMBER; + + p += 4; + } + else + { + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_CHILD_MEMBER; + + end_p = strchr (p + 2, '\''); + node->data.member_name = g_strndup (p + 2, end_p - p - 2); + + p = end_p + 1; + } + } + else if (*p == '[' && *(p + 1) == '*' && *(p + 2) == ']') + { + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_WILDCARD_ELEMENT; + + p += 1; + } + else if (*p == '[') + { + int sign = 1; + int idx; + + end_p = p + 1; + + if (*end_p == '-') + { + sign = -1; + end_p += 1; + } + + /* slice with missing start */ + if (*end_p == ':') + { + int slice_end = g_ascii_strtoll (end_p + 1, (char **) &end_p, 10) * sign; + int slice_step = 1; + + if (*end_p == ':') + { + end_p += 1; + + if (*end_p == '-') + { + sign = -1; + end_p += 1; + } + else + sign = 1; + + slice_step = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign; + + if (*end_p != ']') + { + g_set_error (error, JSON_PATH_ERROR, + JSON_PATH_ERROR_INVALID_QUERY, + _("Malformed slice expression '%*s'"), + end_p - p, + p + 1); + goto fail; + } + } + + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_ELEMENT_SLICE; + node->data.slice.start = 0; + node->data.slice.end = slice_end; + node->data.slice.step = slice_step; + + nodes = g_list_prepend (nodes, node); + p = end_p; + break; + } + + idx = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign; + + if (*end_p == ',') + { + GArray *indices = g_array_new (FALSE, TRUE, sizeof (int)); + + g_array_append_val (indices, idx); + + while (*end_p != ']') + { + end_p += 1; + + if (*end_p == '-') + { + sign = -1; + end_p += 1; + } + else + sign = 1; + + idx = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign; + if (!(*end_p == ',' || *end_p == ']')) + { + g_array_unref (indices); + g_set_error (error, JSON_PATH_ERROR, + JSON_PATH_ERROR_INVALID_QUERY, + _("Invalid set definition '%*s'"), + end_p - p, + p + 1); + goto fail; + } + + g_array_append_val (indices, idx); + } + + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_ELEMENT_SET; + node->data.set.n_indices = indices->len; + node->data.set.indices = (int *) g_array_free (indices, FALSE); + nodes = g_list_prepend (nodes, node); + p = end_p; + break; + } + else if (*end_p == ':') + { + int slice_start = idx; + int slice_end = 0; + int slice_step = 1; + + end_p += 1; + + if (*end_p == '-') + { + sign = -1; + end_p += 1; + } + else + sign = 1; + + slice_end = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign; + if (*end_p == ':') + { + end_p += 1; + + if (*end_p == '-') + { + sign = -1; + end_p += 1; + } + else + sign = 1; + + slice_step = g_ascii_strtoll (end_p + 1, (char **) &end_p, 10) * sign; + } + + if (*end_p != ']') + { + g_set_error (error, JSON_PATH_ERROR, + JSON_PATH_ERROR_INVALID_QUERY, + _("Invalid slice definition '%*s'"), + end_p - p, + p + 1); + goto fail; + } + + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_ELEMENT_SLICE; + node->data.slice.start = slice_start; + node->data.slice.end = slice_end; + node->data.slice.step = slice_step; + nodes = g_list_prepend (nodes, node); + p = end_p; + break; + } + else if (*end_p == ']') + { + node = g_new0 (PathNode, 1); + node->node_type = JSON_PATH_NODE_CHILD_ELEMENT; + node->data.element_index = idx; + nodes = g_list_prepend (nodes, node); + p = end_p; + break; + } + else + { + g_set_error (error, JSON_PATH_ERROR, + JSON_PATH_ERROR_INVALID_QUERY, + _("Invalid array index definition '%*s'"), + end_p - p, + p + 1); + goto fail; + } + } + else + break; + + if (node != NULL) + nodes = g_list_prepend (nodes, node); + } + break; + + default: + break; + } + + p += 1; + } + + nodes = g_list_reverse (nodes); + +#ifdef JSON_ENABLE_DEBUG + if (_json_get_debug_flags () & JSON_DEBUG_PATH) + { + GString *buf = g_string_new (NULL); + + for (l = nodes; l != NULL; l = l->next) + { + PathNode *cur_node = l->data; + + switch (cur_node->node_type) + { + case JSON_PATH_NODE_ROOT: + g_string_append (buf, "data.member_name); + break; + + case JSON_PATH_NODE_CHILD_ELEMENT: + g_string_append_printf (buf, "data.element_index); + break; + + case JSON_PATH_NODE_RECURSIVE_DESCENT: + g_string_append (buf, "data.set.n_indices - 1; i++) + g_string_append_printf (buf, "'%d', ", cur_node->data.set.indices[i]); + + g_string_append_printf (buf, "'%d'", cur_node->data.set.indices[i]); + } + break; + + case JSON_PATH_NODE_ELEMENT_SLICE: + g_string_append_printf (buf, "data.slice.start, + cur_node->data.slice.end, + cur_node->data.slice.step); + break; + + default: + g_string_append (buf, "next != NULL) + g_string_append (buf, ">, "); + else + g_string_append (buf, ">"); + } + + g_message ("[PATH] " G_STRLOC ": expression '%s' => '%s'", expression, buf->str); + g_string_free (buf, TRUE); + } +#endif /* JSON_ENABLE_DEBUG */ + + if (path->nodes != NULL) + { +#if GLIB_CHECK_VERSION (2, 28, 0) + g_list_free_full (path->nodes, path_node_free); +#else + g_list_foreach (path->nodes, (GFunc) path_node_free, NULL); + g_list_free (path->nodes); +#endif + } + + path->nodes = nodes; + path->is_compiled = (path->nodes != NULL); + + return path->nodes != NULL; + +fail: +#if GLIB_CHECK_VERSION (2, 28, 0) + g_list_free_full (nodes, path_node_free); +#else + g_list_foreach (nodes, (GFunc) path_node_free, NULL); + g_list_free (nodes); +#endif + + return FALSE; +} + +static void +walk_path_node (GList *path, + JsonNode *root, + JsonArray *results) +{ + PathNode *node = path->data; + + switch (node->node_type) + { + case JSON_PATH_NODE_ROOT: + walk_path_node (path->next, root, results); + break; + + case JSON_PATH_NODE_CHILD_MEMBER: + if (JSON_NODE_HOLDS_OBJECT (root)) + { + JsonObject *object = json_node_get_object (root); + + if (json_object_has_member (object, node->data.member_name)) + { + JsonNode *member = json_object_get_member (object, node->data.member_name); + + if (path->next == NULL) + { + JSON_NOTE (PATH, "end of path at member '%s'", node->data.member_name); + json_array_add_element (results, json_node_copy (member)); + } + else + walk_path_node (path->next, member, results); + } + } + break; + + case JSON_PATH_NODE_CHILD_ELEMENT: + if (JSON_NODE_HOLDS_ARRAY (root)) + { + JsonArray *array = json_node_get_array (root); + + if (json_array_get_length (array) >= node->data.element_index) + { + JsonNode *element = json_array_get_element (array, node->data.element_index); + + if (path->next == NULL) + { + JSON_NOTE (PATH, "end of path at element '%d'", node->data.element_index); + json_array_add_element (results, json_node_copy (element)); + } + else + walk_path_node (path->next, element, results); + } + } + break; + + case JSON_PATH_NODE_RECURSIVE_DESCENT: + { + PathNode *tmp = path->next->data; + + switch (json_node_get_node_type (root)) + { + case JSON_NODE_OBJECT: + { + JsonObject *object = json_node_get_object (root); + GList *members, *l; + + members = json_object_get_members (object); + for (l = members; l != NULL; l = l->next) + { + JsonNode *m = json_object_get_member (object, l->data); + + if (tmp->node_type == JSON_PATH_NODE_CHILD_MEMBER && + strcmp (tmp->data.member_name, l->data) == 0) + { + JSON_NOTE (PATH, "entering '%s'", tmp->data.member_name); + walk_path_node (path->next, root, results); + } + else + { + JSON_NOTE (PATH, "recursing into '%s'", (char *) l->data); + walk_path_node (path, m, results); + } + } + g_list_free (members); + } + break; + + case JSON_NODE_ARRAY: + { + JsonArray *array = json_node_get_array (root); + GList *members, *l; + int i; + + members = json_array_get_elements (array); + for (l = members, i = 0; l != NULL; l = l->next, i += 1) + { + JsonNode *m = l->data; + + if (tmp->node_type == JSON_PATH_NODE_CHILD_ELEMENT && + tmp->data.element_index == i) + { + JSON_NOTE (PATH, "entering '%d'", tmp->data.element_index); + walk_path_node (path->next, root, results); + } + else + { + JSON_NOTE (PATH, "recursing into '%d'", i); + walk_path_node (path, m, results); + } + } + g_list_free (members); + } + break; + + default: + break; + } + } + break; + + case JSON_PATH_NODE_WILDCARD_MEMBER: + if (JSON_NODE_HOLDS_OBJECT (root)) + { + JsonObject *object = json_node_get_object (root); + GList *members, *l; + + members = json_object_get_members (object); + for (l = members; l != NULL; l = l->next) + { + JsonNode *member = json_object_get_member (object, l->data); + + if (path->next != NULL) + walk_path_node (path->next, member, results); + else + { + JSON_NOTE (PATH, "glob match member '%s'", (char *) l->data); + json_array_add_element (results, json_node_copy (root)); + } + } + g_list_free (members); + } + else + json_array_add_element (results, json_node_copy (root)); + break; + + case JSON_PATH_NODE_WILDCARD_ELEMENT: + if (JSON_NODE_HOLDS_ARRAY (root)) + { + JsonArray *array = json_node_get_array (root); + GList *elements, *l; + int i; + + elements = json_array_get_elements (array); + for (l = elements, i = 0; l != NULL; l = l->next, i += 1) + { + JsonNode *element = l->data; + + if (path->next != NULL) + walk_path_node (path->next, element, results); + else + { + JSON_NOTE (PATH, "glob match element '%d'", i); + json_array_add_element (results, json_node_copy (root)); + } + } + g_list_free (elements); + } + else + json_array_add_element (results, json_node_copy (root)); + break; + + case JSON_PATH_NODE_ELEMENT_SET: + if (JSON_NODE_HOLDS_ARRAY (root)) + { + JsonArray *array = json_node_get_array (root); + int i; + + for (i = 0; i < node->data.set.n_indices; i += 1) + { + int idx = node->data.set.indices[i]; + JsonNode *element = json_array_get_element (array, idx); + + if (path->next != NULL) + walk_path_node (path->next, element, results); + else + { + JSON_NOTE (PATH, "set element '%d'", idx); + json_array_add_element (results, json_node_copy (element)); + } + } + } + break; + + case JSON_PATH_NODE_ELEMENT_SLICE: + if (JSON_NODE_HOLDS_ARRAY (root)) + { + JsonArray *array = json_node_get_array (root); + int i, start, end; + + if (node->data.slice.start < 0) + { + start = json_array_get_length (array) + + node->data.slice.start; + + end = json_array_get_length (array) + + node->data.slice.end; + } + else + { + start = node->data.slice.start; + end = node->data.slice.end; + } + + for (i = start; i < end; i += node->data.slice.step) + { + JsonNode *element = json_array_get_element (array, i); + + if (path->next != NULL) + walk_path_node (path->next, element, results); + else + { + JSON_NOTE (PATH, "slice element '%d'", i); + json_array_add_element (results, json_node_copy (element)); + } + } + } + break; + + default: + break; + } +} + +/** + * json_path_match: + * @path: a compiled #JsonPath + * @root: a #JsonNode + * + * Matches the JSON tree pointed by @root using the expression compiled + * into the #JsonPath. + * + * The matching #JsonNodes will be copied into a #JsonArray and + * returned wrapped in a #JsonNode. + * + * Return value: (transfer full): a newly-created #JsonNode of type + * %JSON_NODE_ARRAY containing an array of matching #JsonNodes. + * Use json_node_free() when done + * + * Since: 0.14 + */ +JsonNode * +json_path_match (JsonPath *path, + JsonNode *root) +{ + JsonArray *results; + JsonNode *retval; + + g_return_val_if_fail (JSON_IS_PATH (path), NULL); + g_return_val_if_fail (path->is_compiled, NULL); + g_return_val_if_fail (root != NULL, NULL); + + results = json_array_new (); + + walk_path_node (path->nodes, root, results); + + retval = json_node_new (JSON_NODE_ARRAY); + json_node_take_array (retval, results); + + return retval; +} + +/** + * json_path_query: + * @expression: a JSONPath expression + * @root: the root of a JSON tree + * @error: return location for a #GError, or %NULL + * + * Queries a JSON tree using a JSONPath expression. + * + * This function is a simple wrapper around json_path_new(), + * json_path_compile() and json_path_match(). It implicitly + * creates a #JsonPath instance, compiles @expression and + * matches it against the JSON tree pointed by @root. + * + * Return value: (transfer full): a newly-created #JsonNode of type + * %JSON_NODE_ARRAY containing an array of matching #JsonNodes. + * Use json_node_free() when done + * + * Since: 0.14 + */ +JsonNode * +json_path_query (const char *expression, + JsonNode *root, + GError **error) +{ + JsonPath *path = json_path_new (); + JsonNode *retval; + + if (!json_path_compile (path, expression, error)) + { + g_object_unref (path); + return NULL; + } + + retval = json_path_match (path, root); + + g_object_unref (path); + + return retval; +} diff --git a/json-glib/json-glib/json-path.h b/json-glib/json-glib/json-path.h new file mode 100644 index 0000000..2bae608 --- /dev/null +++ b/json-glib/json-glib/json-path.h @@ -0,0 +1,97 @@ +/* json-path.h - JSONPath implementation + * + * This file is part of JSON-GLib + * Copyright © 2011 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_PATH_H__ +#define __JSON_PATH_H__ + +#include + +G_BEGIN_DECLS + +#define JSON_TYPE_PATH (json_path_get_type ()) +#define JSON_PATH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_PATH, JsonPath)) +#define JSON_IS_PATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_PATH)) + +/** + * JSON_PATH_ERROR: + * + * Error domain for #JsonPath errors + * + * Since: 0.14 + */ +#define JSON_PATH_ERROR (json_path_error_quark ()) + +/** + * JsonPathError: + * @JSON_PATH_ERROR_INVALID_QUERY: Invalid query + * + * Error code enumeration for the %JSON_PATH_ERROR domain. + * + * Since: 0.14 + */ +typedef enum { + JSON_PATH_ERROR_INVALID_QUERY +} JsonPathError; + +/** + * JsonPath: + * + * The JsonPath structure is an opaque object + * whose members cannot be directly accessed except through the provided + * API. + * + * Since: 0.14 + */ +typedef struct _JsonPath JsonPath; + +/** + * JsonPathClass: + * + * The JsonPathClass structure is an opaque + * object class whose members cannot be directly accessed. + * + * Since: 0.14 + */ +typedef struct _JsonPathClass JsonPathClass; + +GType json_path_get_type (void) G_GNUC_CONST; +GQuark json_path_error_quark (void); + +JsonPath * json_path_new (void); + +gboolean json_path_compile (JsonPath *path, + const char *expression, + GError **error); +JsonNode * json_path_match (JsonPath *path, + JsonNode *root); + +JsonNode * json_path_query (const char *expression, + JsonNode *root, + GError **error); + +G_END_DECLS + +#endif /* __JSON_PATH_H__ */ diff --git a/json-glib/json-glib/json-reader.c b/json-glib/json-glib/json-reader.c new file mode 100644 index 0000000..ddce658 --- /dev/null +++ b/json-glib/json-glib/json-reader.c @@ -0,0 +1,1065 @@ +/* json-reader.h - JSON cursor parser + * + * This file is part of JSON-GLib + * Copyright (C) 2010 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:json-reader + * @Title: JsonReader + * @short_description: A cursor-based parser + * + * #JsonReader provides a simple, cursor-based API for parsing a JSON DOM. It + * is similar, in spirit, to the XML Reader API. + * + * In case of error, #JsonReader will be set in an error state; all subsequent + * calls will simply be ignored until a function that resets the error state is + * called, e.g.: + * + * |[ + * /* ask for the 7th element; if the element does not exist, the + * * reader will be put in an error state + * */ + * json_reader_read_element (reader, 6); + * + * /* in case of error, this will return NULL, otherwise it will + * * return the value of the element + * */ + * str = json_reader_get_string_value (value); + * + * /* this function resets the error state if any was set */ + * json_reader_end_element (reader); + * ]| + * + * If you want to detect the error state as soon as possible, you can use + * json_reader_get_error(): + * + * |[ + * /* like the example above, but in this case we print out the + * * error immediately + * */ + * if (!json_reader_read_element (reader, 6)) + * { + * const GError *error = json_reader_get_error (reader); + * g_print ("Unable to read the element: %s", error->message); + * } + * ]| + * + * #JsonReader is available since JSON-GLib 0.12. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "json-reader.h" + +#include "json-types-private.h" + +#include "json-debug.h" + +#define json_reader_return_if_error_set(r) G_STMT_START { \ + if (((JsonReader *) (r))->priv->error != NULL) \ + return; } G_STMT_END + +#define json_reader_return_val_if_error_set(r,v) G_STMT_START { \ + if (((JsonReader *) (r))->priv->error != NULL) \ + return (v); } G_STMT_END + +struct _JsonReaderPrivate +{ + JsonNode *root; + + JsonNode *current_node; + JsonNode *previous_node; + + gchar *current_member; + + GError *error; +}; + +enum +{ + PROP_0, + + PROP_ROOT, + + PROP_LAST +}; + +static GParamSpec *reader_properties[PROP_LAST] = { NULL, }; + +G_DEFINE_TYPE (JsonReader, json_reader, G_TYPE_OBJECT); + +static void +json_reader_finalize (GObject *gobject) +{ + JsonReaderPrivate *priv = JSON_READER (gobject)->priv; + + if (priv->root != NULL) + json_node_free (priv->root); + + if (priv->error != NULL) + g_clear_error (&priv->error); + + g_free (priv->current_member); + + G_OBJECT_CLASS (json_reader_parent_class)->finalize (gobject); +} + +static void +json_reader_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_ROOT: + json_reader_set_root (JSON_READER (gobject), g_value_get_boxed (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +json_reader_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_ROOT: + g_value_set_boxed (value, JSON_READER (gobject)->priv->root); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +json_reader_class_init (JsonReaderClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (JsonReaderPrivate)); + + /** + * JsonReader:root: + * + * The root of the JSON tree that the #JsonReader should read. + * + * Since: 0.12 + */ + reader_properties[PROP_ROOT] = + g_param_spec_boxed ("root", + "Root Node", + "The root of the tree to read", + JSON_TYPE_NODE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + gobject_class->finalize = json_reader_finalize; + gobject_class->set_property = json_reader_set_property; + gobject_class->get_property = json_reader_get_property; + g_object_class_install_properties (gobject_class, PROP_LAST, reader_properties); +} + +static void +json_reader_init (JsonReader *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, JSON_TYPE_READER, + JsonReaderPrivate); +} + +GQuark +json_reader_error_quark (void) +{ + return g_quark_from_static_string ("json-reader-error"); +} + +/** + * json_reader_new: + * @node: (allow-none): a #JsonNode, or %NULL + * + * Creates a new #JsonReader. You can use this object to read the contents of + * the JSON tree starting from @node + * + * Return value: the newly created #JsonReader. Use g_object_unref() to + * release the allocated resources when done + * + * Since: 0.12 + */ +JsonReader * +json_reader_new (JsonNode *node) +{ + return g_object_new (JSON_TYPE_READER, "root", node, NULL); +} + +/* + * json_reader_unset_error: + * @reader: a #JsonReader + * + * Unsets the error state of @reader, if set + */ +static inline void +json_reader_unset_error (JsonReader *reader) +{ + if (reader->priv->error != NULL) + g_clear_error (&(reader->priv->error)); +} + +/** + * json_reader_set_root: + * @reader: a #JsonReader + * @root: (allow-none): a #JsonNode + * + * Sets the root #JsonNode to be read by @reader. The @reader will take + * a copy of @root + * + * If another #JsonNode is currently set as root, it will be replaced. + * + * Since: 0.12 + */ +void +json_reader_set_root (JsonReader *reader, + JsonNode *root) +{ + JsonReaderPrivate *priv; + + g_return_if_fail (JSON_IS_READER (reader)); + + priv = reader->priv; + + if (priv->root == root) + return; + + if (priv->root != NULL) + { + json_node_free (priv->root); + priv->root = NULL; + priv->current_node = NULL; + priv->previous_node = NULL; + } + + if (root != NULL) + { + priv->root = json_node_copy (root); + priv->current_node = priv->root; + priv->previous_node = NULL; + } + + g_object_notify_by_pspec (G_OBJECT (reader), reader_properties[PROP_ROOT]); +} + +/* + * json_reader_ser_error: + * @reader: a #JsonReader + * @error_code: the #JsonReaderError code for the error + * @error_fmt: format string + * @Varargs: list of arguments for the @error_fmt string + * + * Sets the error state of @reader using the given error code + * and string + * + * Return value: %FALSE, to be used to return immediately from + * the caller function + */ +static gboolean +json_reader_set_error (JsonReader *reader, + JsonReaderError error_code, + const gchar *error_fmt, + ...) +{ + JsonReaderPrivate *priv = reader->priv; + va_list args; + gchar *error_msg; + + if (priv->error != NULL) + g_clear_error (&priv->error); + + va_start (args, error_fmt); + error_msg = g_strdup_vprintf (error_fmt, args); + va_end (args); + + g_set_error_literal (&priv->error, JSON_READER_ERROR, + error_code, + error_msg); + + g_free (error_msg); + + return FALSE; +} + +/** + * json_reader_get_error: + * @reader: a #JsonReader + * + * Retrieves the #GError currently set on @reader, if the #JsonReader + * is in error state + * + * Return value: (transfer none): the pointer to the error, or %NULL + * + * Since: 0.12 + */ +const GError * +json_reader_get_error (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + + return reader->priv->error; +} + +/** + * json_reader_is_array: + * @reader: a #JsonReader + * + * Checks whether the @reader is currently on an array + * + * Return value: %TRUE if the #JsonReader is on an array, and %FALSE + * otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_is_array (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + return FALSE; + + return JSON_NODE_HOLDS_ARRAY (reader->priv->current_node); +} + +/** + * json_reader_is_object: + * @reader: a #JsonReader + * + * Checks whether the @reader is currently on an object + * + * Return value: %TRUE if the #JsonReader is on an object, and %FALSE + * otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_is_object (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + return FALSE; + + return JSON_NODE_HOLDS_OBJECT (reader->priv->current_node); +} + +/** + * json_reader_is_value: + * @reader: a #JsonReader + * + * Checks whether the @reader is currently on a value + * + * Return value: %TRUE if the #JsonReader is on a value, and %FALSE + * otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_is_value (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + return FALSE; + + return JSON_NODE_HOLDS_VALUE (reader->priv->current_node); +} + +/** + * json_reader_read_element: + * @reader: a #JsonReader + * @index_: the index of the element + * + * Advances the cursor of @reader to the element @index_ of the array + * or the object at the current position. + * + * You can use the json_reader_get_value* family of functions to retrieve + * the value of the element; for instance: + * + * |[ + * json_reader_read_element (reader, 0); + * int_value = json_reader_get_int_value (reader); + * ]| + * + * After reading the value, json_reader_end_element() should be called to + * reposition the cursor inside the #JsonReader, e.g.: + * + * |[ + * json_reader_read_element (reader, 1); + * str_value = json_reader_get_string_value (reader); + * json_reader_end_element (reader); + * + * json_reader_read_element (reader, 2); + * str_value = json_reader_get_string_value (reader); + * json_reader_end_element (reader); + * ]| + * + * If @reader is not currently on an array or an object, or if the @index_ is + * bigger than the size of the array or the object, the #JsonReader will be + * put in an error state until json_reader_end_element() is called. + * + * Return value: %TRUE on success, and %FALSE otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_read_element (JsonReader *reader, + guint index_) +{ + JsonReaderPrivate *priv; + + g_return_val_if_fail (JSON_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + priv = reader->priv; + + if (priv->current_node == NULL) + priv->current_node = priv->root; + + if (!(JSON_NODE_HOLDS_ARRAY (priv->current_node) || + JSON_NODE_HOLDS_OBJECT (priv->current_node))) + return json_reader_set_error (reader, JSON_READER_ERROR_NO_ARRAY, + _("The current node is of type '%s', but " + "an array or an object was expected."), + json_node_type_name (priv->current_node)); + + switch (json_node_get_node_type (priv->current_node)) + { + case JSON_NODE_ARRAY: + { + JsonArray *array = json_node_get_array (priv->current_node); + + if (index_ >= json_array_get_length (array)) + return json_reader_set_error (reader, JSON_READER_ERROR_INVALID_INDEX, + _("The index '%d' is greater than the size " + "of the array at the current position."), + index_); + + priv->previous_node = priv->current_node; + priv->current_node = json_array_get_element (array, index_); + } + break; + + case JSON_NODE_OBJECT: + { + JsonObject *object = json_node_get_object (priv->current_node); + GList *members; + const gchar *name; + + if (index_ >= json_object_get_size (object)) + return json_reader_set_error (reader, JSON_READER_ERROR_INVALID_INDEX, + _("The index '%d' is greater than the size " + "of the object at the current position."), + index_); + + priv->previous_node = priv->current_node; + g_free (priv->current_member); + + members = json_object_get_members (object); + name = g_list_nth_data (members, index_); + + priv->current_node = json_object_get_member (object, name); + priv->current_member = g_strdup (name); + + g_list_free (members); + } + break; + + default: + g_assert_not_reached (); + return FALSE; + } + + return TRUE; +} + +/** + * json_reader_end_element: + * @reader: a #JsonReader + * + * Moves the cursor back to the previous node after being positioned + * inside an array + * + * This function resets the error state of @reader, if any was set + * + * Since: 0.12 + */ +void +json_reader_end_element (JsonReader *reader) +{ + JsonReaderPrivate *priv; + JsonNode *tmp; + + g_return_if_fail (JSON_IS_READER (reader)); + + json_reader_unset_error (reader); + + priv = reader->priv; + + if (priv->previous_node != NULL) + tmp = json_node_get_parent (priv->previous_node); + else + tmp = NULL; + + g_free (priv->current_member); + priv->current_member = NULL; + + priv->current_node = priv->previous_node; + priv->previous_node = tmp; +} + +/** + * json_reader_count_elements: + * @reader: a #JsonReader + * + * Counts the elements of the current position, if @reader is + * positioned on an array + * + * Return value: the number of elements, or -1. In case of failure + * the #JsonReader is set in an error state + * + * Since: 0.12 + */ +gint +json_reader_count_elements (JsonReader *reader) +{ + JsonReaderPrivate *priv; + + g_return_val_if_fail (JSON_IS_READER (reader), -1); + + priv = reader->priv; + + if (priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return -1; + } + + if (!JSON_NODE_HOLDS_ARRAY (priv->current_node)) + { + json_reader_set_error (reader, JSON_READER_ERROR_NO_ARRAY, + _("The current position holds a '%s' and not an array"), + json_node_type_get_name (JSON_NODE_TYPE (priv->current_node))); + return -1; + } + + return json_array_get_length (json_node_get_array (priv->current_node)); +} + +/** + * json_reader_read_member: + * @reader: a #JsonReader + * @member_name: the name of the member to read + * + * Advances the cursor of @reader to the @member_name of the object at the + * current position. + * + * You can use the json_reader_get_value* family of functions to retrieve + * the value of the member; for instance: + * + * |[ + * json_reader_read_member (reader, "width"); + * width = json_reader_get_int_value (reader); + * ]| + * + * After reading the value, json_reader_end_member() should be called to + * reposition the cursor inside the #JsonReader, e.g.: + * + * |[ + * json_reader_read_member (reader, "author"); + * author = json_reader_get_string_value (reader); + * json_reader_end_element (reader); + * + * json_reader_read_element (reader, "title"); + * title = json_reader_get_string_value (reader); + * json_reader_end_element (reader); + * ]| + * + * If @reader is not currently on an object, or if the @member_name is not + * defined in the object, the #JsonReader will be put in an error state until + * json_reader_end_member() is called. + * + * Return value: %TRUE on success, and %FALSE otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_read_member (JsonReader *reader, + const gchar *member_name) +{ + JsonReaderPrivate *priv; + JsonObject *object; + + g_return_val_if_fail (JSON_READER (reader), FALSE); + g_return_val_if_fail (member_name != NULL, FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + priv = reader->priv; + + if (priv->current_node == NULL) + priv->current_node = priv->root; + + if (!JSON_NODE_HOLDS_OBJECT (priv->current_node)) + return json_reader_set_error (reader, JSON_READER_ERROR_NO_OBJECT, + _("The current node is of type '%s', but " + "an object was expected."), + json_node_type_name (priv->current_node)); + + object = json_node_get_object (priv->current_node); + if (!json_object_has_member (object, member_name)) + return json_reader_set_error (reader, JSON_READER_ERROR_INVALID_MEMBER, + _("The member '%s' is not defined in the " + "object at the current position."), + member_name); + + g_free (priv->current_member); + + priv->previous_node = priv->current_node; + priv->current_node = json_object_get_member (object, member_name); + priv->current_member = g_strdup (member_name); + + return TRUE; +} + +/** + * json_reader_end_member: + * @reader: a #JsonReader + * + * Moves the cursor back to the previous node after being positioned + * inside an object + * + * This function resets the error state of @reader, if any was set + * + * Since: 0.12 + */ +void +json_reader_end_member (JsonReader *reader) +{ + JsonReaderPrivate *priv; + JsonNode *tmp; + + g_return_if_fail (JSON_IS_READER (reader)); + + json_reader_unset_error (reader); + + priv = reader->priv; + + if (priv->previous_node != NULL) + tmp = json_node_get_parent (priv->previous_node); + else + tmp = NULL; + + g_free (priv->current_member); + priv->current_member = NULL; + + priv->current_node = priv->previous_node; + priv->previous_node = tmp; +} + +/** + * json_reader_list_members: + * @reader: a #JsonReader + * + * Retrieves a list of member names from the current position, if @reader + * is positioned on an object. + * + * Return value: (transfer full): a newly allocated, %NULL-terminated + * array of strings holding the members name. Use g_strfreev() when + * done. + * + * Since: 0.14 + */ +gchar ** +json_reader_list_members (JsonReader *reader) +{ + JsonReaderPrivate *priv; + GList *members, *l; + gchar **retval; + gint i; + + g_return_val_if_fail (JSON_IS_READER (reader), NULL); + + priv = reader->priv; + + if (priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return NULL; + } + + if (!JSON_NODE_HOLDS_OBJECT (priv->current_node)) + { + json_reader_set_error (reader, JSON_READER_ERROR_NO_OBJECT, + _("The current position holds a '%s' and not an object"), + json_node_type_get_name (JSON_NODE_TYPE (priv->current_node))); + return NULL; + } + + members = json_object_get_members (json_node_get_object (priv->current_node)); + if (members == NULL) + return NULL; + + retval = g_new (gchar*, g_list_length (members) + 1); + for (l = members, i = 0; l != NULL; l = l->next, i += 1) + retval[i] = g_strdup (l->data); + + retval[i] = NULL; + + g_list_free (members); + + return retval; +} + +/** + * json_reader_count_members: + * @reader: a #JsonReader + * + * Counts the members of the current position, if @reader is + * positioned on an object + * + * Return value: the number of members, or -1. In case of failure + * the #JsonReader is set in an error state + * + * Since: 0.12 + */ +gint +json_reader_count_members (JsonReader *reader) +{ + JsonReaderPrivate *priv; + + g_return_val_if_fail (JSON_IS_READER (reader), -1); + + priv = reader->priv; + + if (priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return -1; + } + + if (!JSON_NODE_HOLDS_OBJECT (priv->current_node)) + { + json_reader_set_error (reader, JSON_READER_ERROR_NO_OBJECT, + _("The current position holds a '%s' and not an object"), + json_node_type_get_name (JSON_NODE_TYPE (priv->current_node))); + return -1; + } + + return json_object_get_size (json_node_get_object (priv->current_node)); +} + +/** + * json_reader_get_value: + * @reader: a #JsonReader + * + * Retrieves the #JsonNode of the current position of @reader + * + * Return value: (transfer none): a #JsonNode, or %NULL. The returned node + * is owned by the #JsonReader and it should not be modified or freed + * directly + * + * Since: 0.12 + */ +JsonNode * +json_reader_get_value (JsonReader *reader) +{ + JsonNode *node; + + g_return_val_if_fail (JSON_IS_READER (reader), NULL); + json_reader_return_val_if_error_set (reader, NULL); + + if (reader->priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return NULL; + } + + node = reader->priv->current_node; + + if (!JSON_NODE_HOLDS_VALUE (node)) + { + json_reader_set_error (reader, JSON_READER_ERROR_NO_VALUE, + _("The current position holds a '%s' and not a value"), + json_node_type_get_name (JSON_NODE_TYPE (node))); + return NULL; + } + + return reader->priv->current_node; +} + +/** + * json_reader_get_int_value: + * @reader: a #JsonReader + * + * Retrieves the integer value of the current position of @reader + * + * Return value: the integer value + * + * Since: 0.12 + */ +gint64 +json_reader_get_int_value (JsonReader *reader) +{ + JsonNode *node; + + g_return_val_if_fail (JSON_IS_READER (reader), 0); + json_reader_return_val_if_error_set (reader, 0); + + if (reader->priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return 0; + } + + node = reader->priv->current_node; + + if (!JSON_NODE_HOLDS_VALUE (node)) + { + json_reader_set_error (reader, JSON_READER_ERROR_NO_VALUE, + _("The current position holds a '%s' and not a value"), + json_node_type_get_name (JSON_NODE_TYPE (node))); + return 0; + } + + if (json_node_get_value_type (node) != G_TYPE_INT64) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_TYPE, + _("The current position does not hold an integer type")); + return 0; + } + + return json_node_get_int (reader->priv->current_node); +} + +/** + * json_reader_get_double_value: + * @reader: a #JsonReader + * + * Retrieves the floating point value of the current position of @reader + * + * Return value: the floating point value + * + * Since: 0.12 + */ +gdouble +json_reader_get_double_value (JsonReader *reader) +{ + JsonNode *node; + + g_return_val_if_fail (JSON_IS_READER (reader), 0.0); + json_reader_return_val_if_error_set (reader, 0.0); + + if (reader->priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return 0.0; + } + + node = reader->priv->current_node; + + if (!JSON_NODE_HOLDS_VALUE (node)) + { + json_reader_set_error (reader, JSON_READER_ERROR_NO_VALUE, + _("The current position holds a '%s' and not a value"), + json_node_type_get_name (JSON_NODE_TYPE (node))); + return 0.0; + } + + if (json_node_get_value_type (node) != G_TYPE_DOUBLE) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_TYPE, + _("The current position does not hold a floating point type")); + return 0.0; + } + + return json_node_get_double (reader->priv->current_node); +} + +/** + * json_reader_get_string_value: + * @reader: a #JsonReader + * + * Retrieves the string value of the current position of @reader + * + * Return value: the string value + * + * Since: 0.12 + */ +const gchar * +json_reader_get_string_value (JsonReader *reader) +{ + JsonNode *node; + + g_return_val_if_fail (JSON_IS_READER (reader), NULL); + json_reader_return_val_if_error_set (reader, NULL); + + if (reader->priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return NULL; + } + + node = reader->priv->current_node; + + if (!JSON_NODE_HOLDS_VALUE (node)) + { + json_reader_set_error (reader, JSON_READER_ERROR_NO_VALUE, + _("The current position holds a '%s' and not a value"), + json_node_type_get_name (JSON_NODE_TYPE (node))); + return NULL; + } + + if (json_node_get_value_type (node) != G_TYPE_STRING) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_TYPE, + _("The current position does not hold a string type")); + return NULL; + } + + return json_node_get_string (reader->priv->current_node); +} + +/** + * json_reader_get_boolean_value: + * @reader: a #JsonReader + * + * Retrieves the boolean value of the current position of @reader + * + * Return value: the boolean value + * + * Since: 0.12 + */ +gboolean +json_reader_get_boolean_value (JsonReader *reader) +{ + JsonNode *node; + + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return FALSE; + } + + node = reader->priv->current_node; + + if (!JSON_NODE_HOLDS_VALUE (node)) + { + json_reader_set_error (reader, JSON_READER_ERROR_NO_VALUE, + _("The current position holds a '%s' and not a value"), + json_node_type_get_name (JSON_NODE_TYPE (node))); + return FALSE; + } + + if (json_node_get_value_type (node) != G_TYPE_BOOLEAN) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_TYPE, + _("The current position does not hold a boolean type")); + return FALSE; + } + + return json_node_get_boolean (node); +} + +/** + * json_reader_get_null_value: + * @reader: a #JsonReader + * + * Checks whether the value of the current position of @reader is 'null' + * + * Return value: %TRUE if 'null' is set, and %FALSE otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_get_null_value (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return FALSE; + } + + return JSON_NODE_HOLDS_NULL (reader->priv->current_node); +} + +/** + * json_reader_get_member_name: + * @reader: a #JsonReader + * + * Retrieves the name of the current member. + * + * Return value: (transfer none): the name of the member, or %NULL + * + * Since: 0.14 + */ +const gchar * +json_reader_get_member_name (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), NULL); + json_reader_return_val_if_error_set (reader, NULL); + + if (reader->priv->current_node == NULL) + { + json_reader_set_error (reader, JSON_READER_ERROR_INVALID_NODE, + _("No node available at the current position")); + return FALSE; + } + + return reader->priv->current_member; +} diff --git a/json-glib/json-glib/json-reader.h b/json-glib/json-glib/json-reader.h new file mode 100644 index 0000000..6ee0b54 --- /dev/null +++ b/json-glib/json-glib/json-reader.h @@ -0,0 +1,150 @@ +/* json-reader.h - JSON cursor parser + * + * This file is part of JSON-GLib + * Copyright (C) 2010 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_READER_H__ +#define __JSON_READER_H__ + +#include + +G_BEGIN_DECLS + +#define JSON_TYPE_READER (json_reader_get_type ()) +#define JSON_READER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JSON_TYPE_READER, JsonReader)) +#define JSON_IS_READER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JSON_TYPE_READER)) +#define JSON_READER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), JSON_TYPE_READER, JsonReaderClass)) +#define JSON_IS_READER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), JSON_TYPE_READER)) +#define JSON_READER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), JSON_TYPE_READER, JsonReaderClass)) + +/** + * JSON_READER_ERROR: + * + * Error domain for #JsonReader errors + * + * Since: 0.12 + */ +#define JSON_READER_ERROR (json_reader_error_quark ()) + +typedef struct _JsonReader JsonReader; +typedef struct _JsonReaderPrivate JsonReaderPrivate; +typedef struct _JsonReaderClass JsonReaderClass; + +/** + * JsonReaderError: + * @JSON_READER_ERROR_NO_ARRAY: No array found at the current position + * @JSON_READER_ERROR_INVALID_INDEX: Index out of bounds + * @JSON_READER_ERROR_NO_OBJECT: No object found at the current position + * @JSON_READER_ERROR_INVALID_MEMBER: Member not found + * @JSON_READER_ERROR_INVALID_NODE: No valid node found at the current position + * @JSON_READER_ERROR_NO_VALUE: The node at the current position does not + * hold a value + * @JSON_READER_ERROR_INVALID_TYPE: The node at the current position does not + * hold a value of the desired type + * + * Error codes enumeration for #JsonReader errors + * + * Since: 0.12 + */ +typedef enum { + JSON_READER_ERROR_NO_ARRAY, + JSON_READER_ERROR_INVALID_INDEX, + JSON_READER_ERROR_NO_OBJECT, + JSON_READER_ERROR_INVALID_MEMBER, + JSON_READER_ERROR_INVALID_NODE, + JSON_READER_ERROR_NO_VALUE, + JSON_READER_ERROR_INVALID_TYPE +} JsonReaderError; + +/** + * JsonReader: + * + * The JsonReader structure contains only + * private data and should only be accessed using the provided API + * + * Since: 0.12 + */ +struct _JsonReader +{ + /*< private >*/ + GObject parent_instance; + + JsonReaderPrivate *priv; +}; + +/** + * JsonReaderClass: + * + * The JsonReaderClass structure contains only + * private data + * + * Since: 0.12 + */ +struct _JsonReaderClass +{ + /*< private >*/ + GObjectClass parent_class; + + void (*_json_padding0) (void); + void (*_json_padding1) (void); + void (*_json_padding2) (void); + void (*_json_padding3) (void); + void (*_json_padding4) (void); +}; + +GQuark json_reader_error_quark (void); +GType json_reader_get_type (void) G_GNUC_CONST; + +JsonReader * json_reader_new (JsonNode *node); + +void json_reader_set_root (JsonReader *reader, + JsonNode *root); + +const GError * json_reader_get_error (JsonReader *reader); + +gboolean json_reader_is_array (JsonReader *reader); +gboolean json_reader_read_element (JsonReader *reader, + guint index_); +void json_reader_end_element (JsonReader *reader); +gint json_reader_count_elements (JsonReader *reader); + +gboolean json_reader_is_object (JsonReader *reader); +gboolean json_reader_read_member (JsonReader *reader, + const gchar *member_name); +void json_reader_end_member (JsonReader *reader); +gint json_reader_count_members (JsonReader *reader); +gchar ** json_reader_list_members (JsonReader *reader); +const gchar * json_reader_get_member_name (JsonReader *reader); + +gboolean json_reader_is_value (JsonReader *reader); +JsonNode * json_reader_get_value (JsonReader *reader); +gint64 json_reader_get_int_value (JsonReader *reader); +gdouble json_reader_get_double_value (JsonReader *reader); +const gchar * json_reader_get_string_value (JsonReader *reader); +gboolean json_reader_get_boolean_value (JsonReader *reader); +gboolean json_reader_get_null_value (JsonReader *reader); + +G_END_DECLS + +#endif /* __JSON_READER_H__ */ diff --git a/json-glib/json-glib/json-scanner.c b/json-glib/json-glib/json-scanner.c new file mode 100644 index 0000000..6eca2ba --- /dev/null +++ b/json-glib/json-glib/json-scanner.c @@ -0,0 +1,1861 @@ +/* json-scanner.c: Tokenizer for JSON + * Copyright (C) 2008 OpenedHand + * + * Based on JsonScanner: Flexible lexical scanner for general purpose. + * Copyright (C) 1997, 1998 Tim Janik + * + * Modified by Emmanuele Bassi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "json-scanner.h" + +#ifdef G_OS_WIN32 +#include /* For _read() */ +#endif + +struct _JsonScannerConfig +{ + /* Character sets + */ + gchar *cset_skip_characters; /* default: " \t\n" */ + gchar *cset_identifier_first; + gchar *cset_identifier_nth; + gchar *cpair_comment_single; /* default: "#\n" */ + + /* Should symbol lookup work case sensitive? */ + guint case_sensitive : 1; + + /* Boolean values to be adjusted "on the fly" + * to configure scanning behaviour. + */ + guint skip_comment_multi : 1; /* C like comment */ + guint skip_comment_single : 1; /* single line comment */ + guint scan_comment_multi : 1; /* scan multi line comments? */ + guint scan_identifier : 1; + guint scan_identifier_1char : 1; + guint scan_identifier_NULL : 1; + guint scan_symbols : 1; + guint scan_binary : 1; + guint scan_octal : 1; + guint scan_float : 1; + guint scan_hex : 1; /* `0x0ff0' */ + guint scan_hex_dollar : 1; /* `$0ff0' */ + guint scan_string_sq : 1; /* string: 'anything' */ + guint scan_string_dq : 1; /* string: "\\-escapes!\n" */ + guint numbers_2_int : 1; /* bin, octal, hex => int */ + guint int_2_float : 1; /* int => G_TOKEN_FLOAT? */ + guint identifier_2_string : 1; + guint char_2_token : 1; /* return G_TOKEN_CHAR? */ + guint symbol_2_token : 1; + guint scope_0_fallback : 1; /* try scope 0 on lookups? */ + guint store_int64 : 1; /* use value.v_int64 rather than v_int */ + guint padding_dummy; +}; + +static JsonScannerConfig json_scanner_config_template = +{ + ( " \t\r\n" ) /* cset_skip_characters */, + ( + "_" + G_CSET_a_2_z + G_CSET_A_2_Z + ) /* cset_identifier_first */, + ( + G_CSET_DIGITS + "-_" + G_CSET_a_2_z + G_CSET_A_2_Z + ) /* cset_identifier_nth */, + ( "//\n" ) /* cpair_comment_single */, + TRUE /* case_sensitive */, + TRUE /* skip_comment_multi */, + TRUE /* skip_comment_single */, + FALSE /* scan_comment_multi */, + TRUE /* scan_identifier */, + TRUE /* scan_identifier_1char */, + FALSE /* scan_identifier_NULL */, + TRUE /* scan_symbols */, + TRUE /* scan_binary */, + TRUE /* scan_octal */, + TRUE /* scan_float */, + TRUE /* scan_hex */, + TRUE /* scan_hex_dollar */, + TRUE /* scan_string_sq */, + TRUE /* scan_string_dq */, + TRUE /* numbers_2_int */, + FALSE /* int_2_float */, + FALSE /* identifier_2_string */, + TRUE /* char_2_token */, + TRUE /* symbol_2_token */, + FALSE /* scope_0_fallback */, + TRUE /* store_int64 */ +}; + +/* --- defines --- */ +#define to_lower(c) ( \ + (guchar) ( \ + ( (((guchar)(c))>='A' && ((guchar)(c))<='Z') * ('a'-'A') ) | \ + ( (((guchar)(c))>=192 && ((guchar)(c))<=214) * (224-192) ) | \ + ( (((guchar)(c))>=216 && ((guchar)(c))<=222) * (248-216) ) | \ + ((guchar)(c)) \ + ) \ +) + +#define READ_BUFFER_SIZE (4000) + +static const gchar json_symbol_names[] = + "true\0" + "false\0" + "null\0" + "var\0"; + +static const struct +{ + guint name_offset; + guint token; +} json_symbols[] = { + { 0, JSON_TOKEN_TRUE }, + { 5, JSON_TOKEN_FALSE }, + { 11, JSON_TOKEN_NULL }, + { 16, JSON_TOKEN_VAR } +}; + +static const guint n_json_symbols = G_N_ELEMENTS (json_symbols); + +/* --- typedefs --- */ +typedef struct _JsonScannerKey JsonScannerKey; + +struct _JsonScannerKey +{ + guint scope_id; + gchar *symbol; + gpointer value; +}; + +/* --- prototypes --- */ +static gboolean json_scanner_key_equal (gconstpointer v1, + gconstpointer v2); +static guint json_scanner_key_hash (gconstpointer v); + +static inline +JsonScannerKey *json_scanner_lookup_internal (JsonScanner *scanner, + guint scope_id, + const gchar *symbol); +static void json_scanner_get_token_ll (JsonScanner *scanner, + GTokenType *token_p, + GTokenValue *value_p, + guint *line_p, + guint *position_p); +static void json_scanner_get_token_i (JsonScanner *scanner, + GTokenType *token_p, + GTokenValue *value_p, + guint *line_p, + guint *position_p); + +static guchar json_scanner_peek_next_char (JsonScanner *scanner); +static guchar json_scanner_get_char (JsonScanner *scanner, + guint *line_p, + guint *position_p); +static gunichar json_scanner_get_unichar (JsonScanner *scanner, + guint *line_p, + guint *position_p); +static void json_scanner_msg_handler (JsonScanner *scanner, + gchar *message, + gboolean is_error); + +/* --- functions --- */ +static inline gint +json_scanner_char_2_num (guchar c, + guchar base) +{ + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'Z') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'z') + c -= 'a' - 10; + else + return -1; + + if (c < base) + return c; + + return -1; +} + +JsonScanner * +json_scanner_new (void) +{ + JsonScanner *scanner; + JsonScannerConfig *config_templ; + + config_templ = &json_scanner_config_template; + + scanner = g_new0 (JsonScanner, 1); + + scanner->user_data = NULL; + scanner->max_parse_errors = 1; + scanner->parse_errors = 0; + scanner->input_name = NULL; + g_datalist_init (&scanner->qdata); + + scanner->config = g_new0 (JsonScannerConfig, 1); + + scanner->config->case_sensitive = config_templ->case_sensitive; + scanner->config->cset_skip_characters = config_templ->cset_skip_characters; + if (!scanner->config->cset_skip_characters) + scanner->config->cset_skip_characters = ""; + scanner->config->cset_identifier_first = config_templ->cset_identifier_first; + scanner->config->cset_identifier_nth = config_templ->cset_identifier_nth; + scanner->config->cpair_comment_single = config_templ->cpair_comment_single; + scanner->config->skip_comment_multi = config_templ->skip_comment_multi; + scanner->config->skip_comment_single = config_templ->skip_comment_single; + scanner->config->scan_comment_multi = config_templ->scan_comment_multi; + scanner->config->scan_identifier = config_templ->scan_identifier; + scanner->config->scan_identifier_1char = config_templ->scan_identifier_1char; + scanner->config->scan_identifier_NULL = config_templ->scan_identifier_NULL; + scanner->config->scan_symbols = config_templ->scan_symbols; + scanner->config->scan_binary = config_templ->scan_binary; + scanner->config->scan_octal = config_templ->scan_octal; + scanner->config->scan_float = config_templ->scan_float; + scanner->config->scan_hex = config_templ->scan_hex; + scanner->config->scan_hex_dollar = config_templ->scan_hex_dollar; + scanner->config->scan_string_sq = config_templ->scan_string_sq; + scanner->config->scan_string_dq = config_templ->scan_string_dq; + scanner->config->numbers_2_int = config_templ->numbers_2_int; + scanner->config->int_2_float = config_templ->int_2_float; + scanner->config->identifier_2_string = config_templ->identifier_2_string; + scanner->config->char_2_token = config_templ->char_2_token; + scanner->config->symbol_2_token = config_templ->symbol_2_token; + scanner->config->scope_0_fallback = config_templ->scope_0_fallback; + scanner->config->store_int64 = config_templ->store_int64; + + scanner->token = G_TOKEN_NONE; + scanner->value.v_int64 = 0; + scanner->line = 1; + scanner->position = 0; + + scanner->next_token = G_TOKEN_NONE; + scanner->next_value.v_int64 = 0; + scanner->next_line = 1; + scanner->next_position = 0; + + scanner->symbol_table = g_hash_table_new (json_scanner_key_hash, + json_scanner_key_equal); + scanner->input_fd = -1; + scanner->text = NULL; + scanner->text_end = NULL; + scanner->buffer = NULL; + scanner->scope_id = 0; + + scanner->msg_handler = json_scanner_msg_handler; + + return scanner; +} + +static inline void +json_scanner_free_value (GTokenType *token_p, + GTokenValue *value_p) +{ + switch (*token_p) + { + case G_TOKEN_STRING: + case G_TOKEN_IDENTIFIER: + case G_TOKEN_IDENTIFIER_NULL: + case G_TOKEN_COMMENT_SINGLE: + case G_TOKEN_COMMENT_MULTI: + g_free (value_p->v_string); + break; + + default: + break; + } + + *token_p = G_TOKEN_NONE; +} + +static void +json_scanner_destroy_symbol_table_entry (gpointer _key, + gpointer _value, + gpointer _data) +{ + JsonScannerKey *key = _key; + + g_free (key->symbol); + g_slice_free (JsonScannerKey, key); +} + +void +json_scanner_destroy (JsonScanner *scanner) +{ + g_return_if_fail (scanner != NULL); + + g_datalist_clear (&scanner->qdata); + g_hash_table_foreach (scanner->symbol_table, + json_scanner_destroy_symbol_table_entry, + NULL); + g_hash_table_destroy (scanner->symbol_table); + json_scanner_free_value (&scanner->token, &scanner->value); + json_scanner_free_value (&scanner->next_token, &scanner->next_value); + g_free (scanner->config); + g_free (scanner->buffer); + g_free (scanner); +} + +static void +json_scanner_msg_handler (JsonScanner *scanner, + gchar *message, + gboolean is_error) +{ + g_return_if_fail (scanner != NULL); + + g_fprintf (stderr, "%s:%d: ", + scanner->input_name ? scanner->input_name : "", + scanner->line); + if (is_error) + g_fprintf (stderr, "error: "); + + g_fprintf (stderr, "%s\n", message); +} + +void +json_scanner_error (JsonScanner *scanner, + const gchar *format, + ...) +{ + g_return_if_fail (scanner != NULL); + g_return_if_fail (format != NULL); + + scanner->parse_errors++; + + if (scanner->msg_handler) + { + va_list args; + gchar *string; + + va_start (args, format); + string = g_strdup_vprintf (format, args); + va_end (args); + + scanner->msg_handler (scanner, string, TRUE); + + g_free (string); + } +} + +void +json_scanner_warn (JsonScanner *scanner, + const gchar *format, + ...) +{ + g_return_if_fail (scanner != NULL); + g_return_if_fail (format != NULL); + + if (scanner->msg_handler) + { + va_list args; + gchar *string; + + va_start (args, format); + string = g_strdup_vprintf (format, args); + va_end (args); + + scanner->msg_handler (scanner, string, FALSE); + + g_free (string); + } +} + +static gboolean +json_scanner_key_equal (gconstpointer v1, + gconstpointer v2) +{ + const JsonScannerKey *key1 = v1; + const JsonScannerKey *key2 = v2; + + return (key1->scope_id == key2->scope_id) && + (strcmp (key1->symbol, key2->symbol) == 0); +} + +static guint +json_scanner_key_hash (gconstpointer v) +{ + const JsonScannerKey *key = v; + gchar *c; + guint h; + + h = key->scope_id; + for (c = key->symbol; *c; c++) + h = (h << 5) - h + *c; + + return h; +} + +static inline JsonScannerKey * +json_scanner_lookup_internal (JsonScanner *scanner, + guint scope_id, + const gchar *symbol) +{ + JsonScannerKey *key_p; + JsonScannerKey key; + + key.scope_id = scope_id; + + if (!scanner->config->case_sensitive) + { + gchar *d; + const gchar *c; + + key.symbol = g_new (gchar, strlen (symbol) + 1); + for (d = key.symbol, c = symbol; *c; c++, d++) + *d = to_lower (*c); + *d = 0; + key_p = g_hash_table_lookup (scanner->symbol_table, &key); + g_free (key.symbol); + } + else + { + key.symbol = (gchar*) symbol; + key_p = g_hash_table_lookup (scanner->symbol_table, &key); + } + + return key_p; +} + +void +json_scanner_scope_add_symbol (JsonScanner *scanner, + guint scope_id, + const gchar *symbol, + gpointer value) +{ + JsonScannerKey *key; + + g_return_if_fail (scanner != NULL); + g_return_if_fail (symbol != NULL); + + key = json_scanner_lookup_internal (scanner, scope_id, symbol); + if (!key) + { + key = g_slice_new (JsonScannerKey); + key->scope_id = scope_id; + key->symbol = g_strdup (symbol); + key->value = value; + if (!scanner->config->case_sensitive) + { + gchar *c; + + c = key->symbol; + while (*c != 0) + { + *c = to_lower (*c); + c++; + } + } + + g_hash_table_insert (scanner->symbol_table, key, key); + } + else + key->value = value; +} + +void +json_scanner_scope_remove_symbol (JsonScanner *scanner, + guint scope_id, + const gchar *symbol) +{ + JsonScannerKey *key; + + g_return_if_fail (scanner != NULL); + g_return_if_fail (symbol != NULL); + + key = json_scanner_lookup_internal (scanner, scope_id, symbol); + if (key) + { + g_hash_table_remove (scanner->symbol_table, key); + g_free (key->symbol); + g_slice_free (JsonScannerKey, key); + } +} + +gpointer +json_scanner_lookup_symbol (JsonScanner *scanner, + const gchar *symbol) +{ + JsonScannerKey *key; + guint scope_id; + + g_return_val_if_fail (scanner != NULL, NULL); + + if (!symbol) + return NULL; + + scope_id = scanner->scope_id; + key = json_scanner_lookup_internal (scanner, scope_id, symbol); + if (!key && scope_id && scanner->config->scope_0_fallback) + key = json_scanner_lookup_internal (scanner, 0, symbol); + + if (key) + return key->value; + else + return NULL; +} + +gpointer +json_scanner_scope_lookup_symbol (JsonScanner *scanner, + guint scope_id, + const gchar *symbol) +{ + JsonScannerKey *key; + + g_return_val_if_fail (scanner != NULL, NULL); + + if (!symbol) + return NULL; + + key = json_scanner_lookup_internal (scanner, scope_id, symbol); + + if (key) + return key->value; + else + return NULL; +} + +guint +json_scanner_set_scope (JsonScanner *scanner, + guint scope_id) +{ + guint old_scope_id; + + g_return_val_if_fail (scanner != NULL, 0); + + old_scope_id = scanner->scope_id; + scanner->scope_id = scope_id; + + return old_scope_id; +} + +typedef struct { + GHFunc func; + gpointer data; + guint scope_id; +} ForeachClosure; + +static void +json_scanner_foreach_internal (gpointer _key, + gpointer _value, + gpointer _user_data) +{ + JsonScannerKey *key = _value; + ForeachClosure *closure = _user_data; + + if (key->scope_id == closure->scope_id) + closure->func (key->symbol, key->value, closure->data); +} + +void +json_scanner_scope_foreach_symbol (JsonScanner *scanner, + guint scope_id, + GHFunc func, + gpointer user_data) +{ + ForeachClosure closure; + + g_return_if_fail (scanner != NULL); + g_return_if_fail (func != NULL); + + closure.func = func; + closure.data = user_data; + closure.scope_id = scope_id; + + g_hash_table_foreach (scanner->symbol_table, + json_scanner_foreach_internal, + &closure); +} + +GTokenType +json_scanner_peek_next_token (JsonScanner *scanner) +{ + g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF); + + if (scanner->next_token == G_TOKEN_NONE) + { + scanner->next_line = scanner->line; + scanner->next_position = scanner->position; + json_scanner_get_token_i (scanner, + &scanner->next_token, + &scanner->next_value, + &scanner->next_line, + &scanner->next_position); + } + + return scanner->next_token; +} + +GTokenType +json_scanner_get_next_token (JsonScanner *scanner) +{ + g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF); + + if (scanner->next_token != G_TOKEN_NONE) + { + json_scanner_free_value (&scanner->token, &scanner->value); + + scanner->token = scanner->next_token; + scanner->value = scanner->next_value; + scanner->line = scanner->next_line; + scanner->position = scanner->next_position; + scanner->next_token = G_TOKEN_NONE; + } + else + json_scanner_get_token_i (scanner, + &scanner->token, + &scanner->value, + &scanner->line, + &scanner->position); + + return scanner->token; +} + +GTokenType +json_scanner_cur_token (JsonScanner *scanner) +{ + g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF); + + return scanner->token; +} + +GTokenValue +json_scanner_cur_value (JsonScanner *scanner) +{ + GTokenValue v; + + v.v_int64 = 0; + + g_return_val_if_fail (scanner != NULL, v); + + /* MSC isn't capable of handling return scanner->value; ? */ + + v = scanner->value; + + return v; +} + +guint +json_scanner_cur_line (JsonScanner *scanner) +{ + g_return_val_if_fail (scanner != NULL, 0); + + return scanner->line; +} + +guint +json_scanner_cur_position (JsonScanner *scanner) +{ + g_return_val_if_fail (scanner != NULL, 0); + + return scanner->position; +} + +gboolean +json_scanner_eof (JsonScanner *scanner) +{ + g_return_val_if_fail (scanner != NULL, TRUE); + + return scanner->token == G_TOKEN_EOF || scanner->token == G_TOKEN_ERROR; +} + +void +json_scanner_input_file (JsonScanner *scanner, + gint input_fd) +{ + g_return_if_fail (scanner != NULL); + g_return_if_fail (input_fd >= 0); + + if (scanner->input_fd >= 0) + json_scanner_sync_file_offset (scanner); + + scanner->token = G_TOKEN_NONE; + scanner->value.v_int64 = 0; + scanner->line = 1; + scanner->position = 0; + scanner->next_token = G_TOKEN_NONE; + + scanner->input_fd = input_fd; + scanner->text = NULL; + scanner->text_end = NULL; + + if (!scanner->buffer) + scanner->buffer = g_new (gchar, READ_BUFFER_SIZE + 1); +} + +void +json_scanner_input_text (JsonScanner *scanner, + const gchar *text, + guint text_len) +{ + g_return_if_fail (scanner != NULL); + if (text_len) + g_return_if_fail (text != NULL); + else + text = NULL; + + if (scanner->input_fd >= 0) + json_scanner_sync_file_offset (scanner); + + scanner->token = G_TOKEN_NONE; + scanner->value.v_int64 = 0; + scanner->line = 1; + scanner->position = 0; + scanner->next_token = G_TOKEN_NONE; + + scanner->input_fd = -1; + scanner->text = text; + scanner->text_end = text + text_len; + + if (scanner->buffer) + { + g_free (scanner->buffer); + scanner->buffer = NULL; + } +} + +static guchar +json_scanner_peek_next_char (JsonScanner *scanner) +{ + if (scanner->text < scanner->text_end) + return *scanner->text; + else if (scanner->input_fd >= 0) + { + gint count; + gchar *buffer; + + buffer = scanner->buffer; + do + { + count = read (scanner->input_fd, buffer, READ_BUFFER_SIZE); + } + while (count == -1 && (errno == EINTR || errno == EAGAIN)); + + if (count < 1) + { + scanner->input_fd = -1; + + return 0; + } + else + { + scanner->text = buffer; + scanner->text_end = buffer + count; + + return *buffer; + } + } + else + return 0; +} + +void +json_scanner_sync_file_offset (JsonScanner *scanner) +{ + g_return_if_fail (scanner != NULL); + + /* for file input, rewind the filedescriptor to the current + * buffer position and blow the file read ahead buffer. useful + * for third party uses of our file descriptor, which hooks + * onto the current scanning position. + */ + + if (scanner->input_fd >= 0 && scanner->text_end > scanner->text) + { + gint buffered; + + buffered = scanner->text_end - scanner->text; + if (lseek (scanner->input_fd, - buffered, SEEK_CUR) >= 0) + { + /* we succeeded, blow our buffer's contents now */ + scanner->text = NULL; + scanner->text_end = NULL; + } + else + errno = 0; + } +} + +static guchar +json_scanner_get_char (JsonScanner *scanner, + guint *line_p, + guint *position_p) +{ + guchar fchar; + + if (scanner->text < scanner->text_end) + fchar = *(scanner->text++); + else if (scanner->input_fd >= 0) + { + gint count; + gchar *buffer; + + buffer = scanner->buffer; + do + { + count = read (scanner->input_fd, buffer, READ_BUFFER_SIZE); + } + while (count == -1 && (errno == EINTR || errno == EAGAIN)); + + if (count < 1) + { + scanner->input_fd = -1; + fchar = 0; + } + else + { + scanner->text = buffer + 1; + scanner->text_end = buffer + count; + fchar = *buffer; + if (!fchar) + { + json_scanner_sync_file_offset (scanner); + scanner->text_end = scanner->text; + scanner->input_fd = -1; + } + } + } + else + fchar = 0; + + if (fchar == '\n') + { + (*position_p) = 0; + (*line_p)++; + } + else if (fchar) + { + (*position_p)++; + } + + return fchar; +} + +#define is_hex_digit(c) (((c) >= '0' && (c) <= '9') || \ + ((c) >= 'a' && (c) <= 'f') || \ + ((c) >= 'A' && (c) <= 'F')) +#define to_hex_digit(c) (((c) <= '9') ? (c) - '0' : ((c) & 7) + 9) + +static gunichar +json_scanner_get_unichar (JsonScanner *scanner, + guint *line_p, + guint *position_p) +{ + gunichar uchar; + gchar ch; + gint i; + + uchar = 0; + for (i = 0; i < 4; i++) + { + ch = json_scanner_get_char (scanner, line_p, position_p); + + if (is_hex_digit (ch)) + uchar += ((gunichar) to_hex_digit (ch) << ((3 - i) * 4)); + else + break; + } + + g_assert (g_unichar_validate (uchar) || g_unichar_type (uchar) == G_UNICODE_SURROGATE); + + return uchar; +} + +void +json_scanner_unexp_token (JsonScanner *scanner, + GTokenType expected_token, + const gchar *identifier_spec, + const gchar *symbol_spec, + const gchar *symbol_name, + const gchar *message, + gint is_error) +{ + gchar *token_string; + guint token_string_len; + gchar *expected_string; + guint expected_string_len; + gchar *message_prefix; + gboolean print_unexp; + void (*msg_handler) (JsonScanner*, const gchar*, ...); + + g_return_if_fail (scanner != NULL); + + if (is_error) + msg_handler = json_scanner_error; + else + msg_handler = json_scanner_warn; + + if (!identifier_spec) + identifier_spec = "identifier"; + if (!symbol_spec) + symbol_spec = "symbol"; + + token_string_len = 56; + token_string = g_new (gchar, token_string_len + 1); + expected_string_len = 64; + expected_string = g_new (gchar, expected_string_len + 1); + print_unexp = TRUE; + + switch (scanner->token) + { + case G_TOKEN_EOF: + g_snprintf (token_string, token_string_len, "end of file"); + break; + + default: + if (scanner->token >= 1 && scanner->token <= 255) + { + if ((scanner->token >= ' ' && scanner->token <= '~') || + strchr (scanner->config->cset_identifier_first, scanner->token) || + strchr (scanner->config->cset_identifier_nth, scanner->token)) + g_snprintf (token_string, token_string_len, "character `%c'", scanner->token); + else + g_snprintf (token_string, token_string_len, "character `\\%o'", scanner->token); + break; + } + else if (!scanner->config->symbol_2_token) + { + g_snprintf (token_string, token_string_len, "(unknown) token <%d>", scanner->token); + break; + } + /* fall through */ + case G_TOKEN_SYMBOL: + if (expected_token == G_TOKEN_SYMBOL || + (scanner->config->symbol_2_token && + expected_token > G_TOKEN_LAST)) + print_unexp = FALSE; + if (symbol_name) + g_snprintf (token_string, token_string_len, + "%s%s `%s'", + print_unexp ? "" : "invalid ", + symbol_spec, + symbol_name); + else + g_snprintf (token_string, token_string_len, + "%s%s", + print_unexp ? "" : "invalid ", + symbol_spec); + break; + + case G_TOKEN_ERROR: + print_unexp = FALSE; + expected_token = G_TOKEN_NONE; + switch (scanner->value.v_error) + { + case G_ERR_UNEXP_EOF: + g_snprintf (token_string, token_string_len, "scanner: unexpected end of file"); + break; + + case G_ERR_UNEXP_EOF_IN_STRING: + g_snprintf (token_string, token_string_len, "scanner: unterminated string constant"); + break; + + case G_ERR_UNEXP_EOF_IN_COMMENT: + g_snprintf (token_string, token_string_len, "scanner: unterminated comment"); + break; + + case G_ERR_NON_DIGIT_IN_CONST: + g_snprintf (token_string, token_string_len, "scanner: non digit in constant"); + break; + + case G_ERR_FLOAT_RADIX: + g_snprintf (token_string, token_string_len, "scanner: invalid radix for floating constant"); + break; + + case G_ERR_FLOAT_MALFORMED: + g_snprintf (token_string, token_string_len, "scanner: malformed floating constant"); + break; + + case G_ERR_DIGIT_RADIX: + g_snprintf (token_string, token_string_len, "scanner: digit is beyond radix"); + break; + + case G_ERR_UNKNOWN: + default: + g_snprintf (token_string, token_string_len, "scanner: unknown error"); + break; + } + break; + + case G_TOKEN_CHAR: + g_snprintf (token_string, token_string_len, "character `%c'", scanner->value.v_char); + break; + + case G_TOKEN_IDENTIFIER: + case G_TOKEN_IDENTIFIER_NULL: + if (expected_token == G_TOKEN_IDENTIFIER || + expected_token == G_TOKEN_IDENTIFIER_NULL) + print_unexp = FALSE; + g_snprintf (token_string, token_string_len, + "%s%s `%s'", + print_unexp ? "" : "invalid ", + identifier_spec, + scanner->token == G_TOKEN_IDENTIFIER ? scanner->value.v_string : "null"); + break; + + case G_TOKEN_BINARY: + case G_TOKEN_OCTAL: + case G_TOKEN_INT: + case G_TOKEN_HEX: + if (scanner->config->store_int64) + g_snprintf (token_string, token_string_len, "number `%" G_GUINT64_FORMAT "'", scanner->value.v_int64); + else + g_snprintf (token_string, token_string_len, "number `%lu'", scanner->value.v_int); + break; + + case G_TOKEN_FLOAT: + g_snprintf (token_string, token_string_len, "number `%.3f'", scanner->value.v_float); + break; + + case G_TOKEN_STRING: + if (expected_token == G_TOKEN_STRING) + print_unexp = FALSE; + g_snprintf (token_string, token_string_len, + "%s%sstring constant \"%s\"", + print_unexp ? "" : "invalid ", + scanner->value.v_string[0] == 0 ? "empty " : "", + scanner->value.v_string); + token_string[token_string_len - 2] = '"'; + token_string[token_string_len - 1] = 0; + break; + + case G_TOKEN_COMMENT_SINGLE: + case G_TOKEN_COMMENT_MULTI: + g_snprintf (token_string, token_string_len, "comment"); + break; + + case G_TOKEN_NONE: + /* somehow the user's parsing code is screwed, there isn't much + * we can do about it. + * Note, a common case to trigger this is + * json_scanner_peek_next_token(); json_scanner_unexp_token(); + * without an intermediate json_scanner_get_next_token(). + */ + g_assert_not_reached (); + break; + } + + + switch (expected_token) + { + gboolean need_valid; + gchar *tstring; + case G_TOKEN_EOF: + g_snprintf (expected_string, expected_string_len, "end of file"); + break; + default: + if (expected_token >= 1 && expected_token <= 255) + { + if ((expected_token >= ' ' && expected_token <= '~') || + strchr (scanner->config->cset_identifier_first, expected_token) || + strchr (scanner->config->cset_identifier_nth, expected_token)) + g_snprintf (expected_string, expected_string_len, "character `%c'", expected_token); + else + g_snprintf (expected_string, expected_string_len, "character `\\%o'", expected_token); + break; + } + else if (!scanner->config->symbol_2_token) + { + g_snprintf (expected_string, expected_string_len, "(unknown) token <%d>", expected_token); + break; + } + /* fall through */ + case G_TOKEN_SYMBOL: + need_valid = (scanner->token == G_TOKEN_SYMBOL || + (scanner->config->symbol_2_token && + scanner->token > G_TOKEN_LAST)); + g_snprintf (expected_string, expected_string_len, + "%s%s", + need_valid ? "valid " : "", + symbol_spec); + /* FIXME: should we attempt to lookup the symbol_name for symbol_2_token? */ + break; + case G_TOKEN_CHAR: + g_snprintf (expected_string, expected_string_len, "%scharacter", + scanner->token == G_TOKEN_CHAR ? "valid " : ""); + break; + case G_TOKEN_BINARY: + tstring = "binary"; + g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); + break; + case G_TOKEN_OCTAL: + tstring = "octal"; + g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); + break; + case G_TOKEN_INT: + tstring = "integer"; + g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); + break; + case G_TOKEN_HEX: + tstring = "hexadecimal"; + g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); + break; + case G_TOKEN_FLOAT: + tstring = "float"; + g_snprintf (expected_string, expected_string_len, "%snumber (%s)", + scanner->token == expected_token ? "valid " : "", tstring); + break; + case G_TOKEN_STRING: + g_snprintf (expected_string, + expected_string_len, + "%sstring constant", + scanner->token == G_TOKEN_STRING ? "valid " : ""); + break; + case G_TOKEN_IDENTIFIER: + case G_TOKEN_IDENTIFIER_NULL: + need_valid = (scanner->token == G_TOKEN_IDENTIFIER_NULL || + scanner->token == G_TOKEN_IDENTIFIER); + g_snprintf (expected_string, + expected_string_len, + "%s%s", + need_valid ? "valid " : "", + identifier_spec); + break; + case G_TOKEN_COMMENT_SINGLE: + tstring = "single-line"; + g_snprintf (expected_string, expected_string_len, "%scomment (%s)", + scanner->token == expected_token ? "valid " : "", tstring); + break; + case G_TOKEN_COMMENT_MULTI: + tstring = "multi-line"; + g_snprintf (expected_string, expected_string_len, "%scomment (%s)", + scanner->token == expected_token ? "valid " : "", tstring); + break; + case G_TOKEN_NONE: + case G_TOKEN_ERROR: + /* this is handled upon printout */ + break; + } + + if (message && message[0] != 0) + message_prefix = " - "; + else + { + message_prefix = ""; + message = ""; + } + if (expected_token == G_TOKEN_ERROR) + { + msg_handler (scanner, + "failure around %s%s%s", + token_string, + message_prefix, + message); + } + else if (expected_token == G_TOKEN_NONE) + { + if (print_unexp) + msg_handler (scanner, + "unexpected %s%s%s", + token_string, + message_prefix, + message); + else + msg_handler (scanner, + "%s%s%s", + token_string, + message_prefix, + message); + } + else + { + if (print_unexp) + msg_handler (scanner, + "unexpected %s, expected %s%s%s", + token_string, + expected_string, + message_prefix, + message); + else + msg_handler (scanner, + "%s, expected %s%s%s", + token_string, + expected_string, + message_prefix, + message); + } + + g_free (token_string); + g_free (expected_string); +} + +static void +json_scanner_get_token_i (JsonScanner *scanner, + GTokenType *token_p, + GTokenValue *value_p, + guint *line_p, + guint *position_p) +{ + do + { + json_scanner_free_value (token_p, value_p); + json_scanner_get_token_ll (scanner, token_p, value_p, line_p, position_p); + } + while (((*token_p > 0 && *token_p < 256) && + strchr (scanner->config->cset_skip_characters, *token_p)) || + (*token_p == G_TOKEN_CHAR && + strchr (scanner->config->cset_skip_characters, value_p->v_char)) || + (*token_p == G_TOKEN_COMMENT_MULTI && + scanner->config->skip_comment_multi) || + (*token_p == G_TOKEN_COMMENT_SINGLE && + scanner->config->skip_comment_single)); + + switch (*token_p) + { + case G_TOKEN_IDENTIFIER: + if (scanner->config->identifier_2_string) + *token_p = G_TOKEN_STRING; + break; + + case G_TOKEN_SYMBOL: + if (scanner->config->symbol_2_token) + *token_p = (GTokenType) value_p->v_symbol; + break; + + case G_TOKEN_BINARY: + case G_TOKEN_OCTAL: + case G_TOKEN_HEX: + if (scanner->config->numbers_2_int) + *token_p = G_TOKEN_INT; + break; + + default: + break; + } + + if (*token_p == G_TOKEN_INT && + scanner->config->int_2_float) + { + *token_p = G_TOKEN_FLOAT; + if (scanner->config->store_int64) + { +#ifdef _MSC_VER + /* work around error C2520, see gvaluetransform.c */ + value_p->v_float = (__int64)value_p->v_int64; +#else + value_p->v_float = value_p->v_int64; +#endif + } + else + value_p->v_float = value_p->v_int; + } + + errno = 0; +} + +static void +json_scanner_get_token_ll (JsonScanner *scanner, + GTokenType *token_p, + GTokenValue *value_p, + guint *line_p, + guint *position_p) +{ + JsonScannerConfig *config; + GTokenType token; + gboolean in_comment_multi; + gboolean in_comment_single; + gboolean in_string_sq; + gboolean in_string_dq; + GString *gstring; + GTokenValue value; + guchar ch; + + config = scanner->config; + (*value_p).v_int64 = 0; + + if ((scanner->text >= scanner->text_end && scanner->input_fd < 0) || + scanner->token == G_TOKEN_EOF) + { + *token_p = G_TOKEN_EOF; + return; + } + + in_comment_multi = FALSE; + in_comment_single = FALSE; + in_string_sq = FALSE; + in_string_dq = FALSE; + gstring = NULL; + + do /* while (ch != 0) */ + { + gboolean dotted_float = FALSE; + + ch = json_scanner_get_char (scanner, line_p, position_p); + + value.v_int64 = 0; + token = G_TOKEN_NONE; + + /* this is *evil*, but needed ;( + * we first check for identifier first character, because it + * might interfere with other key chars like slashes or numbers + */ + if (config->scan_identifier && + ch && strchr (config->cset_identifier_first, ch)) + goto identifier_precedence; + + switch (ch) + { + case 0: + token = G_TOKEN_EOF; + (*position_p)++; + /* ch = 0; */ + break; + + case '/': + if (!config->scan_comment_multi || + json_scanner_peek_next_char (scanner) != '*') + goto default_case; + json_scanner_get_char (scanner, line_p, position_p); + token = G_TOKEN_COMMENT_MULTI; + in_comment_multi = TRUE; + gstring = g_string_new (NULL); + while ((ch = json_scanner_get_char (scanner, line_p, position_p)) != 0) + { + if (ch == '*' && json_scanner_peek_next_char (scanner) == '/') + { + json_scanner_get_char (scanner, line_p, position_p); + in_comment_multi = FALSE; + break; + } + else + gstring = g_string_append_c (gstring, ch); + } + ch = 0; + break; + + case '\'': + if (!config->scan_string_sq) + goto default_case; + token = G_TOKEN_STRING; + in_string_sq = TRUE; + gstring = g_string_new (NULL); + while ((ch = json_scanner_get_char (scanner, line_p, position_p)) != 0) + { + if (ch == '\'') + { + in_string_sq = FALSE; + break; + } + else + gstring = g_string_append_c (gstring, ch); + } + ch = 0; + break; + + case '"': + if (!config->scan_string_dq) + goto default_case; + token = G_TOKEN_STRING; + in_string_dq = TRUE; + gstring = g_string_new (NULL); + while ((ch = json_scanner_get_char (scanner, line_p, position_p)) != 0) + { + if (ch == '"') + { + in_string_dq = FALSE; + break; + } + else + { + if (ch == '\\') + { + ch = json_scanner_get_char (scanner, line_p, position_p); + switch (ch) + { + guint i; + guint fchar; + + case 0: + break; + + case '\\': + gstring = g_string_append_c (gstring, '\\'); + break; + + case 'n': + gstring = g_string_append_c (gstring, '\n'); + break; + + case 't': + gstring = g_string_append_c (gstring, '\t'); + break; + + case 'r': + gstring = g_string_append_c (gstring, '\r'); + break; + + case 'b': + gstring = g_string_append_c (gstring, '\b'); + break; + + case 'f': + gstring = g_string_append_c (gstring, '\f'); + break; + + case 'u': + fchar = json_scanner_peek_next_char (scanner); + if (is_hex_digit (fchar)) + { + gunichar ucs; + + ucs = json_scanner_get_unichar (scanner, line_p, position_p); + + if (g_unichar_type (ucs) == G_UNICODE_SURROGATE) + { + /* read next surrogate */ + if ('\\' == json_scanner_get_char (scanner, line_p, position_p) + && 'u' == json_scanner_get_char (scanner, line_p, position_p)) + { + gunichar ucs_lo = json_scanner_get_unichar (scanner, line_p, position_p); + g_assert (g_unichar_type (ucs_lo) == G_UNICODE_SURROGATE); + ucs = (((ucs & 0x3ff) << 10) | (ucs_lo & 0x3ff)) + 0x10000; + } + } + + g_assert (g_unichar_validate (ucs)); + gstring = g_string_append_unichar (gstring, ucs); + } + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + i = ch - '0'; + fchar = json_scanner_peek_next_char (scanner); + if (fchar >= '0' && fchar <= '7') + { + ch = json_scanner_get_char (scanner, line_p, position_p); + i = i * 8 + ch - '0'; + fchar = json_scanner_peek_next_char (scanner); + if (fchar >= '0' && fchar <= '7') + { + ch = json_scanner_get_char (scanner, line_p, position_p); + i = i * 8 + ch - '0'; + } + } + gstring = g_string_append_c (gstring, i); + break; + + default: + gstring = g_string_append_c (gstring, ch); + break; + } + } + else + gstring = g_string_append_c (gstring, ch); + } + } + ch = 0; + break; + + case '.': + if (!config->scan_float) + goto default_case; + token = G_TOKEN_FLOAT; + dotted_float = TRUE; + ch = json_scanner_get_char (scanner, line_p, position_p); + goto number_parsing; + + case '$': + if (!config->scan_hex_dollar) + goto default_case; + token = G_TOKEN_HEX; + ch = json_scanner_get_char (scanner, line_p, position_p); + goto number_parsing; + + case '0': + if (config->scan_octal) + token = G_TOKEN_OCTAL; + else + token = G_TOKEN_INT; + ch = json_scanner_peek_next_char (scanner); + if (config->scan_hex && (ch == 'x' || ch == 'X')) + { + token = G_TOKEN_HEX; + json_scanner_get_char (scanner, line_p, position_p); + ch = json_scanner_get_char (scanner, line_p, position_p); + if (ch == 0) + { + token = G_TOKEN_ERROR; + value.v_error = G_ERR_UNEXP_EOF; + (*position_p)++; + break; + } + if (json_scanner_char_2_num (ch, 16) < 0) + { + token = G_TOKEN_ERROR; + value.v_error = G_ERR_DIGIT_RADIX; + ch = 0; + break; + } + } + else if (config->scan_binary && (ch == 'b' || ch == 'B')) + { + token = G_TOKEN_BINARY; + json_scanner_get_char (scanner, line_p, position_p); + ch = json_scanner_get_char (scanner, line_p, position_p); + if (ch == 0) + { + token = G_TOKEN_ERROR; + value.v_error = G_ERR_UNEXP_EOF; + (*position_p)++; + break; + } + if (json_scanner_char_2_num (ch, 10) < 0) + { + token = G_TOKEN_ERROR; + value.v_error = G_ERR_NON_DIGIT_IN_CONST; + ch = 0; + break; + } + } + else + ch = '0'; + /* fall through */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + number_parsing: + { + gboolean in_number = TRUE; + gchar *endptr; + + if (token == G_TOKEN_NONE) + token = G_TOKEN_INT; + + gstring = g_string_new (dotted_float ? "0." : ""); + gstring = g_string_append_c (gstring, ch); + + do /* while (in_number) */ + { + gboolean is_E; + + is_E = token == G_TOKEN_FLOAT && (ch == 'e' || ch == 'E'); + + ch = json_scanner_peek_next_char (scanner); + + if (json_scanner_char_2_num (ch, 36) >= 0 || + (config->scan_float && ch == '.') || + (is_E && (ch == '+' || ch == '-'))) + { + ch = json_scanner_get_char (scanner, line_p, position_p); + + switch (ch) + { + case '.': + if (token != G_TOKEN_INT && token != G_TOKEN_OCTAL) + { + value.v_error = token == G_TOKEN_FLOAT ? G_ERR_FLOAT_MALFORMED : G_ERR_FLOAT_RADIX; + token = G_TOKEN_ERROR; + in_number = FALSE; + } + else + { + token = G_TOKEN_FLOAT; + gstring = g_string_append_c (gstring, ch); + } + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + gstring = g_string_append_c (gstring, ch); + break; + + case '-': + case '+': + if (token != G_TOKEN_FLOAT) + { + token = G_TOKEN_ERROR; + value.v_error = G_ERR_NON_DIGIT_IN_CONST; + in_number = FALSE; + } + else + gstring = g_string_append_c (gstring, ch); + break; + + case 'e': + case 'E': + if ((token != G_TOKEN_HEX && !config->scan_float) || + (token != G_TOKEN_HEX && + token != G_TOKEN_OCTAL && + token != G_TOKEN_FLOAT && + token != G_TOKEN_INT)) + { + token = G_TOKEN_ERROR; + value.v_error = G_ERR_NON_DIGIT_IN_CONST; + in_number = FALSE; + } + else + { + if (token != G_TOKEN_HEX) + token = G_TOKEN_FLOAT; + gstring = g_string_append_c (gstring, ch); + } + break; + + default: + if (token != G_TOKEN_HEX) + { + token = G_TOKEN_ERROR; + value.v_error = G_ERR_NON_DIGIT_IN_CONST; + in_number = FALSE; + } + else + gstring = g_string_append_c (gstring, ch); + break; + } + } + else + in_number = FALSE; + } + while (in_number); + + endptr = NULL; + if (token == G_TOKEN_FLOAT) + value.v_float = g_strtod (gstring->str, &endptr); + else + { + guint64 ui64 = 0; + switch (token) + { + case G_TOKEN_BINARY: + ui64 = g_ascii_strtoull (gstring->str, &endptr, 2); + break; + case G_TOKEN_OCTAL: + ui64 = g_ascii_strtoull (gstring->str, &endptr, 8); + break; + case G_TOKEN_INT: + ui64 = g_ascii_strtoull (gstring->str, &endptr, 10); + break; + case G_TOKEN_HEX: + ui64 = g_ascii_strtoull (gstring->str, &endptr, 16); + break; + default: ; + } + if (scanner->config->store_int64) + value.v_int64 = ui64; + else + value.v_int = ui64; + } + if (endptr && *endptr) + { + token = G_TOKEN_ERROR; + if (*endptr == 'e' || *endptr == 'E') + value.v_error = G_ERR_NON_DIGIT_IN_CONST; + else + value.v_error = G_ERR_DIGIT_RADIX; + } + g_string_free (gstring, TRUE); + gstring = NULL; + ch = 0; + } /* number_parsing:... */ + break; + + default: + default_case: + { + if (config->cpair_comment_single && + ch == config->cpair_comment_single[0]) + { + token = G_TOKEN_COMMENT_SINGLE; + in_comment_single = TRUE; + gstring = g_string_new (NULL); + ch = json_scanner_get_char (scanner, line_p, position_p); + while (ch != 0) + { + if (ch == config->cpair_comment_single[1]) + { + in_comment_single = FALSE; + ch = 0; + break; + } + + gstring = g_string_append_c (gstring, ch); + ch = json_scanner_get_char (scanner, line_p, position_p); + } + /* ignore a missing newline at EOF for single line comments */ + if (in_comment_single && + config->cpair_comment_single[1] == '\n') + in_comment_single = FALSE; + } + else if (config->scan_identifier && ch && + strchr (config->cset_identifier_first, ch)) + { + identifier_precedence: + + if (config->cset_identifier_nth && ch && + strchr (config->cset_identifier_nth, + json_scanner_peek_next_char (scanner))) + { + token = G_TOKEN_IDENTIFIER; + gstring = g_string_new (NULL); + gstring = g_string_append_c (gstring, ch); + do + { + ch = json_scanner_get_char (scanner, line_p, position_p); + gstring = g_string_append_c (gstring, ch); + ch = json_scanner_peek_next_char (scanner); + } + while (ch && strchr (config->cset_identifier_nth, ch)); + ch = 0; + } + else if (config->scan_identifier_1char) + { + token = G_TOKEN_IDENTIFIER; + value.v_identifier = g_new0 (gchar, 2); + value.v_identifier[0] = ch; + ch = 0; + } + } + if (ch) + { + if (config->char_2_token) + token = ch; + else + { + token = G_TOKEN_CHAR; + value.v_char = ch; + } + ch = 0; + } + } /* default_case:... */ + break; + } + g_assert (ch == 0 && token != G_TOKEN_NONE); /* paranoid */ + } + while (ch != 0); + + if (in_comment_multi || in_comment_single || + in_string_sq || in_string_dq) + { + token = G_TOKEN_ERROR; + if (gstring) + { + g_string_free (gstring, TRUE); + gstring = NULL; + } + (*position_p)++; + if (in_comment_multi || in_comment_single) + value.v_error = G_ERR_UNEXP_EOF_IN_COMMENT; + else /* (in_string_sq || in_string_dq) */ + value.v_error = G_ERR_UNEXP_EOF_IN_STRING; + } + + if (gstring) + { + value.v_string = g_string_free (gstring, FALSE); + gstring = NULL; + } + + if (token == G_TOKEN_IDENTIFIER) + { + if (config->scan_symbols) + { + JsonScannerKey *key; + guint scope_id; + + scope_id = scanner->scope_id; + key = json_scanner_lookup_internal (scanner, scope_id, value.v_identifier); + if (!key && scope_id && scanner->config->scope_0_fallback) + key = json_scanner_lookup_internal (scanner, 0, value.v_identifier); + + if (key) + { + g_free (value.v_identifier); + token = G_TOKEN_SYMBOL; + value.v_symbol = key->value; + } + } + + if (token == G_TOKEN_IDENTIFIER && + config->scan_identifier_NULL && + strlen (value.v_identifier) == 4) + { + gchar *null_upper = "NULL"; + gchar *null_lower = "null"; + + if (scanner->config->case_sensitive) + { + if (value.v_identifier[0] == null_upper[0] && + value.v_identifier[1] == null_upper[1] && + value.v_identifier[2] == null_upper[2] && + value.v_identifier[3] == null_upper[3]) + token = G_TOKEN_IDENTIFIER_NULL; + } + else + { + if ((value.v_identifier[0] == null_upper[0] || + value.v_identifier[0] == null_lower[0]) && + (value.v_identifier[1] == null_upper[1] || + value.v_identifier[1] == null_lower[1]) && + (value.v_identifier[2] == null_upper[2] || + value.v_identifier[2] == null_lower[2]) && + (value.v_identifier[3] == null_upper[3] || + value.v_identifier[3] == null_lower[3])) + token = G_TOKEN_IDENTIFIER_NULL; + } + } + } + + *token_p = token; + *value_p = value; +} diff --git a/json-glib/json-glib/json-scanner.h b/json-glib/json-glib/json-scanner.h new file mode 100644 index 0000000..e3df02c --- /dev/null +++ b/json-glib/json-glib/json-scanner.h @@ -0,0 +1,169 @@ +/* json-scanner.h: Tokenizer for JSON + * + * This file is part of JSON-GLib + * Copyright (C) 2008 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * JsonScanner is a specialized tokenizer for JSON adapted from + * the GScanner tokenizer in GLib; GScanner came with this notice: + * + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + * + * JsonScanner: modified by Emmanuele Bassi + */ + +#ifndef __JSON_SCANNER_H__ +#define __JSON_SCANNER_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _JsonScanner JsonScanner; +typedef struct _JsonScannerConfig JsonScannerConfig; + +typedef void (* JsonScannerMsgFunc) (JsonScanner *scanner, + gchar *message, + gboolean is_error); + +/** + * JsonTokenType: + * @JSON_TOKEN_INVALID: marker + * @JSON_TOKEN_TRUE: symbol for 'true' bareword + * @JSON_TOKEN_FALSE: symbol for 'false' bareword + * @JSON_TOKEN_NULL: symbol for 'null' bareword + * @JSON_TOKEN_VAR: symbol for 'var' bareword + * @JSON_TOKEN_LAST: marker + * + * Tokens for JsonScanner-based parser, extending #GTokenType. + */ +typedef enum { + JSON_TOKEN_INVALID = G_TOKEN_LAST, + + JSON_TOKEN_TRUE, + JSON_TOKEN_FALSE, + JSON_TOKEN_NULL, + JSON_TOKEN_VAR, + + JSON_TOKEN_LAST +} JsonTokenType; + +/** + * JsonScanner: + * + * Tokenizer scanner for JSON. See #GScanner + * + * Since: 0.6 + */ +struct _JsonScanner +{ + /*< private >*/ + /* unused fields */ + gpointer user_data; + guint max_parse_errors; + + /* json_scanner_error() increments this field */ + guint parse_errors; + + /* name of input stream, featured by the default message handler */ + const gchar *input_name; + + /* quarked data */ + GData *qdata; + + /* link into the scanner configuration */ + JsonScannerConfig *config; + + /* fields filled in after json_scanner_get_next_token() */ + GTokenType token; + GTokenValue value; + guint line; + guint position; + + /* fields filled in after json_scanner_peek_next_token() */ + GTokenType next_token; + GTokenValue next_value; + guint next_line; + guint next_position; + + /* to be considered private */ + GHashTable *symbol_table; + gint input_fd; + const gchar *text; + const gchar *text_end; + gchar *buffer; + guint scope_id; + + /* handler function for _warn and _error */ + JsonScannerMsgFunc msg_handler; +}; + +JsonScanner *json_scanner_new (void); +void json_scanner_destroy (JsonScanner *scanner); +void json_scanner_input_file (JsonScanner *scanner, + gint input_fd); +void json_scanner_sync_file_offset (JsonScanner *scanner); +void json_scanner_input_text (JsonScanner *scanner, + const gchar *text, + guint text_len); +GTokenType json_scanner_get_next_token (JsonScanner *scanner); +GTokenType json_scanner_peek_next_token (JsonScanner *scanner); +GTokenType json_scanner_cur_token (JsonScanner *scanner); +GTokenValue json_scanner_cur_value (JsonScanner *scanner); +guint json_scanner_cur_line (JsonScanner *scanner); +guint json_scanner_cur_position (JsonScanner *scanner); +gboolean json_scanner_eof (JsonScanner *scanner); +guint json_scanner_set_scope (JsonScanner *scanner, + guint scope_id); +void json_scanner_scope_add_symbol (JsonScanner *scanner, + guint scope_id, + const gchar *symbol, + gpointer value); +void json_scanner_scope_remove_symbol (JsonScanner *scanner, + guint scope_id, + const gchar *symbol); +gpointer json_scanner_scope_lookup_symbol (JsonScanner *scanner, + guint scope_id, + const gchar *symbol); +void json_scanner_scope_foreach_symbol (JsonScanner *scanner, + guint scope_id, + GHFunc func, + gpointer user_data); +gpointer json_scanner_lookup_symbol (JsonScanner *scanner, + const gchar *symbol); +void json_scanner_unexp_token (JsonScanner *scanner, + GTokenType expected_token, + const gchar *identifier_spec, + const gchar *symbol_spec, + const gchar *symbol_name, + const gchar *message, + gint is_error); +void json_scanner_error (JsonScanner *scanner, + const gchar *format, + ...) G_GNUC_PRINTF (2,3); +void json_scanner_warn (JsonScanner *scanner, + const gchar *format, + ...) G_GNUC_PRINTF (2,3); + +G_END_DECLS + +#endif /* __JSON_SCANNER_H__ */ diff --git a/json-glib/json-glib/json-serializable.c b/json-glib/json-glib/json-serializable.c new file mode 100644 index 0000000..e49ccb1 --- /dev/null +++ b/json-glib/json-glib/json-serializable.c @@ -0,0 +1,341 @@ +/* json-gobject.c - JSON GObject integration + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Author: + * Emmanuele Bassi + */ + +/** + * SECTION:json-serializable + * @short_description: Interface for serialize and deserialize special GObjects + * + * #JsonSerializable is an interface for #GObject classes that + * allows controlling how the class is going to be serialized + * or deserialized by json_construct_gobject() and + * json_serialize_gobject() respectively. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "json-types-private.h" +#include "json-gobject-private.h" +#include "json-debug.h" + +/** + * json_serializable_serialize_property: + * @serializable: a #JsonSerializable object + * @property_name: the name of the property + * @value: the value of the property + * @pspec: a #GParamSpec + * + * Asks a #JsonSerializable implementation to serialize a #GObject + * property into a #JsonNode object. + * + * Return value: a #JsonNode containing the serialized property + */ +JsonNode * +json_serializable_serialize_property (JsonSerializable *serializable, + const gchar *property_name, + const GValue *value, + GParamSpec *pspec) +{ + JsonSerializableIface *iface; + + g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), NULL); + g_return_val_if_fail (property_name != NULL, NULL); + g_return_val_if_fail (value != NULL, NULL); + g_return_val_if_fail (pspec != NULL, NULL); + + iface = JSON_SERIALIZABLE_GET_IFACE (serializable); + + return iface->serialize_property (serializable, property_name, value, pspec); +} + +/** + * json_serializable_deserialize_property: + * @serializable: a #JsonSerializable + * @property_name: the name of the property + * @value: (out): a pointer to an uninitialized #GValue + * @pspec: a #GParamSpec + * @property_node: a #JsonNode containing the serialized property + * + * Asks a #JsonSerializable implementation to deserialize the + * property contained inside @property_node into @value. + * + * Return value: %TRUE if the property was successfully deserialized. + */ +gboolean +json_serializable_deserialize_property (JsonSerializable *serializable, + const gchar *property_name, + GValue *value, + GParamSpec *pspec, + JsonNode *property_node) +{ + JsonSerializableIface *iface; + + g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), FALSE); + g_return_val_if_fail (property_name != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (pspec != NULL, FALSE); + g_return_val_if_fail (property_node != NULL, FALSE); + + iface = JSON_SERIALIZABLE_GET_IFACE (serializable); + + return iface->deserialize_property (serializable, + property_name, + value, + pspec, + property_node); +} + +static gboolean +json_serializable_real_deserialize (JsonSerializable *serializable, + const gchar *name, + GValue *value, + GParamSpec *pspec, + JsonNode *node) +{ + JSON_NOTE (GOBJECT, "Default deserialization for property '%s'", pspec->name); + return json_deserialize_pspec (value, pspec, node); +} + +static JsonNode * +json_serializable_real_serialize (JsonSerializable *serializable, + const gchar *name, + const GValue *value, + GParamSpec *pspec) +{ + JSON_NOTE (GOBJECT, "Default serialization for property '%s'", pspec->name); + + if (g_param_value_defaults (pspec, (GValue *)value)) + return NULL; + + return json_serialize_pspec (value, pspec); +} + +static GParamSpec * +json_serializable_real_find_property (JsonSerializable *serializable, + const char *name) +{ + return g_object_class_find_property (G_OBJECT_GET_CLASS (serializable), name); +} + +static GParamSpec ** +json_serializable_real_list_properties (JsonSerializable *serializable, + guint *n_pspecs) +{ + return g_object_class_list_properties (G_OBJECT_GET_CLASS (serializable), n_pspecs); +} + +static void +json_serializable_real_set_property (JsonSerializable *serializable, + GParamSpec *pspec, + const GValue *value) +{ + g_object_set_property (G_OBJECT (serializable), pspec->name, value); +} + +static void +json_serializable_real_get_property (JsonSerializable *serializable, + GParamSpec *pspec, + GValue *value) +{ + g_object_get_property (G_OBJECT (serializable), pspec->name, value); +} + +/* typedef to satisfy G_DEFINE_INTERFACE's naming */ +typedef JsonSerializableIface JsonSerializableInterface; + +static void +json_serializable_default_init (JsonSerializableInterface *iface) +{ + iface->serialize_property = json_serializable_real_serialize; + iface->deserialize_property = json_serializable_real_deserialize; + iface->find_property = json_serializable_real_find_property; + iface->list_properties = json_serializable_real_list_properties; + iface->set_property = json_serializable_real_set_property; + iface->get_property = json_serializable_real_get_property; +} + +G_DEFINE_INTERFACE (JsonSerializable, json_serializable, G_TYPE_OBJECT); + +/** + * json_serializable_default_serialize_property: + * @serializable: a #JsonSerializable object + * @property_name: the name of the property + * @value: the value of the property + * @pspec: a #GParamSpec + * + * Calls the default implementation of the #JsonSerializable + * serialize_property() virtual function + * + * This function can be used inside a custom implementation + * of the serialize_property() virtual function in lieu of: + * + * |[ + * JsonSerializable *iface; + * JsonNode *node; + * + * iface = g_type_default_interface_peek (JSON_TYPE_SERIALIZABLE); + * node = iface->serialize_property (serializable, property_name, + * value, + * pspec); + * ]| + * + * Return value: (transfer full): a #JsonNode containing the serialized + * property + * + * Since: 0.10 + */ +JsonNode * +json_serializable_default_serialize_property (JsonSerializable *serializable, + const gchar *property_name, + const GValue *value, + GParamSpec *pspec) +{ + g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), NULL); + g_return_val_if_fail (property_name != NULL, NULL); + g_return_val_if_fail (value != NULL, NULL); + g_return_val_if_fail (pspec != NULL, NULL); + + return json_serializable_real_serialize (serializable, + property_name, + value, pspec); +} + +/** + * json_serializable_default_deserialize_property: + * @serializable: a #JsonSerializable + * @property_name: the name of the property + * @value: a pointer to an uninitialized #GValue + * @pspec: a #GParamSpec + * @property_node: a #JsonNode containing the serialized property + * + * Calls the default implementation of the #JsonSerializable + * deserialize_property() virtual function + * + * This function can be used inside a custom implementation + * of the deserialize_property() virtual function in lieu of: + * + * |[ + * JsonSerializable *iface; + * gboolean res; + * + * iface = g_type_default_interface_peek (JSON_TYPE_SERIALIZABLE); + * res = iface->deserialize_property (serializable, property_name, + * value, + * pspec, + * property_node); + * ]| + * + * Return value: %TRUE if the property was successfully deserialized. + * + * Since: 0.10 + */ +gboolean +json_serializable_default_deserialize_property (JsonSerializable *serializable, + const gchar *property_name, + GValue *value, + GParamSpec *pspec, + JsonNode *property_node) +{ + g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), FALSE); + g_return_val_if_fail (property_name != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (pspec != NULL, FALSE); + g_return_val_if_fail (property_node != NULL, FALSE); + + return json_serializable_real_deserialize (serializable, + property_name, + value, pspec, + property_node); +} + +/** + * json_serializable_find_property: + * @serializable: a #JsonSerializable + * @name: the name of the property + * + * FIXME + * + * Return value: (transfer none): the #GParamSpec for the property + * or %NULL if no property was found + * + * Since: 0.14 + */ +GParamSpec * +json_serializable_find_property (JsonSerializable *serializable, + const char *name) +{ + g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return JSON_SERIALIZABLE_GET_IFACE (serializable)->find_property (serializable, name); +} + +/** + * json_serializable_list_properties: + * @serializable: a #JsonSerializable + * @n_pspecs: (out): return location for the length of the array + * of #GParamSpec returned by the function + * + * FIXME + * + * Return value: (array length=n_pspecs) (transfer container): an array + * of #GParamSpec. Use g_free() to free the array when done. + * + * Since: 0.14 + */ +GParamSpec ** +json_serializable_list_properties (JsonSerializable *serializable, + guint *n_pspecs) +{ + g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), NULL); + + return JSON_SERIALIZABLE_GET_IFACE (serializable)->list_properties (serializable, n_pspecs); +} + +void +json_serializable_set_property (JsonSerializable *serializable, + GParamSpec *pspec, + const GValue *value) +{ + g_return_if_fail (JSON_IS_SERIALIZABLE (serializable)); + g_return_if_fail (G_IS_PARAM_SPEC (pspec)); + g_return_if_fail (value != NULL); + + JSON_SERIALIZABLE_GET_IFACE (serializable)->set_property (serializable, + pspec, + value); +} + +void +json_serializable_get_property (JsonSerializable *serializable, + GParamSpec *pspec, + GValue *value) +{ + g_return_if_fail (JSON_IS_SERIALIZABLE (serializable)); + g_return_if_fail (G_IS_PARAM_SPEC (pspec)); + g_return_if_fail (value != NULL); + + JSON_SERIALIZABLE_GET_IFACE (serializable)->get_property (serializable, + pspec, + value); +} diff --git a/json-glib/json-glib/json-types-private.h b/json-glib/json-glib/json-types-private.h new file mode 100644 index 0000000..e7f73f2 --- /dev/null +++ b/json-glib/json-glib/json-types-private.h @@ -0,0 +1,66 @@ +/* json-types-private.h - JSON data types private header + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#ifndef __JSON_TYPES_PRIVATE_H__ +#define __JSON_TYPES_PRIVATE_H__ + +#include "json-types.h" + +G_BEGIN_DECLS + +struct _JsonNode +{ + /*< private >*/ + JsonNodeType type; + + union { + JsonObject *object; + JsonArray *array; + GValue value; + } data; + + JsonNode *parent; +}; + +struct _JsonArray +{ + GPtrArray *elements; + + volatile gint ref_count; +}; + +struct _JsonObject +{ + GHashTable *members; + + /* the members of the object, ordered in reverse */ + GList *members_ordered; + + volatile gint ref_count; +}; + +const gchar *json_node_type_get_name (JsonNodeType node_type); + +G_END_DECLS + +#endif /* __JSON_TYPES_PRIVATE_H__ */ diff --git a/json-glib/json-glib/json-types.h b/json-glib/json-glib/json-types.h new file mode 100644 index 0000000..ecbf198 --- /dev/null +++ b/json-glib/json-glib/json-types.h @@ -0,0 +1,334 @@ +/* json-types.h - JSON data types + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_TYPES_H__ +#define __JSON_TYPES_H__ + +#include + +G_BEGIN_DECLS + +/** + * JSON_NODE_TYPE: + * @node: a #JsonNode + * + * Evaluates to the #JsonNodeType contained by @node + */ +#define JSON_NODE_TYPE(node) (json_node_get_node_type ((node))) + +/** + * JSON_NODE_HOLDS: + * @node: a #JsonNode + * @t: a #JsonNodeType + * + * Evaluates to %TRUE if the @node holds type @t + * + * Since: 0.10 + */ +#define JSON_NODE_HOLDS(node,t) (json_node_get_node_type ((node)) == (t)) + +/** + * JSON_NODE_HOLDS_VALUE: + * @node: a #JsonNode + * + * Evaluates to %TRUE if @node holds a %JSON_NODE_VALUE + * + * Since: 0.10 + */ +#define JSON_NODE_HOLDS_VALUE(node) (JSON_NODE_HOLDS ((node), JSON_NODE_VALUE)) + +/** + * JSON_NODE_HOLDS_OBJECT: + * @node: a #JsonNode + * + * Evaluates to %TRUE if @node holds a %JSON_NODE_OBJECT + * + * Since: 0.10 + */ +#define JSON_NODE_HOLDS_OBJECT(node) (JSON_NODE_HOLDS ((node), JSON_NODE_OBJECT)) + +/** + * JSON_NODE_HOLDS_ARRAY: + * @node: a #JsonNode + * + * Evaluates to %TRUE if @node holds a %JSON_NODE_ARRAY + * + * Since: 0.10 + */ +#define JSON_NODE_HOLDS_ARRAY(node) (JSON_NODE_HOLDS ((node), JSON_NODE_ARRAY)) + +/** + * JSON_NODE_HOLDS_NULL: + * @node: a #JsonNode + * + * Evaluates to %TRUE if @node holds a %JSON_NODE_NULL + * + * Since: 0.10 + */ +#define JSON_NODE_HOLDS_NULL(node) (JSON_NODE_HOLDS ((node), JSON_NODE_NULL)) + +#define JSON_TYPE_NODE (json_node_get_type ()) +#define JSON_TYPE_OBJECT (json_object_get_type ()) +#define JSON_TYPE_ARRAY (json_array_get_type ()) + +/** + * JsonNode: + * + * A generic container of JSON data types. The contents of the #JsonNode + * structure are private and should only be accessed via the provided + * functions and never directly. + */ +typedef struct _JsonNode JsonNode; + +/** + * JsonObject: + * + * A JSON object type. The contents of the #JsonObject structure are private + * and should only be accessed by the provided API + */ +typedef struct _JsonObject JsonObject; + +/** + * JsonArray: + * + * A JSON array type. The contents of the #JsonArray structure are private + * and should only be accessed by the provided API + */ +typedef struct _JsonArray JsonArray; + +/** + * JsonNodeType: + * @JSON_NODE_OBJECT: The node contains a #JsonObject + * @JSON_NODE_ARRAY: The node contains a #JsonArray + * @JSON_NODE_VALUE: The node contains a fundamental type + * @JSON_NODE_NULL: Special type, for nodes containing null + * + * Indicates the content of a #JsonNode. + */ +typedef enum { + JSON_NODE_OBJECT, + JSON_NODE_ARRAY, + JSON_NODE_VALUE, + JSON_NODE_NULL +} JsonNodeType; + +/** + * JsonObjectForeach: + * @object: the iterated #JsonObject + * @member_name: the name of the member + * @member_node: a #JsonNode containing the @member_name value + * @user_data: data passed to the function + * + * The function to be passed to json_object_foreach_member(). You + * should not add or remove members to and from @object within + * this function. It is safe to change the value of @member_node. + * + * Since: 0.8 + */ +typedef void (* JsonObjectForeach) (JsonObject *object, + const gchar *member_name, + JsonNode *member_node, + gpointer user_data); + +/** + * JsonArrayForeach: + * @array: the iterated #JsonArray + * @index_: the index of the element + * @element_node: a #JsonNode containing the value at @index_ + * @user_data: data passed to the function + * + * The function to be passed to json_array_foreach_element(). You + * should not add or remove elements to and from @array within + * this function. It is safe to change the value of @element_node. + * + * Since: 0.8 + */ +typedef void (* JsonArrayForeach) (JsonArray *array, + guint index_, + JsonNode *element_node, + gpointer user_data); + +/* + * JsonNode + */ +GType json_node_get_type (void) G_GNUC_CONST; +JsonNode * json_node_new (JsonNodeType type); +JsonNode * json_node_copy (JsonNode *node); +void json_node_free (JsonNode *node); +JsonNodeType json_node_get_node_type (JsonNode *node); +GType json_node_get_value_type (JsonNode *node); +void json_node_set_parent (JsonNode *node, + JsonNode *parent); +JsonNode * json_node_get_parent (JsonNode *node); +const gchar * json_node_type_name (JsonNode *node); + +void json_node_set_object (JsonNode *node, + JsonObject *object); +void json_node_take_object (JsonNode *node, + JsonObject *object); +JsonObject * json_node_get_object (JsonNode *node); +JsonObject * json_node_dup_object (JsonNode *node); +void json_node_set_array (JsonNode *node, + JsonArray *array); +void json_node_take_array (JsonNode *node, + JsonArray *array); +JsonArray * json_node_get_array (JsonNode *node); +JsonArray * json_node_dup_array (JsonNode *node); +void json_node_set_value (JsonNode *node, + const GValue *value); +void json_node_get_value (JsonNode *node, + GValue *value); +void json_node_set_string (JsonNode *node, + const gchar *value); +const gchar * json_node_get_string (JsonNode *node); +gchar * json_node_dup_string (JsonNode *node); +void json_node_set_int (JsonNode *node, + gint64 value); +gint64 json_node_get_int (JsonNode *node); +void json_node_set_double (JsonNode *node, + gdouble value); +gdouble json_node_get_double (JsonNode *node); +void json_node_set_boolean (JsonNode *node, + gboolean value); +gboolean json_node_get_boolean (JsonNode *node); +gboolean json_node_is_null (JsonNode *node); + +/* + * JsonObject + */ +GType json_object_get_type (void) G_GNUC_CONST; +JsonObject * json_object_new (void); +JsonObject * json_object_ref (JsonObject *object); +void json_object_unref (JsonObject *object); + +#ifndef JSON_DISABLE_DEPRECATED +void json_object_add_member (JsonObject *object, + const gchar *member_name, + JsonNode *node) G_GNUC_DEPRECATED; +#endif /* JSON_DISABLE_DEPRECATED */ + +void json_object_set_member (JsonObject *object, + const gchar *member_name, + JsonNode *node); +void json_object_set_int_member (JsonObject *object, + const gchar *member_name, + gint64 value); +void json_object_set_double_member (JsonObject *object, + const gchar *member_name, + gdouble value); +void json_object_set_boolean_member (JsonObject *object, + const gchar *member_name, + gboolean value); +void json_object_set_string_member (JsonObject *object, + const gchar *member_name, + const gchar *value); +void json_object_set_null_member (JsonObject *object, + const gchar *member_name); +void json_object_set_array_member (JsonObject *object, + const gchar *member_name, + JsonArray *value); +void json_object_set_object_member (JsonObject *object, + const gchar *member_name, + JsonObject *value); +GList * json_object_get_members (JsonObject *object); +JsonNode * json_object_get_member (JsonObject *object, + const gchar *member_name); +JsonNode * json_object_dup_member (JsonObject *object, + const gchar *member_name); +gint64 json_object_get_int_member (JsonObject *object, + const gchar *member_name); +gdouble json_object_get_double_member (JsonObject *object, + const gchar *member_name); +gboolean json_object_get_boolean_member (JsonObject *object, + const gchar *member_name); +const gchar * json_object_get_string_member (JsonObject *object, + const gchar *member_name); +gboolean json_object_get_null_member (JsonObject *object, + const gchar *member_name); +JsonArray * json_object_get_array_member (JsonObject *object, + const gchar *member_name); +JsonObject * json_object_get_object_member (JsonObject *object, + const gchar *member_name); +gboolean json_object_has_member (JsonObject *object, + const gchar *member_name); +void json_object_remove_member (JsonObject *object, + const gchar *member_name); +GList * json_object_get_values (JsonObject *object); +guint json_object_get_size (JsonObject *object); +void json_object_foreach_member (JsonObject *object, + JsonObjectForeach func, + gpointer data); + +GType json_array_get_type (void) G_GNUC_CONST; +JsonArray * json_array_new (void); +JsonArray * json_array_sized_new (guint n_elements); +JsonArray * json_array_ref (JsonArray *array); +void json_array_unref (JsonArray *array); +void json_array_add_element (JsonArray *array, + JsonNode *node); +void json_array_add_int_element (JsonArray *array, + gint64 value); +void json_array_add_double_element (JsonArray *array, + gdouble value); +void json_array_add_boolean_element (JsonArray *array, + gboolean value); +void json_array_add_string_element (JsonArray *array, + const gchar *value); +void json_array_add_null_element (JsonArray *array); +void json_array_add_array_element (JsonArray *array, + JsonArray *value); +void json_array_add_object_element (JsonArray *array, + JsonObject *value); +GList * json_array_get_elements (JsonArray *array); +JsonNode * json_array_get_element (JsonArray *array, + guint index_); +gint64 json_array_get_int_element (JsonArray *array, + guint index_); +gdouble json_array_get_double_element (JsonArray *array, + guint index_); +gboolean json_array_get_boolean_element (JsonArray *array, + guint index_); +const gchar * json_array_get_string_element (JsonArray *array, + guint index_); +gboolean json_array_get_null_element (JsonArray *array, + guint index_); +JsonArray * json_array_get_array_element (JsonArray *array, + guint index_); +JsonObject * json_array_get_object_element (JsonArray *array, + guint index_); +JsonNode * json_array_dup_element (JsonArray *array, + guint index_); +void json_array_remove_element (JsonArray *array, + guint index_); +guint json_array_get_length (JsonArray *array); +void json_array_foreach_element (JsonArray *array, + JsonArrayForeach func, + gpointer data); + +G_END_DECLS + +#endif /* __JSON_TYPES_H__ */ diff --git a/json-glib/json-glib/json-version.h b/json-glib/json-glib/json-version.h new file mode 100644 index 0000000..49d1853 --- /dev/null +++ b/json-glib/json-glib/json-version.h @@ -0,0 +1,100 @@ +/* json-version.h - JSON-GLib versioning information + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_VERSION_H__ +#define __JSON_VERSION_H__ + +/** + * SECTION:json-version + * @short_description: JSON-GLib version checking + * + * JSON-GLib provides macros to check the version of the library + * at compile-time + */ + +/** + * JSON_MAJOR_VERSION: + * + * Json major version component (e.g. 1 if %JSON_VERSION is 1.2.3) + */ +#define JSON_MAJOR_VERSION (0) + +/** + * JSON_MINOR_VERSION: + * + * Json minor version component (e.g. 2 if %JSON_VERSION is 1.2.3) + */ +#define JSON_MINOR_VERSION (14) + +/** + * JSON_MICRO_VERSION: + * + * Json micro version component (e.g. 3 if %JSON_VERSION is 1.2.3) + */ +#define JSON_MICRO_VERSION (2) + +/** + * JSON_VERSION + * + * Json version. + */ +#define JSON_VERSION (0.14.2) + +/** + * JSON_VERSION_S: + * + * Json version, encoded as a string, useful for printing and + * concatenation. + */ +#define JSON_VERSION_S "0.14.2" + +/** + * JSON_VERSION_HEX: + * + * Json version, encoded as an hexadecimal number, useful for + * integer comparisons. + */ +#define JSON_VERSION_HEX (JSON_MAJOR_VERSION << 24 | \ + JSON_MINOR_VERSION << 16 | \ + JSON_MICRO_VERSION << 8) + +/** + * JSON_CHECK_VERSION: + * @major: required major version + * @minor: required minor version + * @micro: required micro version + * + * Compile-time version checking. Evaluates to %TRUE if the version + * of Json is greater than the required one. + */ +#define JSON_CHECK_VERSION(major,minor,micro) \ + (JSON_MAJOR_VERSION > (major) || \ + (JSON_MAJOR_VERSION == (major) && JSON_MINOR_VERSION > (minor)) || \ + (JSON_MAJOR_VERSION == (major) && JSON_MINOR_VERSION == (minor) && \ + JSON_MICRO_VERSION >= (micro))) + +#endif /* __JSON_VERSION_H__ */ diff --git a/json-glib/json-glib/json-version.h.in b/json-glib/json-glib/json-version.h.in new file mode 100644 index 0000000..bc05f1b --- /dev/null +++ b/json-glib/json-glib/json-version.h.in @@ -0,0 +1,100 @@ +/* json-version.h - JSON-GLib versioning information + * + * This file is part of JSON-GLib + * Copyright (C) 2007 OpenedHand Ltd. + * Copyright (C) 2009 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Emmanuele Bassi + */ + +#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __JSON_VERSION_H__ +#define __JSON_VERSION_H__ + +/** + * SECTION:json-version + * @short_description: JSON-GLib version checking + * + * JSON-GLib provides macros to check the version of the library + * at compile-time + */ + +/** + * JSON_MAJOR_VERSION: + * + * Json major version component (e.g. 1 if %JSON_VERSION is 1.2.3) + */ +#define JSON_MAJOR_VERSION (@JSON_MAJOR_VERSION@) + +/** + * JSON_MINOR_VERSION: + * + * Json minor version component (e.g. 2 if %JSON_VERSION is 1.2.3) + */ +#define JSON_MINOR_VERSION (@JSON_MINOR_VERSION@) + +/** + * JSON_MICRO_VERSION: + * + * Json micro version component (e.g. 3 if %JSON_VERSION is 1.2.3) + */ +#define JSON_MICRO_VERSION (@JSON_MICRO_VERSION@) + +/** + * JSON_VERSION + * + * Json version. + */ +#define JSON_VERSION (@JSON_VERSION@) + +/** + * JSON_VERSION_S: + * + * Json version, encoded as a string, useful for printing and + * concatenation. + */ +#define JSON_VERSION_S "@JSON_VERSION@" + +/** + * JSON_VERSION_HEX: + * + * Json version, encoded as an hexadecimal number, useful for + * integer comparisons. + */ +#define JSON_VERSION_HEX (JSON_MAJOR_VERSION << 24 | \ + JSON_MINOR_VERSION << 16 | \ + JSON_MICRO_VERSION << 8) + +/** + * JSON_CHECK_VERSION: + * @major: required major version + * @minor: required minor version + * @micro: required micro version + * + * Compile-time version checking. Evaluates to %TRUE if the version + * of Json is greater than the required one. + */ +#define JSON_CHECK_VERSION(major,minor,micro) \ + (JSON_MAJOR_VERSION > (major) || \ + (JSON_MAJOR_VERSION == (major) && JSON_MINOR_VERSION > (minor)) || \ + (JSON_MAJOR_VERSION == (major) && JSON_MINOR_VERSION == (minor) && \ + JSON_MICRO_VERSION >= (micro))) + +#endif /* __JSON_VERSION_H__ */ diff --git a/json-glib/json-glib/tests/Makefile.am b/json-glib/json-glib/tests/Makefile.am new file mode 100644 index 0000000..5d5f784 --- /dev/null +++ b/json-glib/json-glib/tests/Makefile.am @@ -0,0 +1,39 @@ +include $(top_srcdir)/build/autotools/Makefile.am.gtest +include $(top_srcdir)/build/autotools/Makefile.am.silent + +NULL = + +DISTCLEANFILES = + +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/json-glib \ + $(NULL) + +AM_CPPFLAGS = $(JSON_DEBUG_CFLAGS) -DTESTS_DATA_DIR=\""$(top_srcdir)/json-glib/tests"\" +AM_CFLAGS = -g $(JSON_CFLAGS) $(MAINTAINER_CFLAGS) +LDADD = \ + ../libjson-glib.la \ + $(JSON_LIBS) \ + $(NULL) + +EXTRA_DIST += stream-load.json + +noinst_PROGRAMS = $(TEST_PROGS) +TEST_PROGS += \ + array \ + boxed \ + builder \ + generator \ + gvariant \ + node \ + object \ + parser \ + path \ + reader \ + serialize-simple \ + serialize-complex \ + serialize-full \ + $(NULL) + +-include $(top_srcdir)/build/autotools/Makefile.am.gitignore diff --git a/json-glib/json-glib/tests/array.c b/json-glib/json-glib/tests/array.c new file mode 100644 index 0000000..ce8242b --- /dev/null +++ b/json-glib/json-glib/tests/array.c @@ -0,0 +1,122 @@ +#include +#include +#include + +#include +#include + +static void +test_empty_array (void) +{ + JsonArray *array = json_array_new (); + + g_assert_cmpint (json_array_get_length (array), ==, 0); + g_assert (json_array_get_elements (array) == NULL); + + json_array_unref (array); +} + +static void +test_add_element (void) +{ + JsonArray *array = json_array_new (); + JsonNode *node = json_node_new (JSON_NODE_NULL); + + g_assert_cmpint (json_array_get_length (array), ==, 0); + + json_array_add_element (array, node); + g_assert_cmpint (json_array_get_length (array), ==, 1); + + node = json_array_get_element (array, 0); + g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_NULL); + + json_array_unref (array); +} + +static void +test_remove_element (void) +{ + JsonArray *array = json_array_new (); + JsonNode *node = json_node_new (JSON_NODE_NULL); + + json_array_add_element (array, node); + + json_array_remove_element (array, 0); + g_assert_cmpint (json_array_get_length (array), ==, 0); + + json_array_unref (array); +} + +typedef struct _TestForeachFixture +{ + GList *elements; + gint n_elements; + gint iterations; +} TestForeachFixture; + +static const struct { + JsonNodeType element_type; + GType element_gtype; +} type_verify[] = { + { JSON_NODE_VALUE, G_TYPE_INT64 }, + { JSON_NODE_VALUE, G_TYPE_BOOLEAN }, + { JSON_NODE_VALUE, G_TYPE_STRING }, + { JSON_NODE_NULL, G_TYPE_INVALID } +}; + +static void +verify_foreach (JsonArray *array, + guint index_, + JsonNode *element_node, + gpointer user_data) +{ + TestForeachFixture *fixture = user_data; + + g_assert (g_list_find (fixture->elements, element_node)); + g_assert (json_node_get_node_type (element_node) == type_verify[index_].element_type); + g_assert (json_node_get_value_type (element_node) == type_verify[index_].element_gtype); + + fixture->iterations += 1; +} + +static void +test_foreach_element (void) +{ + JsonArray *array = json_array_new (); + TestForeachFixture fixture = { 0, }; + + json_array_add_int_element (array, 42); + json_array_add_boolean_element (array, TRUE); + json_array_add_string_element (array, "hello"); + json_array_add_null_element (array); + + fixture.elements = json_array_get_elements (array); + g_assert (fixture.elements != NULL); + + fixture.n_elements = json_array_get_length (array); + g_assert_cmpint (fixture.n_elements, ==, g_list_length (fixture.elements)); + + fixture.iterations = 0; + + json_array_foreach_element (array, verify_foreach, &fixture); + + g_assert_cmpint (fixture.iterations, ==, fixture.n_elements); + + g_list_free (fixture.elements); + json_array_unref (array); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/array/empty-array", test_empty_array); + g_test_add_func ("/array/add-element", test_add_element); + g_test_add_func ("/array/remove-element", test_remove_element); + g_test_add_func ("/array/foreach-element", test_foreach_element); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/boxed.c b/json-glib/json-glib/tests/boxed.c new file mode 100644 index 0000000..deeef13 --- /dev/null +++ b/json-glib/json-glib/tests/boxed.c @@ -0,0 +1,264 @@ +#include +#include +#include + +#include + +#include +#include + +#define TEST_TYPE_BOXED (test_boxed_get_type ()) +#define TEST_TYPE_OBJECT (test_object_get_type ()) +#define TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject)) +#define TEST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT)) +#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass)) +#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT)) +#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass)) + +typedef struct _TestBoxed TestBoxed; +typedef struct _TestObject TestObject; +typedef struct _TestObjectClass TestObjectClass; + +struct _TestBoxed +{ + gint foo; + gboolean bar; +}; + +struct _TestObject +{ + GObject parent_instance; + + TestBoxed blah; +}; + +struct _TestObjectClass +{ + GObjectClass parent_class; +}; + +GType test_object_get_type (void); + +/*** implementation ***/ + +static gpointer +test_boxed_copy (gpointer src) +{ + return g_slice_dup (TestBoxed, src); +} + +static void +test_boxed_free (gpointer boxed) +{ + if (G_LIKELY (boxed != NULL)) + g_slice_free (TestBoxed, boxed); +} + +static JsonNode * +test_boxed_serialize (gconstpointer boxed) +{ + const TestBoxed *test = boxed; + JsonObject *object; + JsonNode *node; + + if (boxed == NULL) + return json_node_new (JSON_NODE_NULL); + + object = json_object_new (); + node = json_node_new (JSON_NODE_OBJECT); + + json_object_set_int_member (object, "foo", test->foo); + json_object_set_boolean_member (object, "bar", test->bar); + + json_node_take_object (node, object); + + if (g_test_verbose ()) + { + g_print ("Serialize: { foo: %" G_GINT64_FORMAT ", bar: %s }\n", + json_object_get_int_member (object, "foo"), + json_object_get_boolean_member (object, "bar") ? "true" : "false"); + } + + return node; +} + +static gpointer +test_boxed_deserialize (JsonNode *node) +{ + JsonObject *object; + TestBoxed *test; + + if (json_node_get_node_type (node) != JSON_NODE_OBJECT) + return NULL; + + object = json_node_get_object (node); + + test = g_slice_new (TestBoxed); + test->foo = json_object_get_int_member (object, "foo"); + test->bar = json_object_get_boolean_member (object, "bar"); + + if (g_test_verbose ()) + { + g_print ("Deserialize: { foo: %d, bar: %s }\n", + test->foo, + test->bar ? "true" : "false"); + } + + return test; +} + +GType +test_boxed_get_type (void) +{ + static GType b_type = 0; + + if (G_UNLIKELY (b_type == 0)) + { + b_type = g_boxed_type_register_static ("TestBoxed", + test_boxed_copy, + test_boxed_free); + + if (g_test_verbose ()) + g_print ("Registering transform functions\n"); + + json_boxed_register_serialize_func (b_type, JSON_NODE_OBJECT, + test_boxed_serialize); + json_boxed_register_deserialize_func (b_type, JSON_NODE_OBJECT, + test_boxed_deserialize); + } + + return b_type; +} + +enum +{ + PROP_0, + + PROP_BLAH +}; + +G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT); + +static void +test_object_finalize (GObject *gobject) +{ + G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject); +} + +static void +test_object_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_BLAH: + { + const TestBoxed *blah = g_value_get_boxed (value); + + TEST_OBJECT (gobject)->blah = *blah; + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +test_object_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_BLAH: + g_value_set_boxed (value, &(TEST_OBJECT (gobject)->blah)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +test_object_class_init (TestObjectClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = test_object_set_property; + gobject_class->get_property = test_object_get_property; + gobject_class->finalize = test_object_finalize; + + g_object_class_install_property (gobject_class, + PROP_BLAH, + g_param_spec_boxed ("blah", "Blah", "Blah", + TEST_TYPE_BOXED, + G_PARAM_READWRITE)); +} + +static void +test_object_init (TestObject *object) +{ + object->blah.foo = 0; + object->blah.bar = FALSE; +} + +static const gchar *serialize_data = +"{\n" +" \"blah\" : {\n" +" \"foo\" : 42,\n" +" \"bar\" : true\n" +" }\n" +"}"; + +static void +test_serialize_boxed (void) +{ + TestBoxed boxed = { 42, TRUE }; + GObject *obj; + gchar *data; + gsize len; + + obj = g_object_new (TEST_TYPE_OBJECT, "blah", &boxed, NULL); + + data = json_gobject_to_data (obj, &len); + + g_assert_cmpint (len, ==, strlen (serialize_data)); + g_assert_cmpstr (data, ==, serialize_data); + + if (g_test_verbose ()) + g_print ("TestObject:\n%s\n", data); + + g_free (data); + g_object_unref (obj); +} + +static void +test_deserialize_boxed (void) +{ + + GObject *obj; + + obj = json_gobject_from_data (TEST_TYPE_OBJECT, serialize_data, -1, NULL); + g_assert (TEST_IS_OBJECT (obj)); + g_assert_cmpint (TEST_OBJECT (obj)->blah.foo, ==, 42); + g_assert (TEST_OBJECT (obj)->blah.bar); + + g_object_unref (obj); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/boxed/serialize-property", test_serialize_boxed); + g_test_add_func ("/boxed/deserialize-property", test_deserialize_boxed); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/builder.c b/json-glib/json-glib/tests/builder.c new file mode 100644 index 0000000..9bf2bd8 --- /dev/null +++ b/json-glib/json-glib/tests/builder.c @@ -0,0 +1,161 @@ +#include +#include +#include + +#include + +#include + +static const gchar *complex_object = "{\"depth1\":[1,{\"depth2\":[3,[null],\"after array\"],\"value2\":true}],\"object1\":{}}"; + +static const gchar *empty_object = "{\"a\":{}}"; + +static const gchar *reset_object = "{\"test\":\"reset\"}"; +static const gchar *reset_array = "[\"reset\"]"; + +static void +test_builder_complex (void) +{ + JsonBuilder *builder = json_builder_new (); + JsonNode *node; + JsonGenerator *generator; + gsize length; + gchar *data; + + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "depth1"); + json_builder_begin_array (builder); + json_builder_add_int_value (builder, 1); + + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "depth2"); + json_builder_begin_array (builder); + json_builder_add_int_value (builder, 3); + + json_builder_begin_array (builder); + json_builder_add_null_value (builder); + json_builder_end_array (builder); + + json_builder_add_string_value (builder, "after array"); + json_builder_end_array (builder); /* depth2 */ + + json_builder_set_member_name (builder, "value2"); + json_builder_add_boolean_value (builder, TRUE); + json_builder_end_object (builder); + + json_builder_end_array (builder); /* depth1 */ + + json_builder_set_member_name (builder, "object1"); + json_builder_begin_object (builder); + json_builder_end_object (builder); + + json_builder_end_object (builder); + + node = json_builder_get_root (builder); + g_object_unref (builder); + + generator = json_generator_new (); + json_generator_set_root (generator, node); + data = json_generator_to_data (generator, &length); + + if (g_test_verbose ()) + g_print ("Builder complex: '%*s'\n", (int)length, data); + + g_assert_cmpint (length, ==, strlen (complex_object)); + g_assert_cmpstr (data, ==, complex_object); + + g_free (data); + json_node_free (node); + g_object_unref (generator); +} + +static void +test_builder_empty (void) +{ + JsonBuilder *builder = json_builder_new (); + JsonNode *node; + JsonGenerator *generator; + gsize length; + gchar *data; + + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "a"); + + json_builder_begin_object (builder); + json_builder_end_object (builder); + + json_builder_end_object (builder); + + node = json_builder_get_root (builder); + g_object_unref (builder); + + generator = json_generator_new (); + json_generator_set_root (generator, node); + data = json_generator_to_data (generator, &length); + + if (g_test_verbose ()) + g_print ("Builder empty: '%*s'\n", (int)length, data); + + g_assert_cmpint (length, ==, strlen (empty_object)); + g_assert_cmpstr (data, ==, empty_object); + + g_free (data); + json_node_free (node); + g_object_unref (generator); +} + +static void +test_builder_reset (void) +{ + JsonBuilder *builder = json_builder_new (); + JsonGenerator *generator = json_generator_new (); + JsonNode *node; + gsize length; + gchar *data; + + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "test"); + json_builder_add_string_value (builder, "reset"); + json_builder_end_object (builder); + + node = json_builder_get_root (builder); + json_generator_set_root (generator, node); + data = json_generator_to_data (generator, &length); + g_assert (strncmp (data, reset_object, length) == 0); + + g_free (data); + json_node_free (node); + + json_builder_reset (builder); + + json_builder_begin_array (builder); + json_builder_add_string_value (builder, "reset"); + json_builder_end_array (builder); + + node = json_builder_get_root (builder); + json_generator_set_root (generator, node); + data = json_generator_to_data (generator, &length); + g_assert (strncmp (data, reset_array, length) == 0); + + g_free (data); + json_node_free (node); + g_object_unref (builder); + g_object_unref (generator); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/builder/complex", test_builder_complex); + g_test_add_func ("/builder/complex", test_builder_empty); + g_test_add_func ("/builder/reset", test_builder_reset); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/generator.c b/json-glib/json-glib/tests/generator.c new file mode 100644 index 0000000..5cc8b92 --- /dev/null +++ b/json-glib/json-glib/tests/generator.c @@ -0,0 +1,330 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include + +#include + +#include + +static const gchar *empty_array = "[]"; +static const gchar *empty_object = "{}"; + +static const gchar *simple_array = "[true,false,null,42,\"foo\"]"; +static const gchar *nested_array = "[true,[false,null],42]"; + +static const gchar *simple_object = "{\"Bool1\":true,\"Bool2\":false,\"Null\":null,\"Int\":42,\"String\":\"foo\"}"; +/* taken from the RFC 4627, Examples section */ +static const gchar *nested_object = +"{" + "\"Image\":{" + "\"Width\":800," + "\"Height\":600," + "\"Title\":\"View from 15th Floor\"," + "\"Thumbnail\":{" + "\"Url\":\"http://www.example.com/image/481989943\"," + "\"Height\":125," + "\"Width\":\"100\"" + "}," + "\"IDs\":[116,943,234,38793]" + "}" +"}"; + +static const struct { + const gchar *lang; + const gchar *sep; + guint matches : 1; +} decimal_separator[] = { + { "C", ".", TRUE }, + { "de", ",", FALSE }, + { "en", ".", TRUE }, + { "fr", ",", FALSE } +}; + +static void +test_empty_array (void) +{ + JsonGenerator *gen = json_generator_new (); + JsonNode *root; + gchar *data; + gsize len; + + root = json_node_new (JSON_NODE_ARRAY); + json_node_take_array (root, json_array_new ()); + + json_generator_set_root (gen, root); + g_object_set (gen, "pretty", FALSE, NULL); + + data = json_generator_to_data (gen, &len); + + g_assert_cmpint (len, ==, strlen (empty_array)); + g_assert_cmpstr (data, ==, empty_array); + + g_free (data); + json_node_free (root); + g_object_unref (gen); +} + +static void +test_empty_object (void) +{ + JsonGenerator *gen = json_generator_new (); + JsonNode *root; + gchar *data; + gsize len; + + root = json_node_new (JSON_NODE_OBJECT); + json_node_take_object (root, json_object_new ()); + + json_generator_set_root (gen, root); + g_object_set (gen, "pretty", FALSE, NULL); + + data = json_generator_to_data (gen, &len); + + g_assert_cmpint (len, ==, strlen (empty_object)); + g_assert_cmpstr (data, ==, empty_object); + + g_free (data); + json_node_free (root); + g_object_unref (gen); +} + +static void +test_simple_array (void) +{ + JsonGenerator *generator = json_generator_new (); + JsonNode *root; + JsonArray *array; + gchar *data; + gsize len; + + root = json_node_new (JSON_NODE_ARRAY); + array = json_array_sized_new (5); + + json_array_add_boolean_element (array, TRUE); + json_array_add_boolean_element (array, FALSE); + json_array_add_null_element (array); + json_array_add_int_element (array, 42); + json_array_add_string_element (array, "foo"); + + json_node_take_array (root, array); + json_generator_set_root (generator, root); + + g_object_set (generator, "pretty", FALSE, NULL); + data = json_generator_to_data (generator, &len); + + if (g_test_verbose ()) + g_print ("checking simple array `%s' (expected: %s)\n", + data, + simple_array); + + g_assert_cmpint (len, ==, strlen (simple_array)); + g_assert_cmpstr (data, ==, simple_array); + + g_free (data); + json_node_free (root); + g_object_unref (generator); +} + +static void +test_nested_array (void) +{ + JsonGenerator *generator = json_generator_new (); + JsonNode *root; + JsonArray *array, *nested; + gchar *data; + gsize len; + + root = json_node_new (JSON_NODE_ARRAY); + array = json_array_sized_new (3); + + json_array_add_boolean_element (array, TRUE); + + { + nested = json_array_sized_new (2); + + json_array_add_boolean_element (nested, FALSE); + json_array_add_null_element (nested); + + json_array_add_array_element (array, nested); + } + + json_array_add_int_element (array, 42); + + json_node_take_array (root, array); + json_generator_set_root (generator, root); + + g_object_set (generator, "pretty", FALSE, NULL); + data = json_generator_to_data (generator, &len); + + g_assert_cmpint (len, ==, strlen (nested_array)); + g_assert_cmpstr (data, ==, nested_array); + + g_free (data); + json_node_free (root); + g_object_unref (generator); +} + +static void +test_simple_object (void) +{ + JsonGenerator *generator = json_generator_new (); + JsonNode *root; + JsonObject *object; + gchar *data; + gsize len; + + root = json_node_new (JSON_NODE_OBJECT); + object = json_object_new (); + + json_object_set_boolean_member (object, "Bool1", TRUE); + json_object_set_boolean_member (object, "Bool2", FALSE); + json_object_set_null_member (object, "Null"); + json_object_set_int_member (object, "Int", 42); + json_object_set_string_member (object, "String", "foo"); + + json_node_take_object (root, object); + json_generator_set_root (generator, root); + + g_object_set (generator, "pretty", FALSE, NULL); + data = json_generator_to_data (generator, &len); + + if (g_test_verbose ()) + g_print ("checking simple object `%s' (expected: %s)\n", + data, + simple_object); + + g_assert_cmpint (len, ==, strlen (simple_object)); + g_assert_cmpstr (data, ==, simple_object); + + g_free (data); + json_node_free (root); + g_object_unref (generator); +} + +static void +test_nested_object (void) +{ + JsonGenerator *generator = json_generator_new (); + JsonNode *root; + JsonObject *object, *nested; + JsonArray *array; + gchar *data; + gsize len; + + root = json_node_new (JSON_NODE_OBJECT); + object = json_object_new (); + + json_object_set_int_member (object, "Width", 800); + json_object_set_int_member (object, "Height", 600); + json_object_set_string_member (object, "Title", "View from 15th Floor"); + + { + nested = json_object_new (); + + json_object_set_string_member (nested, "Url", "http://www.example.com/image/481989943"); + json_object_set_int_member (nested, "Height", 125); + json_object_set_string_member (nested, "Width", "100"); + + json_object_set_object_member (object, "Thumbnail", nested); + } + + { + array = json_array_new (); + + json_array_add_int_element (array, 116); + json_array_add_int_element (array, 943); + json_array_add_int_element (array, 234); + json_array_add_int_element (array, 38793); + + json_object_set_array_member (object, "IDs", array); + } + + nested = json_object_new (); + json_object_set_object_member (nested, "Image", object); + + json_node_take_object (root, nested); + json_generator_set_root (generator, root); + + g_object_set (generator, "pretty", FALSE, NULL); + data = json_generator_to_data (generator, &len); + + if (g_test_verbose ()) + g_print ("checking nested object `%s' (expected: %s)\n", + data, + nested_object); + + g_assert_cmpint (len, ==, strlen (nested_object)); + g_assert_cmpstr (data, ==, nested_object); + + g_free (data); + json_node_free (root); + g_object_unref (generator); +} + +static void +test_decimal_separator (void) +{ + JsonNode *node = json_node_new (JSON_NODE_VALUE); + JsonGenerator *generator = json_generator_new (); + gchar *old_locale; + gint i; + + json_node_set_double (node, 3.14); + + json_generator_set_root (generator, node); + + old_locale = setlocale (LC_NUMERIC, NULL); + + for (i = 0; i < G_N_ELEMENTS (decimal_separator); i++) + { + gchar *str, *expected; + + setlocale (LC_NUMERIC, decimal_separator[i].lang); + + str = json_generator_to_data (generator, NULL); + + if (g_test_verbose ()) + g_print ("%s: value: %.2f - string: '%s'\n", + G_STRFUNC, + json_node_get_double (node), + str); + + g_assert (str != NULL); + expected = strstr (str, decimal_separator[i].sep); + if (decimal_separator[i].matches) + g_assert (expected != NULL); + else + g_assert (expected == NULL); + + g_free (str); + } + + setlocale (LC_NUMERIC, old_locale); + + g_object_unref (generator); + json_node_free (node); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/generator/empty-array", test_empty_array); + g_test_add_func ("/generator/empty-object", test_empty_object); + g_test_add_func ("/generator/simple-array", test_simple_array); + g_test_add_func ("/generator/nested-array", test_nested_array); + g_test_add_func ("/generator/simple-object", test_simple_object); + g_test_add_func ("/generator/nested-object", test_nested_object); + g_test_add_func ("/generator/decimal-separator", test_decimal_separator); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/gvariant.c b/json-glib/json-glib/tests/gvariant.c new file mode 100644 index 0000000..074dd69 --- /dev/null +++ b/json-glib/json-glib/tests/gvariant.c @@ -0,0 +1,233 @@ +#include +#include +#include + +typedef struct +{ + gchar *test_name; + gchar *signature; + gchar *variant_data; + gchar *json_data; +} TestCase; + +/* each entry in this list spawns to a GVariant-to-JSON and + JSON-to-GVariant test */ +const TestCase test_cases[] = + { + /* boolean */ + { "/boolean", "(b)", "(true,)", "[true]" }, + + /* byte */ + { "/byte", "(y)", "(byte 0xff,)", "[255]" }, + + /* int16 */ + { "/int16", "(n)", "(int16 -12345,)", "[-12345]" }, + + /* uint16 */ + { "/uint16", "(q)", "(uint16 40001,)", "[40001]" }, + + /* int32 */ + { "/int32", "(i)", "(-7654321,)", "[-7654321]" }, + + /* uint32 */ + { "/uint32", "(u)", "(uint32 12345678,)", "[12345678]" }, + + /* int64 */ + { "/int64", "(x)", "(int64 -666999666999,)", "[-666999666999]" }, + + /* uint64 */ + { "/uint64", "(t)", "(uint64 1999999999999999,)", "[1999999999999999]" }, + + /* handle */ + { "/handle", "(h)", "(handle 1,)", "[1]" }, + + /* double */ + { "/double", "(d)", "(1.23,)", "[1.23]" }, + + /* string */ + { "/string", "(s)", "('hello world!',)", "[\"hello world!\"]" }, + + /* object-path */ + { "/object-path", "(o)", "(objectpath '/org/gtk/json_glib',)", "[\"/org/gtk/json_glib\"]" }, + + /* signature */ + { "/signature", "(g)", "(signature '(asna{sv}i)',)", "[\"(asna{sv}i)\"]" }, + + /* maybe - null string */ + { "/maybe/simple/null", "(ms)", "(@ms nothing,)", "[null]" }, + + /* maybe - simple string */ + { "/maybe/simple/string", "(ms)", "(@ms 'maybe string',)", "[\"maybe string\"]" }, + + /* maybe - null container */ + { "/maybe/container/null", "(m(sn))", "(@m(sn) nothing,)", "[null]" }, + + /* maybe - tuple container */ + { "/maybe/container/tuple", "(m(sn))", "(@m(sn) ('foo', 0),)", "[[\"foo\",0]]" }, + + /* maybe - variant boolean */ + { "/maybe/variant/boolean", "(mv)", "(@mv ,)", "[true]" }, + + /* empty array */ + { "/array/empty", "as", "@as []", "[]" }, + + /* array of bytes */ + { "/array/byte", "ay", "[byte 0x01, 0x0a, 0x03, 0xff]", "[1,10,3,255]" }, + + /* array of strings */ + { "/array/string", "as", "['a', 'b', 'ab', 'ba']", "[\"a\",\"b\",\"ab\",\"ba\"]" }, + + /* array of array of int32 */ + { "/array/array/int32", "aai", "[[1, 2], [3, 4], [5, 6]]", "[[1,2],[3,4],[5,6]]" }, + + /* array of variants */ + { "/array/variant", "av", "[, , <'oops'>, , <0.5>]", "[true,1,\"oops\",-2,0.5]" }, + + /* tuple */ + { "/tuple", "(bynqiuxthds)", + "(false, byte 0x00, int16 -1, uint16 1, -2, uint32 2, int64 429496729, uint64 3, handle 16, 2.48, 'end')", + "[false,0,-1,1,-2,2,429496729,3,16,2.48,\"end\"]" }, + + /* empty dictionary */ + { "/dictionary/empty", "a{sv}", "@a{sv} {}", "{}" }, + + /* single dictionary entry */ + { "/dictionary/single-entry", "{ss}", "{'hello', 'world'}", "{\"hello\":\"world\"}" }, + + /* dictionary - string : int32 */ + { "/dictionary/string-int32", "a{si}", "{'foo': 1, 'bar': 2}", "{\"foo\":1,\"bar\":2}" }, + + /* dictionary - string : variant */ + { "/dictionary/string-variant", "a{sv}", "{'str': <'hi!'>, 'bool': }", "{\"str\":\"hi!\",\"bool\":true}" }, + + /* dictionary - int64 : variant */ + { "/dictionary/int64-variant", "a{xv}", + "{int64 -5: <'minus five'>, 10: <'ten'>}", "{\"-5\":\"minus five\",\"10\":\"ten\"}" }, + + /* dictionary - uint64 : variant */ + { "/dictionary/uint64-boolean", "a{tb}", + "{uint64 999888777666: true, 0: false}", "{\"999888777666\":true,\"0\":false}" }, + + /* dictionary - boolean : variant */ + { "/dictionary/boolean-variant", "a{by}", "{true: byte 0x01, false: 0x00}", "{\"true\":1,\"false\":0}" }, + + /* dictionary - double : string */ + { "/dictionary/double-string", "a{ds}", "{1.0: 'one point zero'}", "{\"1.000000\":\"one point zero\"}" }, + + /* variant - string */ + { "/variant/string", "(v)", "(<'string within variant'>,)", "[\"string within variant\"]" }, + + /* variant - maybe null */ + { "/variant/maybe/null", "(v)", "(<@mv nothing>,)", "[null]" }, + + /* variant - dictionary */ + { "/variant/dictionary", "v", "<{'foo': <'bar'>, 'hi': }>", "{\"foo\":\"bar\",\"hi\":1024}" }, + + /* variant - variant - array */ + { "/variant/variant/array", "v", "<[<'any'>, <'thing'>, , ]>", "[\"any\",\"thing\",0,-1]" }, + + /* deep-nesting */ + { "/deep-nesting", + "a(a(a(a(a(a(a(a(a(a(s))))))))))", + "[([([([([([([([([([('sorprise',)],)],)],)],)],)],)],)],)],)]", + "[[[[[[[[[[[[[[[[[[[[\"sorprise\"]]]]]]]]]]]]]]]]]]]]" }, + + /* mixed1 */ + { "/mixed1", + "a{s(syba(od))}", + "{'foo': ('bar', byte 0x64, true, [(objectpath '/baz', 1.3), ('/cat', -2.5)])}", + "{\"foo\":[\"bar\",100,true,[[\"/baz\",1.3],[\"/cat\",-2.5]]]}" }, + + /* mixed2 */ + { "/mixed2", + "(a{by}amsvmaba{qm(sg)})", + "({true: byte 0x01, false: 0x00}, [@ms 'do', nothing, 'did'], <@av []>, @mab nothing, {uint16 10000: @m(sg) ('yes', 'august'), 0: nothing})", + "[{\"true\":1,\"false\":0},[\"do\",null,\"did\"],[],null,{\"10000\":[\"yes\",\"august\"],\"0\":null}]" }, + }; + +static void +test_gvariant_to_json (gconstpointer test_data) +{ + TestCase *test_case = (TestCase *) test_data; + GVariant *variant; + gchar *json_data; + gsize len; + + variant = g_variant_parse (G_VARIANT_TYPE (test_case->signature), + test_case->variant_data, + NULL, + NULL, + NULL); + + json_data = json_gvariant_serialize_data (variant, &len); + g_assert (json_data != NULL); + + g_assert_cmpstr (test_case->json_data, ==, json_data); + + g_free (json_data); + g_variant_unref (variant); +} + +static void +test_json_to_gvariant (gconstpointer test_data) +{ + TestCase *test_case = (TestCase *) test_data; + GVariant *variant; + gchar *variant_data; + GError *error = NULL; + + variant = json_gvariant_deserialize_data (test_case->json_data, + -1, + test_case->signature, + &error); + + if (variant == NULL) + { + g_assert_no_error (error); + g_error_free (error); + } + else + { + variant_data = g_variant_print (variant, TRUE); + + g_assert_cmpstr (test_case->variant_data, ==, variant_data); + + g_free (variant_data); + g_variant_unref (variant); + } +} + +gint +main (gint argc, gchar *argv[]) +{ + gint i; + TestCase test_case; + gchar *test_name; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* GVariant to JSON */ + for (i = 0; i < sizeof (test_cases) / sizeof (TestCase); i++) + { + test_case = test_cases[i]; + test_name = g_strdup_printf ("/gvariant/to-json/%s", test_case.test_name); + + g_test_add_data_func (test_name, &test_cases[i], test_gvariant_to_json); + + g_free (test_name); + } + + /* JSON to GVariant */ + for (i = 0; i < sizeof (test_cases) / sizeof (TestCase); i++) + { + test_case = test_cases[i]; + test_name = g_strdup_printf ("/gvariant/from-json/%s", test_case.test_name); + + g_test_add_data_func (test_name, &test_cases[i], test_json_to_gvariant); + + g_free (test_name); + } + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/node.c b/json-glib/json-glib/tests/node.c new file mode 100644 index 0000000..0c2fd9b --- /dev/null +++ b/json-glib/json-glib/tests/node.c @@ -0,0 +1,112 @@ +#include +#include +#include + +static void +test_copy_null (void) +{ + JsonNode *node = json_node_new (JSON_NODE_NULL); + JsonNode *copy = json_node_copy (node); + + g_assert_cmpint (json_node_get_node_type (node), ==, json_node_get_node_type (copy)); + g_assert_cmpint (json_node_get_value_type (node), ==, json_node_get_value_type (copy)); + g_assert_cmpstr (json_node_type_name (node), ==, json_node_type_name (copy)); + + json_node_free (copy); + json_node_free (node); +} + +static void +test_copy_value (void) +{ + JsonNode *node = json_node_new (JSON_NODE_VALUE); + JsonNode *copy; + + json_node_set_string (node, "hello"); + + copy = json_node_copy (node); + g_assert_cmpint (json_node_get_node_type (node), ==, json_node_get_node_type (copy)); + g_assert_cmpstr (json_node_type_name (node), ==, json_node_type_name (copy)); + g_assert_cmpstr (json_node_get_string (node), ==, json_node_get_string (copy)); + + json_node_free (copy); + json_node_free (node); +} + +static void +test_copy_object (void) +{ + JsonObject *obj = json_object_new (); + JsonNode *node = json_node_new (JSON_NODE_OBJECT); + JsonNode *value = json_node_new (JSON_NODE_VALUE); + JsonNode *copy; + + json_node_set_int (value, 42); + json_object_set_member (obj, "answer", value); + + json_node_take_object (node, obj); + + copy = json_node_copy (node); + + g_assert_cmpint (json_node_get_node_type (node), ==, json_node_get_node_type (copy)); + g_assert (json_node_get_object (node) == json_node_get_object (copy)); + + json_node_free (copy); + json_node_free (node); +} + +static void +test_null (void) +{ + JsonNode *node = json_node_new (JSON_NODE_NULL); + + g_assert (JSON_NODE_HOLDS_NULL (node)); + g_assert_cmpint (json_node_get_value_type (node), ==, G_TYPE_INVALID); + g_assert_cmpstr (json_node_type_name (node), ==, "NULL"); + + json_node_free (node); +} + +static void +test_value (void) +{ + JsonNode *node = json_node_new (JSON_NODE_VALUE); + GValue value = { 0, }; + GValue check = { 0, }; + + g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_VALUE); + + g_value_init (&value, G_TYPE_INT64); + g_value_set_int64 (&value, 42); + + g_assert_cmpint (G_VALUE_TYPE (&value), ==, G_TYPE_INT64); + g_assert_cmpint (g_value_get_int64 (&value), ==, 42); + + json_node_set_value (node, &value); + json_node_get_value (node, &check); + + g_assert_cmpint (G_VALUE_TYPE (&value), ==, G_VALUE_TYPE (&check)); + g_assert_cmpint (g_value_get_int64 (&value), ==, g_value_get_int64 (&check)); + g_assert_cmpint (G_VALUE_TYPE (&check), ==, G_TYPE_INT64); + g_assert_cmpint (g_value_get_int64 (&check), ==, 42); + + g_value_unset (&value); + g_value_unset (&check); + json_node_free (node); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/nodes/null-node", test_null); + g_test_add_func ("/nodes/copy-null", test_copy_null); + g_test_add_func ("/nodes/copy-value", test_copy_value); + g_test_add_func ("/nodes/copy-object", test_copy_object); + g_test_add_func ("/nodes/value", test_value); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/object.c b/json-glib/json-glib/tests/object.c new file mode 100644 index 0000000..2f00078 --- /dev/null +++ b/json-glib/json-glib/tests/object.c @@ -0,0 +1,165 @@ +#include +#include +#include + +#include +#include + +static void +test_empty_object (void) +{ + JsonObject *object = json_object_new (); + + g_assert_cmpint (json_object_get_size (object), ==, 0); + g_assert (json_object_get_members (object) == NULL); + + json_object_unref (object); +} + +static void +test_add_member (void) +{ + JsonObject *object = json_object_new (); + JsonNode *node = json_node_new (JSON_NODE_NULL); + + g_assert_cmpint (json_object_get_size (object), ==, 0); + + json_object_set_member (object, "Null", node); + g_assert_cmpint (json_object_get_size (object), ==, 1); + + node = json_object_get_member (object, "Null"); + g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_NULL); + + json_object_unref (object); +} + +static void +test_set_member (void) +{ + JsonNode *node = json_node_new (JSON_NODE_VALUE); + JsonObject *object = json_object_new (); + + g_assert_cmpint (json_object_get_size (object), ==, 0); + + json_node_set_string (node, "Hello"); + + json_object_set_member (object, "String", node); + g_assert_cmpint (json_object_get_size (object), ==, 1); + + node = json_object_get_member (object, "String"); + g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_VALUE); + g_assert_cmpstr (json_node_get_string (node), ==, "Hello"); + + json_object_set_string_member (object, "String", "World"); + node = json_object_get_member (object, "String"); + g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_VALUE); + g_assert_cmpstr (json_node_get_string (node), ==, "World"); + + json_object_set_string_member (object, "String", "Goodbye"); + g_assert_cmpstr (json_object_get_string_member (object, "String"), ==, "Goodbye"); + + json_object_unref (object); +} + +static void +test_remove_member (void) +{ + JsonObject *object = json_object_new (); + JsonNode *node = json_node_new (JSON_NODE_NULL); + + json_object_set_member (object, "Null", node); + + json_object_remove_member (object, "Null"); + g_assert_cmpint (json_object_get_size (object), ==, 0); + + json_object_unref (object); +} + +typedef struct _TestForeachFixture +{ + gint n_members; +} TestForeachFixture; + +static const struct { + const gchar *member_name; + JsonNodeType member_type; + GType member_gtype; +} type_verify[] = { + { "integer", JSON_NODE_VALUE, G_TYPE_INT64 }, + { "boolean", JSON_NODE_VALUE, G_TYPE_BOOLEAN }, + { "string", JSON_NODE_VALUE, G_TYPE_STRING }, + { "null", JSON_NODE_NULL, G_TYPE_INVALID } +}; + +static void +verify_foreach (JsonObject *object, + const gchar *member_name, + JsonNode *member_node, + gpointer user_data) +{ + TestForeachFixture *fixture = user_data; + gint i; + + for (i = 0; i < G_N_ELEMENTS (type_verify); i++) + { + if (strcmp (member_name, type_verify[i].member_name) == 0) + { + g_assert (json_node_get_node_type (member_node) == type_verify[i].member_type); + g_assert (json_node_get_value_type (member_node) == type_verify[i].member_gtype); + break; + } + } + + fixture->n_members += 1; +} + +static void +test_foreach_member (void) +{ + JsonObject *object = json_object_new (); + TestForeachFixture fixture = { 0, }; + + json_object_set_int_member (object, "integer", 42); + json_object_set_boolean_member (object, "boolean", TRUE); + json_object_set_string_member (object, "string", "hello"); + json_object_set_null_member (object, "null"); + + json_object_foreach_member (object, verify_foreach, &fixture); + + g_assert_cmpint (fixture.n_members, ==, json_object_get_size (object)); + + json_object_unref (object); +} + +static void +test_empty_member (void) +{ + JsonObject *object = json_object_new (); + + json_object_set_string_member (object, "string", ""); + g_assert (json_object_has_member (object, "string")); + g_assert_cmpstr (json_object_get_string_member (object, "string"), ==, ""); + + json_object_set_string_member (object, "null", NULL); + g_assert (json_object_has_member (object, "null")); + g_assert (json_object_get_string_member (object, "null") == NULL); + + json_object_unref (object); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/object/empty-object", test_empty_object); + g_test_add_func ("/object/add-member", test_add_member); + g_test_add_func ("/object/set-member", test_set_member); + g_test_add_func ("/object/remove-member", test_remove_member); + g_test_add_func ("/object/foreach-member", test_foreach_member); + g_test_add_func ("/object/empty-member", test_empty_member); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/parser.c b/json-glib/json-glib/tests/parser.c new file mode 100644 index 0000000..d3f2404 --- /dev/null +++ b/json-glib/json-glib/tests/parser.c @@ -0,0 +1,785 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include + +static const gchar *test_empty_string = ""; +static const gchar *test_empty_array_string = "[ ]"; +static const gchar *test_empty_object_string = "{ }"; + +static void +verify_int_value (JsonNode *node) +{ + g_assert_cmpint (42, ==, json_node_get_int (node)); +} + +static void +verify_boolean_value (JsonNode *node) +{ + g_assert_cmpint (TRUE, ==, json_node_get_boolean (node)); +} + +static void +verify_string_value (JsonNode *node) +{ + g_assert_cmpstr ("string", ==, json_node_get_string (node)); +} + +static void +verify_double_value (JsonNode *node) +{ + g_assert_cmpfloat (10.2e3, ==, json_node_get_double (node)); +} + +static const struct { + const gchar *str; + JsonNodeType type; + GType gtype; + void (* verify_value) (JsonNode *node); +} test_base_values[] = { + { "null", JSON_NODE_NULL, G_TYPE_INVALID, NULL, }, + { "42", JSON_NODE_VALUE, G_TYPE_INT64, verify_int_value }, + { "true", JSON_NODE_VALUE, G_TYPE_BOOLEAN, verify_boolean_value }, + { "\"string\"", JSON_NODE_VALUE, G_TYPE_STRING, verify_string_value }, + { "10.2e3", JSON_NODE_VALUE, G_TYPE_DOUBLE, verify_double_value } +}; + +static const struct { + const gchar *str; + gint len; + gint element; + JsonNodeType type; + GType gtype; +} test_simple_arrays[] = { + { "[ true ]", 1, 0, JSON_NODE_VALUE, G_TYPE_BOOLEAN }, + { "[ true, false, null ]", 3, 2, JSON_NODE_NULL, G_TYPE_INVALID }, + { "[ 1, 2, 3.14, \"test\" ]", 4, 3, JSON_NODE_VALUE, G_TYPE_STRING } +}; + +static const gchar *test_nested_arrays[] = { + "[ 42, [ ], null ]", + "[ [ ], [ true, [ true ] ] ]", + "[ [ false, true, 42 ], [ true, false, 3.14 ], \"test\" ]", + "[ true, { } ]", + "[ false, { \"test\" : 42 } ]", + "[ { \"test\" : 42 }, null ]", + "[ true, { \"test\" : 42 }, null ]", + "[ { \"channel\" : \"/meta/connect\" } ]" +}; + +static const struct { + const gchar *str; + gint size; + const gchar *member; + JsonNodeType type; + GType gtype; +} test_simple_objects[] = { + { "{ \"test\" : 42 }", 1, "test", JSON_NODE_VALUE, G_TYPE_INT64 }, + { "{ \"name\" : \"\", \"state\" : 1 }", 2, "name", JSON_NODE_VALUE, G_TYPE_STRING }, + { "{ \"foo\" : \"bar\", \"baz\" : null }", 2, "baz", JSON_NODE_NULL, G_TYPE_INVALID }, + { "{ \"channel\" : \"/meta/connect\" }", 1, "channel", JSON_NODE_VALUE, G_TYPE_STRING }, + { "{ \"halign\":0.5, \"valign\":0.5 }", 2, "valign", JSON_NODE_VALUE, G_TYPE_DOUBLE } +}; + +static const gchar *test_nested_objects[] = { + "{ \"array\" : [ false, \"foo\" ], \"object\" : { \"foo\" : true } }", + "{ " + "\"type\" : \"ClutterGroup\", " + "\"width\" : 1, " + "\"children\" : [ " + "{ " + "\"type\" : \"ClutterRectangle\", " + "\"children\" : [ " + "{ \"type\" : \"ClutterText\", \"text\" : \"hello there\" }" + "] " + "}, " + "{ " + "\"type\" : \"ClutterGroup\", " + "\"width\" : 1, " + "\"children\" : [ " + "{ \"type\" : \"ClutterText\", \"text\" : \"hello\" }" + "] " + "} " + "] " + "}" +}; + +static const struct { + const gchar *str; + const gchar *var; +} test_assignments[] = { + { "var foo = [ false, false, true ]", "foo" }, + { "var bar = [ true, 42 ];", "bar" }, + { "var baz = { \"foo\" : false }", "baz" } +}; + +static const struct +{ + const gchar *str; + const gchar *member; + const gchar *match; +} test_unicode[] = { + { "{ \"test\" : \"foo \\u00e8\" }", "test", "foo è" } +}; + +static const struct +{ + const gchar *str; + JsonParserError code; +} test_invalid[] = { + { "test", JSON_PARSER_ERROR_INVALID_BAREWORD }, + { "[ foo, ]", JSON_PARSER_ERROR_INVALID_BAREWORD }, + { "[ true, ]", JSON_PARSER_ERROR_TRAILING_COMMA }, + { "{ \"foo\" : true \"bar\" : false }", JSON_PARSER_ERROR_MISSING_COMMA }, + { "[ true, [ false, ] ]", JSON_PARSER_ERROR_TRAILING_COMMA }, + { "{ \"foo\" : { \"bar\" : false, } }", JSON_PARSER_ERROR_TRAILING_COMMA }, + { "[ { }, { }, { }, ]", JSON_PARSER_ERROR_TRAILING_COMMA }, + { "{ \"foo\" false }", JSON_PARSER_ERROR_MISSING_COLON } +}; + +static guint n_test_base_values = G_N_ELEMENTS (test_base_values); +static guint n_test_simple_arrays = G_N_ELEMENTS (test_simple_arrays); +static guint n_test_nested_arrays = G_N_ELEMENTS (test_nested_arrays); +static guint n_test_simple_objects = G_N_ELEMENTS (test_simple_objects); +static guint n_test_nested_objects = G_N_ELEMENTS (test_nested_objects); +static guint n_test_assignments = G_N_ELEMENTS (test_assignments); +static guint n_test_unicode = G_N_ELEMENTS (test_unicode); +static guint n_test_invalid = G_N_ELEMENTS (test_invalid); + +static void +test_empty (void) +{ + JsonParser *parser; + GError *error = NULL; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with empty string...\n"); + + if (!json_parser_load_from_data (parser, test_empty_string, -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + if (g_test_verbose ()) + g_print ("checking json_parser_get_root...\n"); + + g_assert (NULL == json_parser_get_root (parser)); + } + + g_object_unref (parser); +} + +static void +test_base_value (void) +{ + gint i; + JsonParser *parser; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with base-values...\n"); + + for (i = 0; i < n_test_base_values; i++) + { + GError *error = NULL; + + if (!json_parser_load_from_data (parser, test_base_values[i].str, -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + JsonNode *root; + + g_assert (NULL != json_parser_get_root (parser)); + + root = json_parser_get_root (parser); + g_assert (root != NULL); + g_assert (json_node_get_parent (root) == NULL); + + if (g_test_verbose ()) + g_print ("checking root node is of the desired type %s...\n", + test_base_values[i].gtype == G_TYPE_INVALID ? "" + : g_type_name (test_base_values[i].gtype)); + g_assert_cmpint (JSON_NODE_TYPE (root), ==, test_base_values[i].type); + g_assert_cmpint (json_node_get_value_type (root), ==, test_base_values[i].gtype); + + if (test_base_values[i].verify_value) + test_base_values[i].verify_value (root); + } + } + + g_object_unref (parser); +} + +static void +test_empty_array (void) +{ + JsonParser *parser; + GError *error = NULL; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with empty array...\n"); + + if (!json_parser_load_from_data (parser, test_empty_array_string, -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + JsonNode *root; + JsonArray *array; + + g_assert (NULL != json_parser_get_root (parser)); + + if (g_test_verbose ()) + g_print ("checking root node is an array...\n"); + root = json_parser_get_root (parser); + g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_ARRAY); + g_assert (json_node_get_parent (root) == NULL); + + array = json_node_get_array (root); + g_assert (array != NULL); + + if (g_test_verbose ()) + g_print ("checking array is empty...\n"); + g_assert_cmpint (json_array_get_length (array), ==, 0); + } + + g_object_unref (parser); +} + +static void +test_simple_array (void) +{ + gint i; + JsonParser *parser; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with simple arrays...\n"); + + for (i = 0; i < n_test_simple_arrays; i++) + { + GError *error = NULL; + + if (g_test_verbose ()) + g_print ("Parsing: '%s'\n", test_simple_arrays[i].str); + + if (!json_parser_load_from_data (parser, test_simple_arrays[i].str, -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + JsonNode *root, *node; + JsonArray *array; + + g_assert (NULL != json_parser_get_root (parser)); + + if (g_test_verbose ()) + g_print ("checking root node is an array...\n"); + root = json_parser_get_root (parser); + g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_ARRAY); + g_assert (json_node_get_parent (root) == NULL); + + array = json_node_get_array (root); + g_assert (array != NULL); + + if (g_test_verbose ()) + g_print ("checking array is of the desired length (%d)...\n", + test_simple_arrays[i].len); + g_assert_cmpint (json_array_get_length (array), ==, test_simple_arrays[i].len); + + if (g_test_verbose ()) + g_print ("checking element %d is of the desired type %s...\n", + test_simple_arrays[i].element, + g_type_name (test_simple_arrays[i].gtype)); + node = json_array_get_element (array, test_simple_arrays[i].element); + g_assert (node != NULL); + g_assert (json_node_get_parent (node) == root); + g_assert_cmpint (JSON_NODE_TYPE (node), ==, test_simple_arrays[i].type); + g_assert_cmpint (json_node_get_value_type (node), ==, test_simple_arrays[i].gtype); + } + } + + g_object_unref (parser); +} + +static void +test_nested_array (void) +{ + gint i; + JsonParser *parser; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with nested arrays...\n"); + + for (i = 0; i < n_test_nested_arrays; i++) + { + GError *error = NULL; + + if (!json_parser_load_from_data (parser, test_nested_arrays[i], -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + JsonNode *root; + JsonArray *array; + + g_assert (NULL != json_parser_get_root (parser)); + + if (g_test_verbose ()) + g_print ("checking root node is an array...\n"); + root = json_parser_get_root (parser); + g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_ARRAY); + g_assert (json_node_get_parent (root) == NULL); + + array = json_node_get_array (root); + g_assert (array != NULL); + + if (g_test_verbose ()) + g_print ("checking array is not empty...\n"); + g_assert_cmpint (json_array_get_length (array), >, 0); + } + } + + g_object_unref (parser); +} + +static void +test_empty_object (void) +{ + JsonParser *parser; + GError *error = NULL; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with empty object...\n"); + + if (!json_parser_load_from_data (parser, test_empty_object_string, -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + JsonNode *root; + JsonObject *object; + + g_assert (NULL != json_parser_get_root (parser)); + + if (g_test_verbose ()) + g_print ("checking root node is an object...\n"); + root = json_parser_get_root (parser); + g_assert (json_node_get_parent (root) == NULL); + g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT); + g_assert (json_node_get_parent (root) == NULL); + + object = json_node_get_object (root); + g_assert (object != NULL); + + if (g_test_verbose ()) + g_print ("checking object is empty...\n"); + g_assert_cmpint (json_object_get_size (object), ==, 0); + } + + g_object_unref (parser); +} + +static void +test_simple_object (void) +{ + gint i; + JsonParser *parser; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with simple objects...\n"); + + for (i = 0; i < n_test_simple_objects; i++) + { + GError *error = NULL; + + if (!json_parser_load_from_data (parser, test_simple_objects[i].str, -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + JsonNode *root, *node; + JsonObject *object; + + g_assert (NULL != json_parser_get_root (parser)); + + if (g_test_verbose ()) + g_print ("checking root node is an object...\n"); + root = json_parser_get_root (parser); + g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT); + g_assert (json_node_get_parent (root) == NULL); + + object = json_node_get_object (root); + g_assert (object != NULL); + + if (g_test_verbose ()) + g_print ("checking object is of the desired size (%d)...\n", + test_simple_objects[i].size); + g_assert_cmpint (json_object_get_size (object), ==, test_simple_objects[i].size); + + if (g_test_verbose ()) + g_print ("checking member '%s' is of the desired type %s...\n", + test_simple_objects[i].member, + g_type_name (test_simple_objects[i].gtype)); + node = json_object_get_member (object, test_simple_objects[i].member); + g_assert (node != NULL); + g_assert (json_node_get_parent (node) == root); + g_assert_cmpint (JSON_NODE_TYPE (node), ==, test_simple_objects[i].type); + g_assert_cmpint (json_node_get_value_type (node), ==, test_simple_objects[i].gtype); + } + } + + g_object_unref (parser); +} + +static void +test_nested_object (void) +{ + gint i; + JsonParser *parser; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with nested objects...\n"); + + for (i = 0; i < n_test_nested_objects; i++) + { + GError *error = NULL; + + if (!json_parser_load_from_data (parser, test_nested_objects[i], -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + JsonNode *root; + JsonObject *object; + + g_assert (NULL != json_parser_get_root (parser)); + + if (g_test_verbose ()) + g_print ("checking root node is an object...\n"); + root = json_parser_get_root (parser); + g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT); + g_assert (json_node_get_parent (root) == NULL); + + object = json_node_get_object (root); + g_assert (object != NULL); + + if (g_test_verbose ()) + g_print ("checking object is not empty...\n"); + g_assert_cmpint (json_object_get_size (object), >, 0); + } + } + + g_object_unref (parser); +} + +static void +test_assignment (void) +{ + gint i; + JsonParser *parser; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with assignments...\n"); + + for (i = 0; i < n_test_assignments; i++) + { + GError *error = NULL; + + if (!json_parser_load_from_data (parser, test_assignments[i].str, -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + gchar *var; + + if (g_test_verbose ()) + g_print ("checking assignment...\n"); + + g_assert (json_parser_has_assignment (parser, &var) == TRUE); + g_assert (var != NULL); + g_assert_cmpstr (var, ==, test_assignments[i].var); + g_assert (NULL != json_parser_get_root (parser)); + } + } + + g_object_unref (parser); +} + +static void +test_unicode_escape (void) +{ + gint i; + JsonParser *parser; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with unicode escape...\n"); + + for (i = 0; i < n_test_unicode; i++) + { + GError *error = NULL; + + if (!json_parser_load_from_data (parser, test_unicode[i].str, -1, &error)) + { + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + + g_error_free (error); + g_object_unref (parser); + exit (1); + } + else + { + JsonNode *root, *node; + JsonObject *object; + + g_assert (NULL != json_parser_get_root (parser)); + + if (g_test_verbose ()) + g_print ("checking root node is an object...\n"); + root = json_parser_get_root (parser); + g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT); + + object = json_node_get_object (root); + g_assert (object != NULL); + + if (g_test_verbose ()) + g_print ("checking object is not empty...\n"); + g_assert_cmpint (json_object_get_size (object), >, 0); + + node = json_object_get_member (object, test_unicode[i].member); + g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_VALUE); + + if (g_test_verbose ()) + g_print ("checking simple string equality...\n"); + g_assert_cmpstr (json_node_get_string (node), ==, test_unicode[i].match); + + if (g_test_verbose ()) + g_print ("checking for valid UTF-8...\n"); + g_assert (g_utf8_validate (json_node_get_string (node), -1, NULL)); + } + } + + g_object_unref (parser); +} + +static void +test_invalid_json (void) +{ + JsonParser *parser; + GError *error = NULL; + gint i; + + parser = json_parser_new (); + g_assert (JSON_IS_PARSER (parser)); + + if (g_test_verbose ()) + g_print ("checking json_parser_load_from_data with invalid data...\n"); + + for (i = 0; i < n_test_invalid; i++) + { + gboolean res; + + if (g_test_verbose ()) + g_print ("Parsing: '%s'\n", test_invalid[i].str); + + res = json_parser_load_from_data (parser, test_invalid[i].str, -1, + &error); + + g_assert (!res); + g_assert_error (error, JSON_PARSER_ERROR, test_invalid[i].code); + + if (g_test_verbose ()) + g_print ("Error: %s\n", error->message); + + g_clear_error (&error); + } + + g_object_unref (parser); +} + +static void +test_stream_sync (void) +{ + JsonParser *parser; + GFile *file; + GFileInputStream *stream; + GError *error = NULL; + JsonNode *root; + + parser = json_parser_new (); + + file = g_file_new_for_path (TESTS_DATA_DIR "/stream-load.json"); + stream = g_file_read (file, NULL, &error); + g_assert (error == NULL); + g_assert (stream != NULL); + + json_parser_load_from_stream (parser, G_INPUT_STREAM (stream), NULL, &error); + g_assert (error == NULL); + + root = json_parser_get_root (parser); + g_assert (root != NULL); + g_assert (JSON_NODE_HOLDS_ARRAY (root)); + + g_object_unref (stream); + g_object_unref (file); + g_object_unref (parser); +} + +static void +on_load_complete (GObject *gobject, + GAsyncResult *result, + gpointer user_data) +{ + JsonParser *parser = JSON_PARSER (gobject); + GMainLoop *main_loop = user_data; + GError *error = NULL; + JsonNode *root; + gboolean res; + + res = json_parser_load_from_stream_finish (parser, result, &error); + g_assert (res); + g_assert (error == NULL); + + root = json_parser_get_root (parser); + g_assert (root != NULL); + g_assert (JSON_NODE_HOLDS_ARRAY (root)); + + g_main_loop_quit (main_loop); +} + +static void +test_stream_async (void) +{ + GMainLoop *main_loop; + GError *error = NULL; + JsonParser *parser = json_parser_new (); + GFile *file = g_file_new_for_path (TESTS_DATA_DIR "/stream-load.json"); + GFileInputStream *stream = g_file_read (file, NULL, &error); + + g_assert (error == NULL); + g_assert (stream != NULL); + + main_loop = g_main_loop_new (NULL, FALSE); + + json_parser_load_from_stream_async (parser, G_INPUT_STREAM (stream), NULL, + on_load_complete, + main_loop); + + g_main_loop_run (main_loop); + + g_main_loop_unref (main_loop); + g_object_unref (stream); + g_object_unref (file); + g_object_unref (parser); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/parser/empty-string", test_empty); + g_test_add_func ("/parser/base-value", test_base_value); + g_test_add_func ("/parser/empty-array", test_empty_array); + g_test_add_func ("/parser/simple-array", test_simple_array); + g_test_add_func ("/parser/nested-array", test_nested_array); + g_test_add_func ("/parser/empty-object", test_empty_object); + g_test_add_func ("/parser/simple-object", test_simple_object); + g_test_add_func ("/parser/nested-object", test_nested_object); + g_test_add_func ("/parser/assignment", test_assignment); + g_test_add_func ("/parser/unicode-escape", test_unicode_escape); + g_test_add_func ("/parser/invalid-json", test_invalid_json); + g_test_add_func ("/parser/stream-sync", test_stream_sync); + g_test_add_func ("/parser/stream-async", test_stream_async); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/path.c b/json-glib/json-glib/tests/path.c new file mode 100644 index 0000000..67d2a87 --- /dev/null +++ b/json-glib/json-glib/tests/path.c @@ -0,0 +1,163 @@ +#include +#include +#include + +static const char *test_json = +"{ \"store\": {" +" \"book\": [ " +" { \"category\": \"reference\"," +" \"author\": \"Nigel Rees\"," +" \"title\": \"Sayings of the Century\"," +" \"price\": \"8.95\"" +" }," +" { \"category\": \"fiction\"," +" \"author\": \"Evelyn Waugh\"," +" \"title\": \"Sword of Honour\"," +" \"price\": \"12.99\"" +" }," +" { \"category\": \"fiction\"," +" \"author\": \"Herman Melville\"," +" \"title\": \"Moby Dick\"," +" \"isbn\": \"0-553-21311-3\"," +" \"price\": \"8.99\"" +" }," +" { \"category\": \"fiction\"," +" \"author\": \"J. R. R. Tolkien\"," +" \"title\": \"The Lord of the Rings\"," +" \"isbn\": \"0-395-19395-8\"," +" \"price\": \"22.99\"" +" }" +" ]," +" \"bicycle\": {" +" \"color\": \"red\"," +" \"price\": \"19.95\"" +" }" +" }" +"}"; + +static const struct { + const char *exp; + const char *res; +} test_expressions[] = { + { + "$.store.book[0].title", + "[\"Sayings of the Century\"]" + }, + { + "$['store']['book'][0]['title']", + "[\"Sayings of the Century\"]" + }, + { + "$.store.book[*].author", + "[\"Nigel Rees\",\"Evelyn Waugh\",\"Herman Melville\",\"J. R. R. Tolkien\"]" + }, + { + "$..author", + "[\"Nigel Rees\",\"Evelyn Waugh\",\"Herman Melville\",\"J. R. R. Tolkien\"]" + }, + { + "$.store.*", + NULL + }, + { + "$.store..price", + "[\"8.95\",\"12.99\",\"8.99\",\"22.99\",\"19.95\"]" + }, + { + "$..book[2]", + "[{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":\"8.99\"}]" + }, + { + "$..book[-1:]", + "[{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":\"22.99\"}]" + }, + { + "$..book[0,1]", + "[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":\"8.95\"},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":\"12.99\"}]" + }, + { + "$..book[:2]", + "[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":\"8.95\"},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":\"12.99\"}]" + }, +}; + +static void +test_expression (void) +{ + JsonPath *path = json_path_new (); + int i; + + for (i = 0; i < G_N_ELEMENTS (test_expressions); i++) + { + const char *expr = test_expressions[i].exp; + GError *error = NULL; + + g_assert (json_path_compile (path, expr, &error)); + g_assert_no_error (error); + } + + g_object_unref (path); +} + +static void +test_match (void) +{ + JsonParser *parser = json_parser_new (); + JsonGenerator *gen = json_generator_new (); + JsonPath *path = json_path_new (); + JsonNode *root; + int i; + + json_parser_load_from_data (parser, test_json, -1, NULL); + root = json_parser_get_root (parser); + + for (i = 0; i < G_N_ELEMENTS (test_expressions); i++) + { + const char *expr = test_expressions[i].exp; + const char *res = test_expressions[i].res; + JsonNode *matches; + char *str; + + if (res == NULL || *res == '\0') + continue; + + g_assert (json_path_compile (path, expr, NULL)); + + matches = json_path_match (path, root); + g_assert (JSON_NODE_HOLDS_ARRAY (matches)); + + json_generator_set_root (gen, matches); + str = json_generator_to_data (gen, NULL); + + if (g_test_verbose ()) + { + g_print ("* expr[%02d]: '%s' =>\n" + "- result: %s\n" + "- expected: %s\n", + i, expr, str, res); + } + + g_assert_cmpstr (str, ==, res); + + g_free (str); + json_node_free (matches); + } + + g_object_unref (parser); + g_object_unref (path); + g_object_unref (gen); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id="); + + g_test_add_func ("/path/expressions", test_expression); + g_test_add_func ("/path/match", test_match); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/reader.c b/json-glib/json-glib/tests/reader.c new file mode 100644 index 0000000..c884ca3 --- /dev/null +++ b/json-glib/json-glib/tests/reader.c @@ -0,0 +1,134 @@ +#include +#include + +#include + +#include + +static const gchar *test_base_array_data = +"[ 0, true, null, \"foo\", 3.14, [ false ], { \"bar\" : 42 } ]"; + +static const gchar *test_base_object_data = +"{ \"text\" : \"hello, world!\", \"foo\" : \"bar\", \"blah\" : 47 }"; + +static const gchar *expected_member_name[] = { + "text", + "foo", + "blah" +}; + +static void +test_base_object (void) +{ + JsonParser *parser = json_parser_new (); + JsonReader *reader = json_reader_new (NULL); + GError *error = NULL; + gchar **members; + gsize n_members, i; + + json_parser_load_from_data (parser, test_base_object_data, -1, &error); + g_assert (error == NULL); + + json_reader_set_root (reader, json_parser_get_root (parser)); + + g_assert (json_reader_is_object (reader)); + g_assert_cmpint (json_reader_count_members (reader), ==, 3); + + members = json_reader_list_members (reader); + g_assert (members != NULL); + + n_members = g_strv_length (members); + g_assert_cmpint (n_members, ==, json_reader_count_members (reader)); + + for (i = 0; i < n_members; i++) + g_assert_cmpstr (members[i], ==, expected_member_name[i]); + + g_strfreev (members); + + g_assert (json_reader_read_member (reader, "foo")); + g_assert (json_reader_is_value (reader)); + g_assert_cmpstr (json_reader_get_string_value (reader), ==, "bar"); + json_reader_end_member (reader); + + g_assert (!json_reader_read_member (reader, "bar")); + g_assert (json_reader_get_error (reader) != NULL); + g_assert_error ((GError *) json_reader_get_error (reader), + JSON_READER_ERROR, + JSON_READER_ERROR_INVALID_MEMBER); + json_reader_end_member (reader); + g_assert (json_reader_get_error (reader) == NULL); + + g_assert (json_reader_read_element (reader, 2)); + g_assert_cmpstr (json_reader_get_member_name (reader), ==, "blah"); + g_assert (json_reader_is_value (reader)); + g_assert_cmpint (json_reader_get_int_value (reader), ==, 47); + json_reader_end_element (reader); + g_assert (json_reader_get_error (reader) == NULL); + + g_object_unref (reader); + g_object_unref (parser); +} + +static void +test_base_array (void) +{ + JsonParser *parser = json_parser_new (); + JsonReader *reader = json_reader_new (NULL); + GError *error = NULL; + + json_parser_load_from_data (parser, test_base_array_data, -1, &error); + g_assert (error == NULL); + + json_reader_set_root (reader, json_parser_get_root (parser)); + + g_assert (json_reader_is_array (reader)); + g_assert_cmpint (json_reader_count_elements (reader), ==, 7); + + json_reader_read_element (reader, 0); + g_assert (json_reader_is_value (reader)); + g_assert_cmpint (json_reader_get_int_value (reader), ==, 0); + json_reader_end_element (reader); + + json_reader_read_element (reader, 3); + g_assert (json_reader_is_value (reader)); + g_assert_cmpstr (json_reader_get_string_value (reader), ==, "foo"); + json_reader_end_element (reader); + + json_reader_read_element (reader, 5); + g_assert (!json_reader_is_value (reader)); + g_assert (json_reader_is_array (reader)); + json_reader_end_element (reader); + + json_reader_read_element (reader, 6); + g_assert (json_reader_is_object (reader)); + + json_reader_read_member (reader, "bar"); + g_assert (json_reader_is_value (reader)); + g_assert_cmpint (json_reader_get_int_value (reader), ==, 42); + json_reader_end_member (reader); + + json_reader_end_element (reader); + + g_assert (!json_reader_read_element (reader, 7)); + g_assert_error ((GError *) json_reader_get_error (reader), + JSON_READER_ERROR, + JSON_READER_ERROR_INVALID_INDEX); + json_reader_end_element (reader); + g_assert (json_reader_get_error (reader) == NULL); + + g_object_unref (reader); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id="); + + g_test_add_func ("/reader/base-array", test_base_array); + g_test_add_func ("/reader/base-object", test_base_object); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/serialize-complex.c b/json-glib/json-glib/tests/serialize-complex.c new file mode 100644 index 0000000..4594fdb --- /dev/null +++ b/json-glib/json-glib/tests/serialize-complex.c @@ -0,0 +1,310 @@ +#include +#include +#include + +#include + +#include +#include + +#define TEST_TYPE_BOXED (test_boxed_get_type ()) +#define TEST_TYPE_OBJECT (test_object_get_type ()) +#define TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject)) +#define TEST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT)) +#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass)) +#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT)) +#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass)) + +typedef struct _TestBoxed TestBoxed; +typedef struct _TestObject TestObject; +typedef struct _TestObjectClass TestObjectClass; + +struct _TestBoxed +{ + gint foo; + gboolean bar; +}; + +struct _TestObject +{ + GObject parent_instance; + + gint foo; + gboolean bar; + gchar *baz; + TestBoxed blah; + gdouble meh; +}; + +struct _TestObjectClass +{ + GObjectClass parent_class; +}; + +GType test_object_get_type (void); + +/*** implementation ***/ + +static TestBoxed * +test_boxed_copy (const TestBoxed *src) +{ + TestBoxed *copy = g_slice_new (TestBoxed); + + *copy = *src; + + return copy; +} + +static void +test_boxed_free (TestBoxed *boxed) +{ + if (G_LIKELY (boxed)) + { + g_slice_free (TestBoxed, boxed); + } +} + +GType +test_boxed_get_type (void) +{ + static GType b_type = 0; + + if (G_UNLIKELY (b_type == 0)) + b_type = g_boxed_type_register_static ("TestBoxed", + (GBoxedCopyFunc) test_boxed_copy, + (GBoxedFreeFunc) test_boxed_free); + + return b_type; +} + +enum +{ + PROP_0, + + PROP_FOO, + PROP_BAR, + PROP_BAZ, + PROP_BLAH, + PROP_MEH +}; + +static JsonSerializableIface *serializable_iface = NULL; + +static void json_serializable_iface_init (gpointer g_iface); + +G_DEFINE_TYPE_WITH_CODE (TestObject, test_object, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE, + json_serializable_iface_init)); + +static JsonNode * +test_object_serialize_property (JsonSerializable *serializable, + const gchar *name, + const GValue *value, + GParamSpec *pspec) +{ + JsonNode *retval = NULL; + + if (strcmp (name, "blah") == 0) + { + TestBoxed *boxed; + JsonObject *obj; + JsonNode *val; + + retval = json_node_new (JSON_NODE_OBJECT); + obj = json_object_new (); + + boxed = g_value_get_boxed (value); + + val = json_node_new (JSON_NODE_VALUE); + json_node_set_int (val, boxed->foo); + json_object_set_member (obj, "foo", val); + + val = json_node_new (JSON_NODE_VALUE); + json_node_set_boolean (val, boxed->bar); + json_object_set_member (obj, "bar", val); + + json_node_take_object (retval, obj); + } + else + retval = serializable_iface->serialize_property (serializable, + name, + value, pspec); + + return retval; +} + +static void +json_serializable_iface_init (gpointer g_iface) +{ + JsonSerializableIface *iface = g_iface; + + serializable_iface = g_type_default_interface_peek (JSON_TYPE_SERIALIZABLE); + + iface->serialize_property = test_object_serialize_property; +} + +static void +test_object_finalize (GObject *gobject) +{ + g_free (TEST_OBJECT (gobject)->baz); + + G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject); +} + +static void +test_object_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_FOO: + TEST_OBJECT (gobject)->foo = g_value_get_int (value); + break; + case PROP_BAR: + TEST_OBJECT (gobject)->bar = g_value_get_boolean (value); + break; + case PROP_BAZ: + g_free (TEST_OBJECT (gobject)->baz); + TEST_OBJECT (gobject)->baz = g_value_dup_string (value); + break; + case PROP_MEH: + TEST_OBJECT (gobject)->meh = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +test_object_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_FOO: + g_value_set_int (value, TEST_OBJECT (gobject)->foo); + break; + case PROP_BAR: + g_value_set_boolean (value, TEST_OBJECT (gobject)->bar); + break; + case PROP_BAZ: + g_value_set_string (value, TEST_OBJECT (gobject)->baz); + break; + case PROP_BLAH: + g_value_set_boxed (value, &(TEST_OBJECT (gobject)->blah)); + break; + case PROP_MEH: + g_value_set_double (value, TEST_OBJECT (gobject)->meh); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +test_object_class_init (TestObjectClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = test_object_set_property; + gobject_class->get_property = test_object_get_property; + gobject_class->finalize = test_object_finalize; + + g_object_class_install_property (gobject_class, + PROP_FOO, + g_param_spec_int ("foo", "Foo", "Foo", + 0, G_MAXINT, 42, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_BAR, + g_param_spec_boolean ("bar", "Bar", "Bar", + TRUE, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_BAZ, + g_param_spec_string ("baz", "Baz", "Baz", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_BLAH, + g_param_spec_boxed ("blah", "Blah", "Blah", + TEST_TYPE_BOXED, + G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, + PROP_MEH, + g_param_spec_double ("meh", "Meh", "Meh", + 0.0, 1.0, 0.0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +test_object_init (TestObject *object) +{ + object->foo = 42; + object->bar = TRUE; + object->baz = g_strdup ("Test"); + object->meh = 0.0; + + object->blah.foo = object->foo; + object->blah.bar = object->bar; +} + +static void +test_serialize (void) +{ + TestObject *obj = g_object_new (TEST_TYPE_OBJECT, + "foo", 47, + "bar", FALSE, + "baz", "Hello, World!", + "meh", 0.5, + NULL); + JsonParser *parser = json_parser_new (); + GError *error = NULL; + JsonObject *object; + JsonNode *node; + gchar *data; + gsize len; + + data = json_gobject_to_data (G_OBJECT (obj), &len); + + g_assert_cmpint (len, >, 0); + if (g_test_verbose ()) + g_print ("TestObject:\n%s\n", data); + + parser = json_parser_new (); + json_parser_load_from_data (parser, data, -1, &error); + g_assert (error == NULL); + + node = json_parser_get_root (parser); + g_assert (json_node_get_node_type (node) == JSON_NODE_OBJECT); + + object = json_node_get_object (node); + g_assert_cmpint (json_object_get_int_member (object, "foo"), ==, 47); + g_assert (!json_object_get_boolean_member (object, "bar")); + g_assert_cmpstr (json_object_get_string_member (object, "baz"), ==, "Hello, World!"); + g_assert_cmpfloat (json_object_get_double_member (object, "meh"), ==, 0.5); + + /* blah is read-only */ + g_assert (json_object_has_member (object, "blah")); + + g_free (data); + g_object_unref (parser); + g_object_unref (obj); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/serialize/gobject-boxed", test_serialize); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/serialize-full.c b/json-glib/json-glib/tests/serialize-full.c new file mode 100644 index 0000000..69280d4 --- /dev/null +++ b/json-glib/json-glib/tests/serialize-full.c @@ -0,0 +1,403 @@ +#include +#include +#include + +#include + +#include +#include + +#define TEST_TYPE_ENUM (test_enum_get_type ()) +#define TEST_TYPE_BOXED (test_boxed_get_type ()) +#define TEST_TYPE_OBJECT (test_object_get_type ()) +#define TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject)) +#define TEST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT)) +#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass)) +#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT)) +#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass)) + +typedef enum { + TEST_ENUM_FOO, + TEST_ENUM_BAR, + TEST_ENUM_BAZ +} TestEnum; + +typedef struct _TestBoxed TestBoxed; +typedef struct _TestObject TestObject; +typedef struct _TestObjectClass TestObjectClass; + +struct _TestBoxed +{ + gint foo; + gboolean bar; +}; + +struct _TestObject +{ + GObject parent_instance; + + gint foo; + gboolean bar; + gchar *baz; + TestBoxed blah; + TestEnum meh; + gchar **mah; + + TestObject *test; +}; + +struct _TestObjectClass +{ + GObjectClass parent_class; +}; + +GType test_object_get_type (void); + +/*** implementation ***/ + +static TestBoxed * +test_boxed_copy (const TestBoxed *src) +{ + TestBoxed *copy = g_slice_new (TestBoxed); + + *copy = *src; + + return copy; +} + +static void +test_boxed_free (TestBoxed *boxed) +{ + if (G_LIKELY (boxed)) + { + g_slice_free (TestBoxed, boxed); + } +} + +GType +test_boxed_get_type (void) +{ + static GType b_type = 0; + + if (G_UNLIKELY (b_type == 0)) + b_type = g_boxed_type_register_static ("TestBoxed", + (GBoxedCopyFunc) test_boxed_copy, + (GBoxedFreeFunc) test_boxed_free); + + return b_type; +} + +GType +test_enum_get_type (void) +{ + static GType e_type = 0; + + if (G_UNLIKELY (e_type == 0)) + { + static const GEnumValue values[] = { + { TEST_ENUM_FOO, "TEST_ENUM_FOO", "foo" }, + { TEST_ENUM_BAR, "TEST_ENUM_BAR", "bar" }, + { TEST_ENUM_BAZ, "TEST_ENUM_BAZ", "baz" }, + { 0, NULL, NULL } + }; + + e_type = g_enum_register_static ("TestEnum", values); + } + + return e_type; +} + +enum +{ + PROP_0, + + PROP_FOO, + PROP_BAR, + PROP_BAZ, + PROP_BLAH, + PROP_MEH, + PROP_MAH, + PROP_TEST +}; + +static void json_serializable_iface_init (gpointer g_iface); + +G_DEFINE_TYPE_WITH_CODE (TestObject, test_object, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE, + json_serializable_iface_init)); + +static JsonNode * +test_object_serialize_property (JsonSerializable *serializable, + const gchar *name, + const GValue *value, + GParamSpec *pspec) +{ + JsonNode *retval; + + if (strcmp (name, "blah") == 0) + { + TestBoxed *boxed; + JsonObject *obj; + + retval = json_node_new (JSON_NODE_OBJECT); + obj = json_object_new (); + + boxed = g_value_get_boxed (value); + + json_object_set_int_member (obj, "foo", boxed->foo); + json_object_set_boolean_member (obj, "bar", boxed->bar); + + json_node_take_object (retval, obj); + + test_boxed_free (boxed); + } + else + { + GValue copy = { 0, }; + + retval = json_node_new (JSON_NODE_VALUE); + + g_value_init (©, G_PARAM_SPEC_VALUE_TYPE (pspec)); + g_value_copy (value, ©); + json_node_set_value (retval, ©); + g_value_unset (©); + } + + return retval; +} + +static void +json_serializable_iface_init (gpointer g_iface) +{ + JsonSerializableIface *iface = g_iface; + + iface->serialize_property = test_object_serialize_property; +} + +static void +test_object_finalize (GObject *gobject) +{ + g_free (TEST_OBJECT (gobject)->baz); + g_strfreev (TEST_OBJECT (gobject)->mah); + + if (TEST_OBJECT (gobject)->test != NULL) + g_object_unref (TEST_OBJECT (gobject)->test); + + G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject); +} + +static void +test_object_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_FOO: + TEST_OBJECT (gobject)->foo = g_value_get_int (value); + break; + + case PROP_BAR: + TEST_OBJECT (gobject)->bar = g_value_get_boolean (value); + break; + + case PROP_BAZ: + g_free (TEST_OBJECT (gobject)->baz); + TEST_OBJECT (gobject)->baz = g_value_dup_string (value); + break; + + case PROP_MEH: + TEST_OBJECT (gobject)->meh = g_value_get_enum (value); + break; + + case PROP_MAH: + TEST_OBJECT (gobject)->mah = g_strdupv (g_value_get_boxed (value)); + break; + + case PROP_TEST: + TEST_OBJECT (gobject)->test = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +test_object_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_FOO: + g_value_set_int (value, TEST_OBJECT (gobject)->foo); + break; + + case PROP_BAR: + g_value_set_boolean (value, TEST_OBJECT (gobject)->bar); + break; + + case PROP_BAZ: + g_value_set_string (value, TEST_OBJECT (gobject)->baz); + break; + + case PROP_BLAH: + g_value_set_boxed (value, &(TEST_OBJECT (gobject)->blah)); + break; + + case PROP_MEH: + g_value_set_enum (value, TEST_OBJECT (gobject)->meh); + break; + + case PROP_MAH: + g_value_set_boxed (value, TEST_OBJECT (gobject)->mah); + break; + + case PROP_TEST: + g_value_set_object (value, TEST_OBJECT (gobject)->test); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +test_object_class_init (TestObjectClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = test_object_set_property; + gobject_class->get_property = test_object_get_property; + gobject_class->finalize = test_object_finalize; + + g_object_class_install_property (gobject_class, + PROP_FOO, + g_param_spec_int ("foo", "Foo", "Foo", + 0, G_MAXINT, 42, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_BAR, + g_param_spec_boolean ("bar", "Bar", "Bar", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (gobject_class, + PROP_BAZ, + g_param_spec_string ("baz", "Baz", "Baz", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_BLAH, + g_param_spec_boxed ("blah", "Blah", "Blah", + TEST_TYPE_BOXED, + G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, + PROP_MEH, + g_param_spec_enum ("meh", "Meh", "Meh", + TEST_TYPE_ENUM, + TEST_ENUM_BAR, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, + PROP_MAH, + g_param_spec_boxed ("mah", "Mah", "Mah", + G_TYPE_STRV, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_TEST, + g_param_spec_object ("test", "Test", "Test", + TEST_TYPE_OBJECT, + G_PARAM_READWRITE)); +} + +static void +test_object_init (TestObject *object) +{ + object->foo = 0; + object->bar = FALSE; + object->baz = NULL; + + object->blah.foo = object->foo; + object->blah.bar = object->bar; + + object->meh = TEST_ENUM_BAR; + + object->mah = NULL; + + object->test = NULL; +} + +static const gchar *var_test = +"{\n" +" \"foo\" : 42,\n" +" \"bar\" : true,\n" +" \"baz\" : \"hello\",\n" +" \"meh\" : \"baz\",\n" +" \"mah\" : [ \"hello\", \", \", \"world\", \"!\" ],\n" +" \"test\" : {\n" +" \"bar\" : true,\n" +" \"baz\" : \"world\",\n" +" \"meh\" : \"foo\"\n" +" }\n" +"}"; + +static void +test_deserialize (void) +{ + TestObject *test; + GObject *object; + GError *error; + gchar *str; + + error = NULL; + object = json_gobject_from_data (TEST_TYPE_OBJECT, var_test, -1, &error); + if (error) + g_error ("*** Unable to parse buffer: %s\n", error->message); + + if (g_test_verbose ()) + g_print ("*** TestObject ***\n" + " foo: %s\n" + " bar: %s\n" + " baz: %s\n" + " meh: %s\n", + TEST_OBJECT (object)->foo == 42 ? "" : "", + TEST_OBJECT (object)->bar == TRUE ? "" : "", + TEST_OBJECT (object)->baz != NULL ? "" : "", + TEST_OBJECT (object)->meh == TEST_ENUM_BAZ ? "" : ""); + + g_assert_cmpint (TEST_OBJECT (object)->foo, ==, 42); + g_assert (TEST_OBJECT (object)->bar); + g_assert_cmpstr (TEST_OBJECT (object)->baz, ==, "hello"); + g_assert_cmpint (TEST_OBJECT (object)->meh, ==, TEST_ENUM_BAZ); + + g_assert (TEST_OBJECT (object)->mah != NULL); + g_assert_cmpint (g_strv_length (TEST_OBJECT (object)->mah), ==, 4); + + str = g_strjoinv (NULL, TEST_OBJECT (object)->mah); + g_assert_cmpstr (str, ==, "hello, world!"); + g_free (str); + + g_assert (TEST_IS_OBJECT (TEST_OBJECT (object)->test)); + test = TEST_OBJECT (TEST_OBJECT (object)->test); + g_assert (test->bar); + g_assert_cmpstr (test->baz, ==, "world"); + g_assert_cmpint (test->meh, ==, TEST_ENUM_FOO); + + g_object_unref (object); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/deserialize/json-to-gobject", test_deserialize); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/serialize-simple.c b/json-glib/json-glib/tests/serialize-simple.c new file mode 100644 index 0000000..42af6fb --- /dev/null +++ b/json-glib/json-glib/tests/serialize-simple.c @@ -0,0 +1,166 @@ +#include +#include +#include + +#include + +#include +#include + +#define TEST_TYPE_OBJECT (test_object_get_type ()) +#define TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject)) +#define TEST_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT)) +#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass)) +#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT)) +#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass)) + +typedef struct _TestObject TestObject; +typedef struct _TestObjectClass TestObjectClass; + +struct _TestObject +{ + GObject parent_instance; + + gint foo; + gboolean bar; + gchar *baz; +}; + +struct _TestObjectClass +{ + GObjectClass parent_class; +}; + +GType test_object_get_type (void); + +/*** implementation ***/ + +enum +{ + PROP_0, + + PROP_FOO, + PROP_BAR, + PROP_BAZ +}; + +G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT); + +static void +test_object_finalize (GObject *gobject) +{ + g_free (TEST_OBJECT (gobject)->baz); + + G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject); +} + +static void +test_object_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_FOO: + TEST_OBJECT (gobject)->foo = g_value_get_int (value); + break; + case PROP_BAR: + TEST_OBJECT (gobject)->bar = g_value_get_boolean (value); + break; + case PROP_BAZ: + g_free (TEST_OBJECT (gobject)->baz); + TEST_OBJECT (gobject)->baz = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +test_object_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_FOO: + g_value_set_int (value, TEST_OBJECT (gobject)->foo); + break; + case PROP_BAR: + g_value_set_boolean (value, TEST_OBJECT (gobject)->bar); + break; + case PROP_BAZ: + g_value_set_string (value, TEST_OBJECT (gobject)->baz); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +} + +static void +test_object_class_init (TestObjectClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = test_object_set_property; + gobject_class->get_property = test_object_get_property; + gobject_class->finalize = test_object_finalize; + + g_object_class_install_property (gobject_class, + PROP_FOO, + g_param_spec_int ("foo", "Foo", "Foo", + 0, G_MAXINT, 42, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_BAR, + g_param_spec_boolean ("bar", "Bar", "Bar", + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_BAZ, + g_param_spec_string ("baz", "Baz", "Baz", + NULL, + G_PARAM_READWRITE)); +} + +static void +test_object_init (TestObject *object) +{ + object->foo = 42; + object->bar = FALSE; + object->baz = g_strdup ("Test"); +} + +static void +test_serialize (void) +{ + TestObject *obj = g_object_new (TEST_TYPE_OBJECT, "bar", TRUE, NULL); + gchar *data; + gsize len; + + data = json_gobject_to_data (G_OBJECT (obj), &len); + + g_assert (data != NULL); + g_assert_cmpint (len, >, 0); + g_assert_cmpint (len, ==, strlen (data)); + + if (g_test_verbose ()) + g_print ("TestObject:\n%s\n", data); + + g_free (data); + g_object_unref (obj); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/serialize/gobject", test_serialize); + + return g_test_run (); +} diff --git a/json-glib/json-glib/tests/stream-load.json b/json-glib/json-glib/tests/stream-load.json new file mode 100644 index 0000000..203f03f --- /dev/null +++ b/json-glib/json-glib/tests/stream-load.json @@ -0,0 +1 @@ +[ { "hello" : "world!\n" } ] diff --git a/lib/Makefile.am b/lib/Makefile.am index c1f2909..d83b407 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -19,8 +19,9 @@ generated_sources = fcall-impr.h searpc-fcall.h searpc-dfun.h \ # rpc_headers = fcall-impr.h searpc-fcall.h searpc-dfun.h searpc-signature.h searpc-marshal.h -AM_CFLAGS = @GLIB2_CFLAGS@ @GOBJECT_CFLAGS@ @JSON_GLIB_CFLAGS@ \ +AM_CFLAGS = @GLIB_CFLAGS@ \ -I${top_builddir}/lib \ + -I${top_srcdir}/json-glib \ -I${top_srcdir}/lib \ -DG_LOG_DOMAIN=\"Searpc\" @@ -38,7 +39,8 @@ libsearpc_la_SOURCES = searpc-client.c searpc-server.c $(generated_sources) libsearpc_la_LDFLAGS = -version-info 1:1:0 -no-undefined -libsearpc_la_LIBADD = @GLIB2_LIBS@ @GOBJECT_LIBS@ @JSON_GLIB_LIBS@ +libsearpc_la_LIBADD = @GLIB_LIBS@ \ + ${top_builddir}/json-glib/json-glib/libjson-glib.la genrpc_files = gencode.py rpc_table.py diff --git a/lib/searpc-client.c b/lib/searpc-client.c index adfe829..6542caf 100644 --- a/lib/searpc-client.c +++ b/lib/searpc-client.c @@ -2,7 +2,7 @@ #include #include -#include +#include "json-glib/json-glib.h" #include "searpc-client.h" #include "searpc-utils.h" diff --git a/lib/searpc-server.c b/lib/searpc-server.c index 2a6816a..2c02f8d 100644 --- a/lib/searpc-server.c +++ b/lib/searpc-server.c @@ -5,7 +5,7 @@ #include #include -#include +#include "json-glib/json-glib.h" #include "searpc-server.h" #include "searpc-utils.h" diff --git a/lib/searpc-utils.h b/lib/searpc-utils.h index 388568c..215663f 100644 --- a/lib/searpc-utils.h +++ b/lib/searpc-utils.h @@ -1,7 +1,7 @@ #ifndef SEARPC_UTILS_H #define SEARPC_UTILS_H -#include +#include "json-glib/json-glib.h" inline static const gchar * diff --git a/libsearpc.pc.in b/libsearpc.pc.in new file mode 100644 index 0000000..0a43bcd --- /dev/null +++ b/libsearpc.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsearpc +Description: Simple C rpc library +Version: @VERSION@ +Libs: -L${libdir} -lsearpc +Cflags: -I${includedir}/searpc +Requires: gobject-2.0 gio-2.0 diff --git a/pysearpc/Makefile.am b/pysearpc/Makefile.am index e9f5afa..0a0b508 100644 --- a/pysearpc/Makefile.am +++ b/pysearpc/Makefile.am @@ -3,7 +3,7 @@ genrpc_files = pygencode.py rpc_table.py AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \ -DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \ - @GLIB2_CFLAGS@ \ + @GLIB_CFLAGS@ \ -I/usr/include/python$(PYTHON_VERSION) \ -I${top_builddir}/lib \ -I${top_srcdir}/lib @@ -20,7 +20,7 @@ pysearpc_LTLIBRARIES = fcallfret.la fcallfret_la_LDFLAGS = -module -avoid-version -export-symbols-regex initfcallfret -no-undefined @SERVER_PKG_RPATH@ fcallfret_la_SOURCES = fcallfret.c fcallfret.h -fcallfret_la_LIBADD = @GLIB2_LIBS@ @GOBJECT_LIBS@ $(top_builddir)/lib/libsearpc.la @LIB_PYTHON@ +fcallfret_la_LIBADD = @GLIB_LIBS@ $(top_builddir)/lib/libsearpc.la @LIB_PYTHON@ EXTRA_DIST = ${genrpc_files} diff --git a/pysearpc/client.py b/pysearpc/client.py index 842c738..3a16c21 100644 --- a/pysearpc/client.py +++ b/pysearpc/client.py @@ -67,7 +67,7 @@ def _fret_objlist(ret_str): return l -def searpc_func(ret_type, param_types, ret_obj_class=None): +def searpc_func(ret_type, param_types): def decorate(func): if len(param_types) == 0: fcall = getattr(fcallfret, 'fcall__void') diff --git a/tests/Makefile.am b/tests/Makefile.am index 5bd1676..c5de671 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,12 +1,11 @@ -AM_CFLAGS = @GLIB2_CFLAGS@ @GOBJECT_CFLAGS@ @JSON_GLIB_CFLAGS@ \ - -I${top_builddir}/lib -I${top_srcdir}/lib - +AM_CFLAGS = @GLIB_CFLAGS@ \ + -I${top_srcdir}/lib check_PROGRAMS = test-searpc test_searpc_SOURCES = test-searpc.c -test_searpc_LDADD = @GLIB2_LIBS@ @GOBJECT_LIBS@ \ +test_searpc_LDADD = @GLIB_LIBS@ \ $(top_builddir)/lib/libsearpc.la test_searpc_LDFLAGS = -static