commit c1a601ff3296bea8f9e037f2cd9ca499cf0219ab
Author: locutus
Date: Fri Jul 22 23:00:34 2011 -0400
built against CB 1000
Signed-off-by: locutus
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2e706a0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+# Eclipse stuff
+/.classpath
+/.project
+/.settings
+
+# netbeans
+/nbproject
+
+# vim
+.*.sw[a-p]
+
+# various other potential build files
+/build
+/bin
+/dist
+/localexport.jardesc
+
+# Mac filesystem dust
+.DS_Store
\ No newline at end of file
diff --git a/AncientGates.jar b/AncientGates.jar
new file mode 100644
index 0000000..eac321b
Binary files /dev/null and b/AncientGates.jar differ
diff --git a/LGPL.txt b/LGPL.txt
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/LGPL.txt
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ 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 that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU 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 as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/LICENCE.txt b/LICENCE.txt
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/LICENCE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state 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 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..05da517
--- /dev/null
+++ b/README.md
@@ -0,0 +1,46 @@
+AncientGates - Easily create portals with custom design
+====================
+Read the full userguide here: [http://mcteam.org/ancient-gates](http://mcteam.org/ancient-gates)
+
+With this plugin the server operators (OPs) can create gates that will teleport anyone who enter to a location specific to that gate. The hightlights are: It is so darn easy to use! :D and The gates can look any way you like \o/
+
+Try the ingame command: /gate
+Thought first you should take a look at the demonstration I and karibu6 created:
+[http://www.youtube.com/watch?v=L4hyqTpeEaA](http://www.youtube.com/watch?v=L4hyqTpeEaA)
+
+Commands
+---------
+
+* /gate help,h,? *[page] Display a help page
+* /gate create,new [id] Create a gate
+* /gate delete,del,remove [id] Delete a gate
+* /gate setfrom [id] Set "from" to your location.
+* /gate setto [id] Set "to" to your location.
+* /gate open [id] Open that gate
+* /gate close [id] Close that gate
+* /gate list,ls Display a list of the gates
+
+FAQ
+---------
+
+Why the name AncientGates?
+Because the purpose of this plugin that I envision is that players should not be able to create gates to other worlds themselves. The server operators build gates with cool ruins around them and in order to get to another world the players need to find one of those ruins.
+
+Who can create a gate?
+Only server operators! Not normal players. There is no suport for any permissions plugin.
+
+Who can destroy a gate?
+Anyone if you do not use a third-party protection plugin like Factions.
+
+Are there IConnomy integration, Features for user to dial other gates etc?
+Nope. This plugin is very minimalistic and plain. Server operators manage the portals players use them any time they are open.
+
+Installing
+----------
+1. Download the latest release: [https://github.com/oloflarsson/minecraft-ancient-gates/downloads](https://github.com/oloflarsson/minecraft-ancient-gates/downloads)
+1. Put AncientGates.jar in the plugins folder.
+
+License
+----------
+This project has a LGPL license just like the Bukkit project.
+This project uses [GSON](http://code.google.com/p/google-gson/) which has a [Apache 2.0 license](http://www.apache.org/licenses/LICENSE-2.0 ).
\ No newline at end of file
diff --git a/gson-license.txt b/gson-license.txt
new file mode 100644
index 0000000..634d1aa
--- /dev/null
+++ b/gson-license.txt
@@ -0,0 +1,13 @@
+Copyright (c) 2008-2009 Google Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/plugin.yml b/plugin.yml
new file mode 100644
index 0000000..5a3ab39
--- /dev/null
+++ b/plugin.yml
@@ -0,0 +1,7 @@
+name: AncientGates
+version: 1.0.1
+main: org.mcteam.ancientgates.Plugin
+commands:
+ gate:
+ description: All of the AncientGates commands
+ usage: See documentation.
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/Conf.java b/src/org/mcteam/ancientgates/Conf.java
new file mode 100644
index 0000000..b64edc3
--- /dev/null
+++ b/src/org/mcteam/ancientgates/Conf.java
@@ -0,0 +1,75 @@
+package org.mcteam.ancientgates;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.bukkit.ChatColor;
+import org.mcteam.ancientgates.util.DiscUtil;
+
+
+public class Conf {
+ public static transient File file = new File(Plugin.instance.getDataFolder(), "conf.json");
+
+ // Colors
+ public static ChatColor colorMember = ChatColor.GREEN;
+ public static ChatColor colorAlly = ChatColor.LIGHT_PURPLE;
+ public static ChatColor colorNeutral = ChatColor.WHITE;
+ public static ChatColor colorEnemy = ChatColor.RED;
+
+ public static ChatColor colorSystem = ChatColor.YELLOW;
+ public static ChatColor colorChrome = ChatColor.GOLD;
+ public static ChatColor colorCommand = ChatColor.AQUA;
+ public static ChatColor colorParameter = ChatColor.DARK_AQUA;
+
+ private static double gateSearchRadius = 7.0;
+
+ static {
+
+ }
+
+ public static double getGateSearchRadius() {
+ return gateSearchRadius;
+ }
+
+ public static int getGateMaxArea() {
+ return (int)gateSearchRadius*10;
+ }
+
+ // -------------------------------------------- //
+ // Persistance
+ // -------------------------------------------- //
+
+ public static boolean save() {
+ //Factions.log("Saving config to disk.");
+
+ try {
+ DiscUtil.write(file, Plugin.gson.toJson(new Conf()));
+ } catch (IOException e) {
+ e.printStackTrace();
+ Plugin.log("Failed to save the config to disk.");
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean load() {
+ Plugin.log("Loading conf from disk");
+
+ if ( ! file.exists()) {
+ Plugin.log("No conf to load from disk. Creating new file.");
+ save();
+ return true;
+ }
+
+ try {
+ Plugin.gson.fromJson(DiscUtil.read(file), Conf.class);
+ } catch (IOException e) {
+ e.printStackTrace();
+ Plugin.log("Failed to load the config from disk.");
+ return false;
+ }
+
+ return true;
+ }
+}
+
diff --git a/src/org/mcteam/ancientgates/Gate.java b/src/org/mcteam/ancientgates/Gate.java
new file mode 100644
index 0000000..0093390
--- /dev/null
+++ b/src/org/mcteam/ancientgates/Gate.java
@@ -0,0 +1,166 @@
+package org.mcteam.ancientgates;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Map.Entry;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.mcteam.ancientgates.gson.reflect.TypeToken;
+import org.mcteam.ancientgates.util.DiscUtil;
+import org.mcteam.ancientgates.util.FloodUtil;
+
+
+public class Gate {
+ private static transient TreeMap instances = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+ private static transient File file = new File(Plugin.instance.getDataFolder(), "gates.json");
+
+ private transient String id;
+ private Location from;
+ private Location to;
+
+ public Gate() {
+
+ }
+
+ // -------------------------------------------- //
+ // Getters And Setters
+ // -------------------------------------------- //
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setFrom(Location from) {
+ this.from = from;
+ }
+
+ public Location getFrom() {
+ return from;
+ }
+
+ public void setTo(Location to) {
+ this.to = to;
+ }
+
+ public Location getTo() {
+ return to;
+ }
+
+ //----------------------------------------------//
+ // The Open And Close Methods
+ //----------------------------------------------//
+
+ public boolean open() {
+ Set blocks = FloodUtil.getGateFrameBlocks(from.getBlock());
+
+ if (blocks == null) {
+ return false;
+ }
+
+ // This is not to do an effect
+ // It is to stop portalblocks from destroyingthemself as they cant rely on non created blocks :P
+ for (Block block : blocks) {
+ block.setType(Material.GLOWSTONE);
+ }
+
+ for (Block block : blocks) {
+ block.setType(Material.PORTAL);
+ }
+
+ return true;
+ }
+
+ public void close() {
+ Set blocks = FloodUtil.getGateFrameBlocks(from.getBlock());
+
+ for (Block block : blocks) {
+ block.setType(Material.AIR);
+ }
+ }
+
+ //----------------------------------------------//
+ // Persistance and entity management
+ //----------------------------------------------//
+
+ public static Gate get(String id) {
+ return instances.get(id);
+ }
+
+ public static boolean exists(String id) {
+ return instances.containsKey(id);
+ }
+
+ public static Gate create(String id) {
+ Gate gate = new Gate();
+ gate.id = id;
+ instances.put(gate.id, gate);
+ Plugin.log("created new gate "+gate.id);
+ //faction.save();
+ return gate;
+ }
+
+ public static void delete(String id) {
+ // Remove the faction
+ instances.remove(id);
+ }
+
+ public static boolean save() {
+ try {
+ DiscUtil.write(file, Plugin.gson.toJson(instances));
+ } catch (IOException e) {
+ Plugin.log("Failed to save the gates to disk due to I/O exception.");
+ e.printStackTrace();
+ return false;
+ } catch (NullPointerException e) {
+ Plugin.log("Failed to save the gates to disk due to NPE.");
+ e.printStackTrace();
+ return false;
+ }
+
+ return true;
+ }
+
+ public static boolean load() {
+ Plugin.log("Loading gates from disk");
+ if ( ! file.exists()) {
+ Plugin.log("No gates to load from disk. Creating new file.");
+ save();
+ return true;
+ }
+
+ try {
+ Type type = new TypeToken
+ *
+ * @author Joel Leitch
+ */
+class DelegatingJsonElementVisitor implements JsonElementVisitor {
+ private final JsonElementVisitor delegate;
+
+ protected DelegatingJsonElementVisitor(JsonElementVisitor delegate) {
+ Preconditions.checkNotNull(delegate);
+ this.delegate = delegate;
+ }
+
+ public void endArray(JsonArray array) throws IOException {
+ delegate.endArray(array);
+ }
+
+ public void endObject(JsonObject object) throws IOException {
+ delegate.endObject(object);
+ }
+
+ public void startArray(JsonArray array) throws IOException {
+ delegate.startArray(array);
+ }
+
+ public void startObject(JsonObject object) throws IOException {
+ delegate.startObject(object);
+ }
+
+ public void visitArrayMember(JsonArray parent, JsonPrimitive member,
+ boolean isFirst) throws IOException {
+ delegate.visitArrayMember(parent, member, isFirst);
+ }
+
+ public void visitArrayMember(JsonArray parent, JsonArray member,
+ boolean isFirst) throws IOException {
+ delegate.visitArrayMember(parent, member, isFirst);
+ }
+
+ public void visitArrayMember(JsonArray parent, JsonObject member,
+ boolean isFirst) throws IOException {
+ delegate.visitArrayMember(parent, member, isFirst);
+ }
+
+ public void visitObjectMember(JsonObject parent, String memberName, JsonPrimitive member,
+ boolean isFirst) throws IOException {
+ delegate.visitObjectMember(parent, memberName, member, isFirst);
+ }
+
+ public void visitObjectMember(JsonObject parent, String memberName, JsonArray member,
+ boolean isFirst) throws IOException {
+ delegate.visitObjectMember(parent, memberName, member, isFirst);
+ }
+
+ public void visitObjectMember(JsonObject parent, String memberName, JsonObject member,
+ boolean isFirst) throws IOException {
+ delegate.visitObjectMember(parent, memberName, member, isFirst);
+ }
+
+ public void visitNullObjectMember(JsonObject parent, String memberName,
+ boolean isFirst) throws IOException {
+ delegate.visitNullObjectMember(parent, memberName, isFirst);
+ }
+
+ public void visitPrimitive(JsonPrimitive primitive) throws IOException {
+ delegate.visitPrimitive(primitive);
+ }
+
+ public void visitNull() throws IOException {
+ delegate.visitNull();
+ }
+
+ public void visitNullArrayMember(JsonArray parent, boolean isFirst) throws IOException {
+ delegate.visitNullArrayMember(parent, isFirst);
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/DisjunctionExclusionStrategy.java b/src/org/mcteam/ancientgates/gson/DisjunctionExclusionStrategy.java
new file mode 100644
index 0000000..d9bf693
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/DisjunctionExclusionStrategy.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.util.Collection;
+
+/**
+ * A wrapper class used to collect numerous {@link ExclusionStrategy} objects
+ * and perform a short-circuited OR operation.
+ *
+ * @author Joel Leitch
+ */
+final class DisjunctionExclusionStrategy implements ExclusionStrategy {
+ private final Collection strategies;
+
+ public DisjunctionExclusionStrategy(Collection strategies) {
+ Preconditions.checkNotNull(strategies);
+ this.strategies = strategies;
+ }
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ for (ExclusionStrategy strategy : strategies) {
+ if (strategy.shouldSkipField(f)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean shouldSkipClass(Class> clazz) {
+ for (ExclusionStrategy strategy : strategies) {
+ if (strategy.shouldSkipClass(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/Escaper.java b/src/org/mcteam/ancientgates/gson/Escaper.java
new file mode 100644
index 0000000..4f0d33f
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/Escaper.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A utility class that is used to perform JSON escaping so that ", <, >, etc. characters are
+ * properly encoded in the JSON string representation before returning to the client code.
+ *
+ *
This class contains a single method to escape a passed in string value:
+ *
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class Escaper {
+
+ private static final char[] HEX_CHARS = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+
+ private static final Set JS_ESCAPE_CHARS;
+ private static final Set HTML_ESCAPE_CHARS;
+
+ static {
+ Set mandatoryEscapeSet = new HashSet();
+ mandatoryEscapeSet.add('"');
+ mandatoryEscapeSet.add('\\');
+ JS_ESCAPE_CHARS = Collections.unmodifiableSet(mandatoryEscapeSet);
+
+ Set htmlEscapeSet = new HashSet();
+ htmlEscapeSet.add('<');
+ htmlEscapeSet.add('>');
+ htmlEscapeSet.add('&');
+ htmlEscapeSet.add('=');
+ htmlEscapeSet.add('\'');
+// htmlEscapeSet.add('/'); -- Removing slash for now since it causes some incompatibilities
+ HTML_ESCAPE_CHARS = Collections.unmodifiableSet(htmlEscapeSet);
+ }
+
+ private final boolean escapeHtmlCharacters;
+
+ Escaper(boolean escapeHtmlCharacters) {
+ this.escapeHtmlCharacters = escapeHtmlCharacters;
+ }
+
+ public String escapeJsonString(CharSequence plainText) {
+ StringBuffer escapedString = new StringBuffer(plainText.length() + 20);
+ try {
+ escapeJsonString(plainText, escapedString);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return escapedString.toString();
+ }
+
+ private void escapeJsonString(CharSequence plainText, StringBuffer out) throws IOException {
+ int pos = 0; // Index just past the last char in plainText written to out.
+ int len = plainText.length();
+
+ for (int charCount, i = 0; i < len; i += charCount) {
+ int codePoint = Character.codePointAt(plainText, i);
+ charCount = Character.charCount(codePoint);
+
+ if (!isControlCharacter(codePoint) && !mustEscapeCharInJsString(codePoint)) {
+ continue;
+ }
+
+ out.append(plainText, pos, i);
+ pos = i + charCount;
+ switch (codePoint) {
+ case '\b':
+ out.append("\\b");
+ break;
+ case '\t':
+ out.append("\\t");
+ break;
+ case '\n':
+ out.append("\\n");
+ break;
+ case '\f':
+ out.append("\\f");
+ break;
+ case '\r':
+ out.append("\\r");
+ break;
+ case '\\':
+ out.append("\\\\");
+ break;
+ case '/':
+ out.append("\\/");
+ break;
+ case '"':
+ out.append("\\\"");
+ break;
+ default:
+ appendHexJavaScriptRepresentation(codePoint, out);
+ break;
+ }
+ }
+ out.append(plainText, pos, len);
+ }
+
+ private boolean mustEscapeCharInJsString(int codepoint) {
+ if (!Character.isSupplementaryCodePoint(codepoint)) {
+ char c = (char) codepoint;
+ return JS_ESCAPE_CHARS.contains(c)
+ || (escapeHtmlCharacters && HTML_ESCAPE_CHARS.contains(c));
+ }
+ return false;
+ }
+
+ private static boolean isControlCharacter(int codePoint) {
+ // JSON spec defines these code points as control characters, so they must be escaped
+ return codePoint < 0x20
+ || codePoint == 0x2028 // Line separator
+ || codePoint == 0x2029 // Paragraph separator
+ || (codePoint >= 0x7f && codePoint <= 0x9f);
+ }
+
+ private static void appendHexJavaScriptRepresentation(int codePoint, Appendable out)
+ throws IOException {
+ if (Character.isSupplementaryCodePoint(codePoint)) {
+ // Handle supplementary unicode values which are not representable in
+ // javascript. We deal with these by escaping them as two 4B sequences
+ // so that they will round-trip properly when sent from java to javascript
+ // and back.
+ char[] surrogates = Character.toChars(codePoint);
+ appendHexJavaScriptRepresentation(surrogates[0], out);
+ appendHexJavaScriptRepresentation(surrogates[1], out);
+ return;
+ }
+ out.append("\\u")
+ .append(HEX_CHARS[(codePoint >>> 12) & 0xf])
+ .append(HEX_CHARS[(codePoint >>> 8) & 0xf])
+ .append(HEX_CHARS[(codePoint >>> 4) & 0xf])
+ .append(HEX_CHARS[codePoint & 0xf]);
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/ExclusionStrategy.java b/src/org/mcteam/ancientgates/gson/ExclusionStrategy.java
new file mode 100644
index 0000000..39d9f6e
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ExclusionStrategy.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * A strategy (or policy) definition that is used to decide whether or not a field or top-level
+ * class should be serialized or deserialized as part of the JSON output/input. For serialization,
+ * if the {@link #shouldSkipClass(Class)} method returns false then that class or field type
+ * will not be part of the JSON output. For deserialization, if {@link #shouldSkipClass(Class)}
+ * returns false, then it will not be set as part of the Java object structure.
+ *
+ *
The following are a few examples that shows how you can use this exclusion mechanism.
+ *
+ *
Exclude fields and objects based on a particular class type:
+ *
Excludes fields and objects based on a particular annotation:
+ *
+ * public @interface FooAnnotation {
+ * // some implementation here
+ * }
+ *
+ * // Excludes any field (or class) that is tagged with an "@FooAnnotation"
+ * private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
+ * public boolean shouldSkipClass(Class<?> clazz) {
+ * return clazz.getAnnotation(FooAnnotation.class) != null;
+ * }
+ *
+ * public boolean shouldSkipField(FieldAttributes f) {
+ * return f.getAnnotation(FooAnnotation.class) != null;
+ * }
+ * }
+ *
+ *
+ *
Now if you want to configure {@code Gson} to use a user defined exclusion strategy, then
+ * the {@code GsonBuilder} is required. The following is an example of how you can use the
+ * {@code GsonBuilder} to configure Gson to use one of the above sample:
+ *
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @see GsonBuilder#setExclusionStrategies(ExclusionStrategy...)
+ *
+ * @since 1.4
+ */
+public interface ExclusionStrategy {
+
+ /**
+ * @param f the field object that is under test
+ * @return true if the field should be ignored; otherwise false
+ */
+ public boolean shouldSkipField(FieldAttributes f);
+
+ /**
+ * @param clazz the class object that is under test
+ * @return true if the class should be ignored; otherwise false
+ */
+ public boolean shouldSkipClass(Class> clazz);
+}
diff --git a/src/org/mcteam/ancientgates/gson/ExposeAnnotationDeserializationExclusionStrategy.java b/src/org/mcteam/ancientgates/gson/ExposeAnnotationDeserializationExclusionStrategy.java
new file mode 100644
index 0000000..2cbfeb6
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ExposeAnnotationDeserializationExclusionStrategy.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mcteam.ancientgates.gson;
+
+import org.mcteam.ancientgates.gson.annotations.Expose;
+
+/**
+ * Excludes fields that do not have the {@link Expose} annotation
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class ExposeAnnotationDeserializationExclusionStrategy implements ExclusionStrategy {
+
+ public boolean shouldSkipClass(Class> clazz) {
+ return false;
+ }
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ Expose annotation = f.getAnnotation(Expose.class);
+ if (annotation == null) {
+ return true;
+ }
+ return !annotation.deserialize();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/ExposeAnnotationSerializationExclusionStrategy.java b/src/org/mcteam/ancientgates/gson/ExposeAnnotationSerializationExclusionStrategy.java
new file mode 100644
index 0000000..ee366a9
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ExposeAnnotationSerializationExclusionStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import org.mcteam.ancientgates.gson.annotations.Expose;
+
+/**
+ * Excludes fields that do not have the {@link Expose} annotation
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class ExposeAnnotationSerializationExclusionStrategy implements ExclusionStrategy {
+
+ public boolean shouldSkipClass(Class> clazz) {
+ return false;
+ }
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ Expose annotation = f.getAnnotation(Expose.class);
+ if (annotation == null) {
+ return true;
+ }
+ return !annotation.serialize();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/FieldAttributes.java b/src/org/mcteam/ancientgates/gson/FieldAttributes.java
new file mode 100644
index 0000000..f7ab987
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/FieldAttributes.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * A data object that stores attributes of a field.
+ *
+ *
This class is immutable; therefore, it can be safely shared across threads.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @since 1.4
+ */
+public final class FieldAttributes {
+ private static final String MAX_CACHE_PROPERTY_NAME =
+ "com.bukkit.mcteam.gson.annotation_cache_size_hint";
+
+ private static final Cache, String>, Collection> ANNOTATION_CACHE =
+ new LruCache,String>, Collection>(getMaxCacheSize());
+
+ private final Class> declaringClazz;
+ private final Field field;
+ private final Class> declaredType;
+ private final boolean isSynthetic;
+ private final int modifiers;
+ private final String name;
+
+ // Fields used for lazy initialization
+ private Type genericType;
+ private Collection annotations;
+
+ /**
+ * Constructs a Field Attributes object from the {@code f}.
+ *
+ * @param f the field to pull attributes from
+ */
+ FieldAttributes(final Class> declaringClazz, final Field f) {
+ Preconditions.checkNotNull(declaringClazz);
+ this.declaringClazz = declaringClazz;
+ name = f.getName();
+ declaredType = f.getType();
+ isSynthetic = f.isSynthetic();
+ modifiers = f.getModifiers();
+ field = f;
+ }
+
+ private static int getMaxCacheSize() {
+ final int defaultMaxCacheSize = 2000;
+ try {
+ String propertyValue = System.getProperty(
+ MAX_CACHE_PROPERTY_NAME, String.valueOf(defaultMaxCacheSize));
+ return Integer.parseInt(propertyValue);
+ } catch (NumberFormatException e) {
+ return defaultMaxCacheSize;
+ }
+ }
+
+ /**
+ * @return the declaring class that contains this field
+ */
+ public Class> getDeclaringClass() {
+ return declaringClazz;
+ }
+
+ /**
+ * @return the name of the field
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ *
For example, assume the following class definition:
+ *
+ * public class Foo {
+ * private String bar;
+ * private List<String> red;
+ * }
+ *
+ * Type listParmeterizedType = new TypeToken>() {}.getType();
+ *
+ *
+ *
This method would return {@code String.class} for the {@code bar} field and
+ * {@code listParameterizedType} for the {@code red} field.
+ *
+ * @return the specific type declared for this field
+ */
+ public Type getDeclaredType() {
+ if (genericType == null) {
+ genericType = field.getGenericType();
+ }
+ return genericType;
+ }
+
+ /**
+ * Returns the {@code Class>} object that was declared for this field.
+ *
+ *
For example, assume the following class definition:
+ *
This method would return {@code String.class} for the {@code bar} field and
+ * {@code List.class} for the {@code red} field.
+ *
+ * @return the specific class object that was declared for the field
+ */
+ public Class> getDeclaredClass() {
+ return declaredType;
+ }
+
+ /**
+ * Return the {@code T} annotation object from this field if it exist; otherwise returns
+ * {@code null}.
+ *
+ * @param annotation the class of the annotation that will be retrieved
+ * @return the annotation instance if it is bound to the field; otherwise {@code null}
+ */
+ public T getAnnotation(Class annotation) {
+ return getAnnotationFromArray(getAnnotations(), annotation);
+ }
+
+ /**
+ * Return the annotations that are present on this field.
+ *
+ * @return an array of all the annotations set on the field
+ * @since 1.4
+ */
+ public Collection getAnnotations() {
+ if (annotations == null) {
+ Pair, String> key = new Pair, String>(declaringClazz, name);
+ annotations = ANNOTATION_CACHE.getElement(key);
+ if (annotations == null) {
+ annotations = Collections.unmodifiableCollection(
+ Arrays.asList(field.getAnnotations()));
+ ANNOTATION_CACHE.addElement(key, annotations);
+ }
+ }
+ return annotations;
+ }
+
+ /**
+ * Returns {@code true} if the field is defined with the {@code modifier}.
+ *
+ *
+ *
+ * @see java.lang.reflect.Modifier
+ */
+ public boolean hasModifier(int modifier) {
+ return (modifiers & modifier) != 0;
+ }
+
+ /**
+ * This is exposed internally only for the removing synthetic fields from the JSON output.
+ *
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ */
+ void set(Object instance, Object value) throws IllegalAccessException {
+ field.set(instance, value);
+ }
+
+ /**
+ * This is exposed internally only for the removing synthetic fields from the JSON output.
+ *
+ * @return true if the field is synthetic; otherwise false
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ */
+ Object get(Object instance) throws IllegalAccessException {
+ return field.get(instance);
+ }
+
+ /**
+ * This is exposed internally only for the removing synthetic fields from the JSON output.
+ *
+ * @return true if the field is synthetic; otherwise false
+ */
+ boolean isSynthetic() {
+ return isSynthetic;
+ }
+
+ /**
+ * @deprecated remove this when {@link FieldNamingStrategy} is deleted.
+ */
+ @Deprecated
+ Field getFieldObject() {
+ return field;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static T getAnnotationFromArray(
+ Collection annotations, Class annotation) {
+ for (Annotation a : annotations) {
+ if (a.annotationType() == annotation) {
+ return (T) a;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/FieldNamingPolicy.java b/src/org/mcteam/ancientgates/gson/FieldNamingPolicy.java
new file mode 100644
index 0000000..86289b4
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/FieldNamingPolicy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * An enumeration that defines a few standard naming conventions for JSON field names.
+ * This enumeration should be used in conjunction with {@link org.mcteam.ancientgates.gson.GsonBuilder}
+ * to configure a {@link org.mcteam.ancientgates.gson.Gson} instance to properly translate Java field
+ * names into the desired JSON field names.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public enum FieldNamingPolicy {
+ /**
+ * Using this naming policy with Gson will ensure that the first "letter" of the Java
+ * field name is capitalized when serialized to its JSON form.
+ *
+ *
Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":
+ *
+ *
someFieldName ---> SomeFieldName
+ *
_someFieldName ---> _SomeFieldName
+ *
+ */
+ UPPER_CAMEL_CASE(new ModifyFirstLetterNamingPolicy(
+ ModifyFirstLetterNamingPolicy.LetterModifier.UPPER)),
+
+ /**
+ * Using this naming policy with Gson will ensure that the first "letter" of the Java
+ * field name is capitalized when serialized to its JSON form and the words will be
+ * separated by a space.
+ *
+ *
Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":
+ *
+ *
someFieldName ---> Some Field Name
+ *
_someFieldName ---> _Some Field Name
+ *
+ *
+ * @since 1.4
+ */
+ UPPER_CAMEL_CASE_WITH_SPACES(new UpperCamelCaseSeparatorNamingPolicy(" ")),
+
+ /**
+ * Using this naming policy with Gson will modify the Java Field name from its camel cased
+ * form to a lower case field name where each word is separated by an underscore (_).
+ *
+ *
Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":
+ *
+ *
someFieldName ---> some_field_name
+ *
_someFieldName ---> _some_field_name
+ *
aStringField ---> a_string_field
+ *
aURL ---> a_u_r_l
+ *
+ */
+ LOWER_CASE_WITH_UNDERSCORES(new LowerCamelCaseSeparatorNamingPolicy("_")),
+
+ /**
+ * Using this naming policy with Gson will modify the Java Field name from its camel cased
+ * form to a lower case field name where each word is separated by a dash (-).
+ *
+ *
Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":
+ *
+ *
someFieldName ---> some-field-name
+ *
_someFieldName ---> _some-field-name
+ *
aStringField ---> a-string-field
+ *
aURL ---> a-u-r-l
+ *
+ * Using dashes in JavaScript is not recommended since dash is also used for a minus sign in
+ * expressions. This requires that a field named with dashes is always accessed as a quoted
+ * property like {@code myobject['my-field']}. Accessing it as an object field
+ * {@code myobject.my-field} will result in an unintended javascript expression.
+ * @since 1.4
+ */
+ LOWER_CASE_WITH_DASHES(new LowerCamelCaseSeparatorNamingPolicy("-"));
+
+ private final FieldNamingStrategy2 namingPolicy;
+
+ private FieldNamingPolicy(FieldNamingStrategy2 namingPolicy) {
+ this.namingPolicy = namingPolicy;
+ }
+
+ FieldNamingStrategy2 getFieldNamingPolicy() {
+ return namingPolicy;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/FieldNamingStrategy.java b/src/org/mcteam/ancientgates/gson/FieldNamingStrategy.java
new file mode 100644
index 0000000..1f425dc
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/FieldNamingStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Field;
+
+/**
+ * A mechanism for providing custom field naming in Gson. This allows the client code to translate
+ * field names into a particular convention that is not supported as a normal Java field
+ * declaration rules. For example, Java does not support "-" characters in a field name.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.3
+ */
+public interface FieldNamingStrategy {
+
+ /**
+ * Translates the field name into its JSON field name representation.
+ *
+ * @param f the field object that we are translating
+ * @return the translated field name.
+ * @since 1.3
+ */
+ public String translateName(Field f);
+}
diff --git a/src/org/mcteam/ancientgates/gson/FieldNamingStrategy2.java b/src/org/mcteam/ancientgates/gson/FieldNamingStrategy2.java
new file mode 100644
index 0000000..ad02f35
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/FieldNamingStrategy2.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * The new mechanism for providing custom field naming in Gson. This allows the client code
+ * to translate field names into a particular convention that is not supported as a normal
+ * Java field declaration rules. For example, Java does not support "-" characters in a
+ * field name.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+interface FieldNamingStrategy2 {
+
+ /**
+ * Translates the field name into its JSON field name representation.
+ *
+ * @param f the field that is being translated
+ * @return the translated field name.
+ * @since 1.3
+ */
+ public String translateName(FieldAttributes f);
+}
diff --git a/src/org/mcteam/ancientgates/gson/FieldNamingStrategy2Adapter.java b/src/org/mcteam/ancientgates/gson/FieldNamingStrategy2Adapter.java
new file mode 100644
index 0000000..944bc2a
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/FieldNamingStrategy2Adapter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * Adapts the old "deprecated" {@link FieldNamingStrategy} to the new {@link FieldNamingStrategy2}
+ * type.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class FieldNamingStrategy2Adapter implements FieldNamingStrategy2 {
+ private final FieldNamingStrategy adaptee;
+
+ public FieldNamingStrategy2Adapter(FieldNamingStrategy adaptee) {
+ Preconditions.checkNotNull(adaptee);
+ this.adaptee = adaptee;
+ }
+
+ @SuppressWarnings("deprecation")
+ public String translateName(FieldAttributes f) {
+ return adaptee.translateName(f.getFieldObject());
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/GenericArrayTypeImpl.java b/src/org/mcteam/ancientgates/gson/GenericArrayTypeImpl.java
new file mode 100644
index 0000000..c314cb5
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/GenericArrayTypeImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+
+/**
+ * An simple pojo-like immutable instance of the {@link GenericArrayType}. This object provides
+ * us the ability to create reflective types on demand. This object is required for support
+ * object similar to the one defined below:
+ *
During parsing or serialization, we know the real variable type parameter {@code T},
+ * so we can build a new {@code GenericTypeArray} with the "real" type parameters and
+ * pass that object along instead.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class GenericArrayTypeImpl implements GenericArrayType {
+
+ private final Type genericComponentType;
+
+ public GenericArrayTypeImpl(Type genericComponentType) {
+ this.genericComponentType = genericComponentType;
+ }
+
+ public Type getGenericComponentType() {
+ return genericComponentType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GenericArrayType)) {
+ return false;
+ }
+ GenericArrayType that = (GenericArrayType) o;
+ Type thatComponentType = that.getGenericComponentType();
+ return genericComponentType == null ?
+ thatComponentType == null : genericComponentType.equals(thatComponentType);
+ }
+
+ @Override
+ public int hashCode() {
+ return (genericComponentType == null) ? 0 : genericComponentType.hashCode();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/Gson.java b/src/org/mcteam/ancientgates/gson/Gson.java
new file mode 100644
index 0000000..0f79792
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/Gson.java
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.mcteam.ancientgates.gson.JsonSerializationContextDefault;
+import org.mcteam.ancientgates.gson.stream.JsonReader;
+import org.mcteam.ancientgates.gson.stream.JsonToken;
+import org.mcteam.ancientgates.gson.stream.JsonWriter;
+import org.mcteam.ancientgates.gson.stream.MalformedJsonException;
+
+/**
+ * This is the main class for using Gson. Gson is typically used by first constructing a
+ * Gson instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)}
+ * methods on it.
+ *
+ *
You can create a Gson instance by invoking {@code new Gson()} if the default configuration
+ * is all you need. You can also use {@link GsonBuilder} to build a Gson instance with various
+ * configuration options such as versioning support, pretty printing, custom
+ * {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s.
+ *
+ *
Here is an example of how Gson is used for a simple Class:
+ *
+ *
+ * Gson gson = new Gson(); // Or use new GsonBuilder().create();
+ * MyType target = new MyType();
+ * String json = gson.toJson(target); // serializes target to Json
+ * MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
+ *
+ *
+ *
If the object that your are serializing/deserializing is a {@code ParameterizedType}
+ * (i.e. contains at least one type parameter and may be an array) then you must use the
+ * {@link #toJson(Object, Type)} or {@link #fromJson(String, Type)} method. Here is an
+ * example for serializing and deserialing a {@code ParameterizedType}:
+ *
+ *
See the Gson User Guide
+ * for a more complete set of examples.
+ *
+ * @see org.mcteam.ancientgates.gson.reflect.TypeToken
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class Gson {
+
+ //TODO(inder): get rid of all the registerXXX methods and take all such parameters in the
+ // constructor instead. At the minimum, mark those methods private.
+
+ private static final String NULL_STRING = "null";
+
+ static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
+
+ // Default instances of plug-ins
+ static final AnonymousAndLocalClassExclusionStrategy DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY =
+ new AnonymousAndLocalClassExclusionStrategy();
+ static final SyntheticFieldExclusionStrategy DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY =
+ new SyntheticFieldExclusionStrategy(true);
+ static final ModifierBasedExclusionStrategy DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY =
+ new ModifierBasedExclusionStrategy(new int[] { Modifier.TRANSIENT, Modifier.STATIC });
+ static final FieldNamingStrategy2 DEFAULT_NAMING_POLICY =
+ new SerializedNameAnnotationInterceptingNamingPolicy(new JavaFieldNamingPolicy());
+
+ private static final ExclusionStrategy DEFAULT_EXCLUSION_STRATEGY =
+ createExclusionStrategy(VersionConstants.IGNORE_VERSIONS);
+
+ private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
+
+ private final ExclusionStrategy serializationStrategy;
+
+ private final ExclusionStrategy deserializationStrategy;
+
+ private final FieldNamingStrategy2 fieldNamingPolicy;
+ private final MappedObjectConstructor objectConstructor;
+
+ /** Map containing Type or Class objects as keys */
+ private final ParameterizedTypeHandlerMap> serializers;
+
+ /** Map containing Type or Class objects as keys */
+ private final ParameterizedTypeHandlerMap> deserializers;
+
+ private final boolean serializeNulls;
+ private final boolean htmlSafe;
+ private final boolean generateNonExecutableJson;
+ private final boolean prettyPrinting;
+
+ /**
+ * Constructs a Gson object with default configuration. The default configuration has the
+ * following settings:
+ *
+ *
The JSON generated by toJson methods is in compact representation. This
+ * means that all the unneeded white-space is removed. You can change this behavior with
+ * {@link GsonBuilder#setPrettyPrinting()}.
+ *
The generated JSON omits all the fields that are null. Note that nulls in arrays are
+ * kept as is since an array is an ordered list. Moreover, if a field is not null, but its
+ * generated JSON is empty, the field is kept. You can configure Gson to serialize null values
+ * by setting {@link GsonBuilder#serializeNulls()}.
+ *
Gson provides default serialization and deserialization for Enums, {@link Map},
+ * {@link java.net.URL}, {@link java.net.URI}, {@link java.util.Locale}, {@link java.util.Date},
+ * {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If you would prefer
+ * to change the default representation, you can do so by registering a type adapter through
+ * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.
+ *
The default Date format is same as {@link java.text.DateFormat#DEFAULT}. This format
+ * ignores the millisecond portion of the date during serialization. You can change
+ * this by invoking {@link GsonBuilder#setDateFormat(int)} or
+ * {@link GsonBuilder#setDateFormat(String)}.
+ *
By default, Gson ignores the {@link org.mcteam.ancientgates.gson.annotations.Expose} annotation.
+ * You can enable Gson to serialize/deserialize only those fields marked with this annotation
+ * through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}.
+ *
By default, Gson ignores the {@link org.mcteam.ancientgates.gson.annotations.Since} annotation. You
+ * can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.
+ *
The default field naming policy for the output Json is same as in Java. So, a Java class
+ * field versionNumber will be output as "versionNumber@quot; in
+ * Json. The same rules are applied for mapping incoming Json to the Java classes. You can
+ * change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.
+ *
By default, Gson excludes transient or static fields from
+ * consideration for serialization and deserialization. You can change this behavior through
+ * {@link GsonBuilder#excludeFieldsWithModifiers(int...)}.
+ *
+ */
+ public Gson() {
+ this(DEFAULT_EXCLUSION_STRATEGY, DEFAULT_EXCLUSION_STRATEGY, DEFAULT_NAMING_POLICY,
+ new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
+ false, DefaultTypeAdapters.getDefaultSerializers(),
+ DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE, true, false);
+ }
+
+ Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy,
+ FieldNamingStrategy2 fieldNamingPolicy, MappedObjectConstructor objectConstructor,
+ boolean serializeNulls, ParameterizedTypeHandlerMap> serializers,
+ ParameterizedTypeHandlerMap> deserializers,
+ boolean generateNonExecutableGson, boolean htmlSafe, boolean prettyPrinting) {
+ this.serializationStrategy = serializationStrategy;
+ this.deserializationStrategy = deserializationStrategy;
+ this.fieldNamingPolicy = fieldNamingPolicy;
+ this.objectConstructor = objectConstructor;
+ this.serializeNulls = serializeNulls;
+ this.serializers = serializers;
+ this.deserializers = deserializers;
+ this.generateNonExecutableJson = generateNonExecutableGson;
+ this.htmlSafe = htmlSafe;
+ this.prettyPrinting = prettyPrinting;
+ }
+
+ private ObjectNavigatorFactory createDefaultObjectNavigatorFactory(ExclusionStrategy strategy) {
+ return new ObjectNavigatorFactory(strategy, fieldNamingPolicy);
+ }
+
+ private static ExclusionStrategy createExclusionStrategy(double version) {
+ List strategies = new LinkedList();
+ strategies.add(DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY);
+ strategies.add(DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY);
+ strategies.add(DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY);
+ if (version != VersionConstants.IGNORE_VERSIONS) {
+ strategies.add(new VersionExclusionStrategy(version));
+ }
+ return new DisjunctionExclusionStrategy(strategies);
+ }
+
+ /**
+ * This method serializes the specified object into its equivalent representation as a tree of
+ * {@link JsonElement}s. This method should be used when the specified object is not a generic
+ * type. This method uses {@link Class#getClass()} to get the type for the specified object, but
+ * the {@code getClass()} loses the generic type information because of the Type Erasure feature
+ * of Java. Note that this method works fine if the any of the object fields are of generic type,
+ * just the object itself should not be of a generic type. If the object is of generic type, use
+ * {@link #toJsonTree(Object, Type)} instead.
+ *
+ * @param src the object for which Json representation is to be created setting for Gson
+ * @return Json representation of {@code src}.
+ * @since 1.4
+ */
+ public JsonElement toJsonTree(Object src) {
+ if (src == null) {
+ return JsonNull.createJsonNull();
+ }
+ return toJsonTree(src, src.getClass());
+ }
+
+ /**
+ * This method serializes the specified object, including those of generic types, into its
+ * equivalent representation as a tree of {@link JsonElement}s. This method must be used if the
+ * specified object is a generic type. For non-generic objects, use {@link #toJsonTree(Object)}
+ * instead.
+ *
+ * @param src the object for which JSON representation is to be created
+ * @param typeOfSrc The specific genericized type of src. You can obtain
+ * this type by using the {@link org.mcteam.ancientgates.gson.reflect.TypeToken} class. For example,
+ * to get the type for {@code Collection}, you should use:
+ *
+ * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
+ *
+ * @return Json representation of {@code src}
+ * @since 1.4
+ */
+ public JsonElement toJsonTree(Object src, Type typeOfSrc) {
+ if (src == null) {
+ return JsonNull.createJsonNull();
+ }
+ JsonSerializationContextDefault context = new JsonSerializationContextDefault(
+ createDefaultObjectNavigatorFactory(serializationStrategy), serializeNulls, serializers);
+ return context.serialize(src, typeOfSrc, true);
+ }
+
+ /**
+ * This method serializes the specified object into its equivalent Json representation.
+ * This method should be used when the specified object is not a generic type. This method uses
+ * {@link Class#getClass()} to get the type for the specified object, but the
+ * {@code getClass()} loses the generic type information because of the Type Erasure feature
+ * of Java. Note that this method works fine if the any of the object fields are of generic type,
+ * just the object itself should not be of a generic type. If the object is of generic type, use
+ * {@link #toJson(Object, Type)} instead. If you want to write out the object to a
+ * {@link Writer}, use {@link #toJson(Object, Appendable)} instead.
+ *
+ * @param src the object for which Json representation is to be created setting for Gson
+ * @return Json representation of {@code src}.
+ */
+ public String toJson(Object src) {
+ if (src == null) {
+ return serializeNulls ? NULL_STRING : "";
+ }
+ return toJson(src, src.getClass());
+ }
+
+ /**
+ * This method serializes the specified object, including those of generic types, into its
+ * equivalent Json representation. This method must be used if the specified object is a generic
+ * type. For non-generic objects, use {@link #toJson(Object)} instead. If you want to write out
+ * the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead.
+ *
+ * @param src the object for which JSON representation is to be created
+ * @param typeOfSrc The specific genericized type of src. You can obtain
+ * this type by using the {@link org.mcteam.ancientgates.gson.reflect.TypeToken} class. For example,
+ * to get the type for {@code Collection}, you should use:
+ *
+ * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
+ *
+ * @return Json representation of {@code src}
+ */
+ public String toJson(Object src, Type typeOfSrc) {
+ StringWriter writer = new StringWriter();
+ toJson(toJsonTree(src, typeOfSrc), writer);
+ return writer.toString();
+ }
+
+ /**
+ * This method serializes the specified object into its equivalent Json representation.
+ * This method should be used when the specified object is not a generic type. This method uses
+ * {@link Class#getClass()} to get the type for the specified object, but the
+ * {@code getClass()} loses the generic type information because of the Type Erasure feature
+ * of Java. Note that this method works fine if the any of the object fields are of generic type,
+ * just the object itself should not be of a generic type. If the object is of generic type, use
+ * {@link #toJson(Object, Type, Appendable)} instead.
+ *
+ * @param src the object for which Json representation is to be created setting for Gson
+ * @param writer Writer to which the Json representation needs to be written
+ * @throws JsonIOException if there was a problem writing to the writer
+ * @since 1.2
+ */
+ public void toJson(Object src, Appendable writer) throws JsonIOException {
+ try {
+ if (src != null) {
+ toJson(src, src.getClass(), writer);
+ } else if (serializeNulls) {
+ writeOutNullString(writer);
+ }
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ /**
+ * This method serializes the specified object, including those of generic types, into its
+ * equivalent Json representation. This method must be used if the specified object is a generic
+ * type. For non-generic objects, use {@link #toJson(Object, Appendable)} instead.
+ *
+ * @param src the object for which JSON representation is to be created
+ * @param typeOfSrc The specific genericized type of src. You can obtain
+ * this type by using the {@link org.mcteam.ancientgates.gson.reflect.TypeToken} class. For example,
+ * to get the type for {@code Collection}, you should use:
+ *
+ * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
+ *
+ * @param writer Writer to which the Json representation of src needs to be written.
+ * @throws JsonIOException if there was a problem writing to the writer
+ * @since 1.2
+ */
+ public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException {
+ JsonElement jsonElement = toJsonTree(src, typeOfSrc);
+ toJson(jsonElement, writer);
+ }
+
+ /**
+ * Writes the JSON representation of {@code src} of type {@code typeOfSrc} to
+ * {@code writer}.
+ * @throws JsonIOException if there was a problem writing to the writer
+ */
+ public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
+ toJson(toJsonTree(src, typeOfSrc), writer);
+ }
+
+ /**
+ * Converts a tree of {@link JsonElement}s into its equivalent JSON representation.
+ *
+ * @param jsonElement root of a tree of {@link JsonElement}s
+ * @return JSON String representation of the tree
+ * @since 1.4
+ */
+ public String toJson(JsonElement jsonElement) {
+ StringWriter writer = new StringWriter();
+ toJson(jsonElement, writer);
+ return writer.toString();
+ }
+
+ /**
+ * Writes out the equivalent JSON for a tree of {@link JsonElement}s.
+ *
+ * @param jsonElement root of a tree of {@link JsonElement}s
+ * @param writer Writer to which the Json representation needs to be written
+ * @throws JsonIOException if there was a problem writing to the writer
+ * @since 1.4
+ */
+ public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOException {
+ try {
+ if (generateNonExecutableJson) {
+ writer.append(JSON_NON_EXECUTABLE_PREFIX);
+ }
+ JsonWriter jsonWriter = new JsonWriter(Streams.writerForAppendable(writer));
+ if (prettyPrinting) {
+ jsonWriter.setIndent(" ");
+ }
+ toJson(jsonElement, jsonWriter);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Writes the JSON for {@code jsonElement} to {@code writer}.
+ * @throws JsonIOException if there was a problem writing to the writer
+ */
+ public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException {
+ boolean oldLenient = writer.isLenient();
+ writer.setLenient(true);
+ boolean oldHtmlSafe = writer.isHtmlSafe();
+ writer.setHtmlSafe(htmlSafe);
+ try {
+ Streams.write(jsonElement, serializeNulls, writer);
+ } catch (IOException e) {
+ throw new JsonIOException(e);
+ } finally {
+ writer.setLenient(oldLenient);
+ writer.setHtmlSafe(oldHtmlSafe);
+ }
+ }
+
+ /**
+ * This method deserializes the specified Json into an object of the specified class. It is not
+ * suitable to use if the specified class is a generic type since it will not have the generic
+ * type information because of the Type Erasure feature of Java. Therefore, this method should not
+ * be used if the desired type is a generic type. Note that this method works fine if the any of
+ * the fields of the specified object are generics, just the object itself should not be a
+ * generic type. For the cases when the object is of generic type, invoke
+ * {@link #fromJson(String, Type)}. If you have the Json in a {@link Reader} instead of
+ * a String, use {@link #fromJson(Reader, Class)} instead.
+ *
+ * @param the type of the desired object
+ * @param json the string from which the object is to be deserialized
+ * @param classOfT the class of T
+ * @return an object of type T from the string
+ * @throws JsonSyntaxException if json is not a valid representation for an object of type
+ * classOfT
+ */
+ public T fromJson(String json, Class classOfT) throws JsonSyntaxException {
+ Object object = fromJson(json, (Type) classOfT);
+ return Primitives.wrap(classOfT).cast(object);
+ }
+
+ /**
+ * This method deserializes the specified Json into an object of the specified type. This method
+ * is useful if the specified object is a generic type. For non-generic objects, use
+ * {@link #fromJson(String, Class)} instead. If you have the Json in a {@link Reader} instead of
+ * a String, use {@link #fromJson(Reader, Type)} instead.
+ *
+ * @param the type of the desired object
+ * @param json the string from which the object is to be deserialized
+ * @param typeOfT The specific genericized type of src. You can obtain this type by using the
+ * {@link org.mcteam.ancientgates.gson.reflect.TypeToken} class. For example, to get the type for
+ * {@code Collection}, you should use:
+ *
+ * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
+ *
+ * @return an object of type T from the string
+ * @throws JsonParseException if json is not a valid representation for an object of type typeOfT
+ * @throws JsonSyntaxException if json is not a valid representation for an object of type
+ */
+ @SuppressWarnings("unchecked")
+ public T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
+ if (json == null) {
+ return null;
+ }
+ StringReader reader = new StringReader(json);
+ T target = (T) fromJson(reader, typeOfT);
+ return target;
+ }
+
+ /**
+ * This method deserializes the Json read from the specified reader into an object of the
+ * specified class. It is not suitable to use if the specified class is a generic type since it
+ * will not have the generic type information because of the Type Erasure feature of Java.
+ * Therefore, this method should not be used if the desired type is a generic type. Note that
+ * this method works fine if the any of the fields of the specified object are generics, just the
+ * object itself should not be a generic type. For the cases when the object is of generic type,
+ * invoke {@link #fromJson(Reader, Type)}. If you have the Json in a String form instead of a
+ * {@link Reader}, use {@link #fromJson(String, Class)} instead.
+ *
+ * @param the type of the desired object
+ * @param json the reader producing the Json from which the object is to be deserialized.
+ * @param classOfT the class of T
+ * @return an object of type T from the string
+ * @throws JsonIOException if there was a problem reading from the Reader
+ * @throws JsonSyntaxException if json is not a valid representation for an object of type
+ * @since 1.2
+ */
+ public T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException {
+ JsonReader jsonReader = new JsonReader(json);
+ Object object = fromJson(jsonReader, classOfT);
+ assertFullConsumption(object, jsonReader);
+ return Primitives.wrap(classOfT).cast(object);
+ }
+
+ /**
+ * This method deserializes the Json read from the specified reader into an object of the
+ * specified type. This method is useful if the specified object is a generic type. For
+ * non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the Json in a
+ * String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead.
+ *
+ * @param the type of the desired object
+ * @param json the reader producing Json from which the object is to be deserialized
+ * @param typeOfT The specific genericized type of src. You can obtain this type by using the
+ * {@link org.mcteam.ancientgates.gson.reflect.TypeToken} class. For example, to get the type for
+ * {@code Collection}, you should use:
+ *
+ * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
+ *
+ * @return an object of type T from the json
+ * @throws JsonIOException if there was a problem reading from the Reader
+ * @throws JsonSyntaxException if json is not a valid representation for an object of type
+ * @since 1.2
+ */
+ public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
+ JsonReader jsonReader = new JsonReader(json);
+ T object = this.fromJson(jsonReader, typeOfT);
+ assertFullConsumption(object, jsonReader);
+ return object;
+ }
+
+ private static void assertFullConsumption(Object obj, JsonReader reader) {
+ try {
+ if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) {
+ throw new JsonIOException("JSON document was not fully consumed.");
+ }
+ } catch (MalformedJsonException e) {
+ throw new JsonSyntaxException(e);
+ } catch (IOException e) {
+ throw new JsonIOException(e);
+ }
+ }
+
+ /**
+ * Reads the next JSON value from {@code reader} and convert it to an object
+ * of type {@code typeOfT}.
+ * Since Type is not parameterized by T, this method is type unsafe and should be used carefully
+ *
+ * @throws JsonIOException if there was a problem writing to the Reader
+ * @throws JsonSyntaxException if json is not a valid representation for an object of type
+ */
+ @SuppressWarnings("unchecked")
+ public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
+ boolean oldLenient = reader.isLenient();
+ reader.setLenient(true);
+ try {
+ JsonElement root = Streams.parse(reader);
+ return (T) fromJson(root, typeOfT);
+ } finally {
+ reader.setLenient(oldLenient);
+ }
+ }
+
+ /**
+ * This method deserializes the Json read from the specified parse tree into an object of the
+ * specified type. It is not suitable to use if the specified class is a generic type since it
+ * will not have the generic type information because of the Type Erasure feature of Java.
+ * Therefore, this method should not be used if the desired type is a generic type. Note that
+ * this method works fine if the any of the fields of the specified object are generics, just the
+ * object itself should not be a generic type. For the cases when the object is of generic type,
+ * invoke {@link #fromJson(JsonElement, Type)}.
+ * @param the type of the desired object
+ * @param json the root of the parse tree of {@link JsonElement}s from which the object is to
+ * be deserialized
+ * @param classOfT The class of T
+ * @return an object of type T from the json
+ * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
+ * @since 1.3
+ */
+ public T fromJson(JsonElement json, Class classOfT) throws JsonSyntaxException {
+ Object object = fromJson(json, (Type) classOfT);
+ return Primitives.wrap(classOfT).cast(object);
+ }
+
+ /**
+ * This method deserializes the Json read from the specified parse tree into an object of the
+ * specified type. This method is useful if the specified object is a generic type. For
+ * non-generic objects, use {@link #fromJson(JsonElement, Class)} instead.
+ *
+ * @param the type of the desired object
+ * @param json the root of the parse tree of {@link JsonElement}s from which the object is to
+ * be deserialized
+ * @param typeOfT The specific genericized type of src. You can obtain this type by using the
+ * {@link org.mcteam.ancientgates.gson.reflect.TypeToken} class. For example, to get the type for
+ * {@code Collection}, you should use:
+ *
+ * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
+ *
+ * @return an object of type T from the json
+ * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
+ * @since 1.3
+ */
+ @SuppressWarnings("unchecked")
+ public T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
+ if (json == null) {
+ return null;
+ }
+ JsonDeserializationContext context = new JsonDeserializationContextDefault(
+ createDefaultObjectNavigatorFactory(deserializationStrategy), deserializers,
+ objectConstructor);
+ T target = (T) context.deserialize(json, typeOfT);
+ return target;
+ }
+
+ /**
+ * Appends the {@link #NULL_STRING} to the {@code writer} object.
+ *
+ * @param writer the object to append the null value to
+ */
+ private void writeOutNullString(Appendable writer) throws IOException {
+ writer.append(NULL_STRING);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("{")
+ .append("serializeNulls:").append(serializeNulls)
+ .append(",serializers:").append(serializers)
+ .append(",deserializers:").append(deserializers)
+
+ // using the name instanceCreator instead of ObjectConstructor since the users of Gson are
+ // more familiar with the concept of Instance Creators. Moreover, the objectConstructor is
+ // just a utility class around instance creators, and its toString() only displays them.
+ .append(",instanceCreators:").append(objectConstructor)
+ .append("}");
+ return sb.toString();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/GsonBuilder.java b/src/org/mcteam/ancientgates/gson/GsonBuilder.java
new file mode 100644
index 0000000..73bc2b5
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/GsonBuilder.java
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+import java.text.DateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.mcteam.ancientgates.gson.DefaultTypeAdapters.DefaultDateTypeAdapter;
+
+
+/**
+ *
Use this builder to construct a {@link Gson} instance when you need to set configuration
+ * options other than the default. For {@link Gson} with default configuration, it is simpler to
+ * use {@code new Gson()}. {@code GsonBuilder} is best used by creating it, and then invoking its
+ * various configuration methods, and finally calling create.
+ *
+ *
The following is an example shows how to use the {@code GsonBuilder} to construct a Gson
+ * instance:
+ *
+ *
NOTE: the order of invocation of configuration methods does not matter.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class GsonBuilder {
+ private static final InnerClassExclusionStrategy innerClassExclusionStrategy =
+ new InnerClassExclusionStrategy();
+ private static final ExposeAnnotationSerializationExclusionStrategy
+ exposeAnnotationSerializationExclusionStrategy =
+ new ExposeAnnotationSerializationExclusionStrategy();
+ private static final ExposeAnnotationDeserializationExclusionStrategy
+ exposeAnnotationDeserializationExclusionStrategy =
+ new ExposeAnnotationDeserializationExclusionStrategy();
+
+ private final Collection exclusionStrategies =
+ new HashSet();
+
+ private double ignoreVersionsAfter;
+ private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy;
+ private boolean serializeInnerClasses;
+ private boolean excludeFieldsWithoutExposeAnnotation;
+ private LongSerializationPolicy longSerializationPolicy;
+ private FieldNamingStrategy2 fieldNamingPolicy;
+ private final ParameterizedTypeHandlerMap> instanceCreators;
+ private final ParameterizedTypeHandlerMap> serializers;
+ private final ParameterizedTypeHandlerMap> deserializers;
+ private boolean serializeNulls;
+ private String datePattern;
+ private int dateStyle;
+ private int timeStyle;
+ private boolean serializeSpecialFloatingPointValues;
+ private boolean escapeHtmlChars;
+ private boolean prettyPrinting;
+ private boolean generateNonExecutableJson;
+
+ /**
+ * Creates a GsonBuilder instance that can be used to build Gson with various configuration
+ * settings. GsonBuilder follows the builder pattern, and it is typically used by first
+ * invoking various configuration methods to set desired options, and finally calling
+ * {@link #create()}.
+ */
+ public GsonBuilder() {
+ // add default exclusion strategies
+ exclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY);
+ exclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY);
+
+ // setup default values
+ ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS;
+ serializeInnerClasses = true;
+ prettyPrinting = false;
+ escapeHtmlChars = true;
+ modifierBasedExclusionStrategy = Gson.DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY;
+ excludeFieldsWithoutExposeAnnotation = false;
+ longSerializationPolicy = LongSerializationPolicy.DEFAULT;
+ fieldNamingPolicy = Gson.DEFAULT_NAMING_POLICY;
+ instanceCreators = new ParameterizedTypeHandlerMap>();
+ serializers = new ParameterizedTypeHandlerMap>();
+ deserializers = new ParameterizedTypeHandlerMap>();
+ serializeNulls = false;
+ dateStyle = DateFormat.DEFAULT;
+ timeStyle = DateFormat.DEFAULT;
+ serializeSpecialFloatingPointValues = false;
+ generateNonExecutableJson = false;
+ }
+
+ /**
+ * Configures Gson to enable versioning support.
+ *
+ * @param ignoreVersionsAfter any field or type marked with a version higher than this value
+ * are ignored during serialization or deserialization.
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ public GsonBuilder setVersion(double ignoreVersionsAfter) {
+ this.ignoreVersionsAfter = ignoreVersionsAfter;
+ return this;
+ }
+
+ /**
+ * Configures Gson to excludes all class fields that have the specified modifiers. By default,
+ * Gson will exclude all fields marked transient or static. This method will override that
+ * behavior.
+ *
+ * @param modifiers the field modifiers. You must use the modifiers specified in the
+ * {@link java.lang.reflect.Modifier} class. For example,
+ * {@link java.lang.reflect.Modifier#TRANSIENT},
+ * {@link java.lang.reflect.Modifier#STATIC}.
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ public GsonBuilder excludeFieldsWithModifiers(int... modifiers) {
+ modifierBasedExclusionStrategy = new ModifierBasedExclusionStrategy(modifiers);
+ return this;
+ }
+
+ /**
+ * Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some
+ * special text. This prevents attacks from third-party sites through script sourcing. See
+ * Gson Issue 42
+ * for details.
+ *
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.3
+ */
+ public GsonBuilder generateNonExecutableJson() {
+ this.generateNonExecutableJson = true;
+ return this;
+ }
+
+ /**
+ * Configures Gson to exclude all fields from consideration for serialization or deserialization
+ * that do not have the {@link org.mcteam.ancientgates.gson.annotations.Expose} annotation.
+ *
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ public GsonBuilder excludeFieldsWithoutExposeAnnotation() {
+ excludeFieldsWithoutExposeAnnotation = true;
+ return this;
+ }
+
+ /**
+ * Configure Gson to serialize null fields. By default, Gson omits all fields that are null
+ * during serialization.
+ *
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.2
+ */
+ public GsonBuilder serializeNulls() {
+ this.serializeNulls = true;
+ return this;
+ }
+
+ /**
+ * Configures Gson to exclude inner classes during serialization.
+ *
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.3
+ */
+ public GsonBuilder disableInnerClassSerialization() {
+ serializeInnerClasses = false;
+ return this;
+ }
+
+ /**
+ * Configures Gson to apply a specific serialization policy for {@code Long} and {@code long}
+ * objects.
+ *
+ * @param serializationPolicy the particular policy to use for serializing longs.
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.3
+ */
+ public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) {
+ this.longSerializationPolicy = serializationPolicy;
+ return this;
+ }
+
+ /**
+ * Configures Gson to apply a specific naming policy to an object's field during serialization
+ * and deserialization.
+ *
+ * @param namingConvention the JSON field naming convention to use for serialization and
+ * deserialization.
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) {
+ return setFieldNamingStrategy(namingConvention.getFieldNamingPolicy());
+ }
+
+ /**
+ * Configures Gson to apply a specific naming policy strategy to an object's field during
+ * serialization and deserialization.
+ *
+ * @param fieldNamingStrategy the actual naming strategy to apply to the fields
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.3
+ */
+ public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
+ return setFieldNamingStrategy(new FieldNamingStrategy2Adapter(fieldNamingStrategy));
+ }
+
+ /**
+ * Configures Gson to apply a specific naming policy strategy to an object's field during
+ * serialization and deserialization.
+ *
+ * @param fieldNamingStrategy the actual naming strategy to apply to the fields
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ GsonBuilder setFieldNamingStrategy(FieldNamingStrategy2 fieldNamingStrategy) {
+ this.fieldNamingPolicy =
+ new SerializedNameAnnotationInterceptingNamingPolicy(fieldNamingStrategy);
+ return this;
+ }
+
+ /**
+ * Configures Gson to apply a set of exclusion strategies during both serialization and
+ * deserialization. Each of the {@code strategies} will be applied as a disjunction rule.
+ * This means that if one of the {@code strategies} suggests that a field (or class) should be
+ * skipped then that field (or object) is skipped during serializaiton/deserialization.
+ *
+ * @param strategies the set of strategy object to apply during object (de)serialization.
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.4
+ */
+ public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) {
+ for (ExclusionStrategy strategy : strategies) {
+ exclusionStrategies.add(strategy);
+ }
+ return this;
+ }
+
+ /**
+ * Configures Gson to output Json that fits in a page for pretty printing. This option only
+ * affects Json serialization.
+ *
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ public GsonBuilder setPrettyPrinting() {
+ prettyPrinting = true;
+ return this;
+ }
+
+ /**
+ * By default, Gson escapes HTML characters such as < > etc. Use this option to configure
+ * Gson to pass-through HTML characters as is.
+ *
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.3
+ */
+ public GsonBuilder disableHtmlEscaping() {
+ this.escapeHtmlChars = false;
+ return this;
+ }
+
+ /**
+ * Configures Gson to serialize {@code Date} objects according to the pattern provided. You can
+ * call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation
+ * will be used to decide the serialization format.
+ *
+ *
Note that this pattern must abide by the convention provided by {@code SimpleDateFormat}
+ * class. See the documentation in {@link java.text.SimpleDateFormat} for more information on
+ * valid date and time patterns.
+ *
+ * @param pattern the pattern that dates will be serialized/deserialized to/from
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.2
+ */
+ public GsonBuilder setDateFormat(String pattern) {
+ // TODO(Joel): Make this fail fast if it is an invalid date format
+ this.datePattern = pattern;
+ return this;
+ }
+
+ /**
+ * Configures Gson to to serialize {@code Date} objects according to the style value provided.
+ * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
+ * invocation will be used to decide the serialization format.
+ *
+ *
Note that this style value should be one of the predefined constants in the
+ * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
+ * information on the valid style constants.
+ *
+ * @param style the predefined date style that date objects will be serialized/deserialized
+ * to/from
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.2
+ */
+ public GsonBuilder setDateFormat(int style) {
+ this.dateStyle = style;
+ this.datePattern = null;
+ return this;
+ }
+
+ /**
+ * Configures Gson to to serialize {@code Date} objects according to the style value provided.
+ * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
+ * invocation will be used to decide the serialization format.
+ *
+ *
Note that this style value should be one of the predefined constants in the
+ * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
+ * information on the valid style constants.
+ *
+ * @param dateStyle the predefined date style that date objects will be serialized/deserialized
+ * to/from
+ * @param timeStyle the predefined style for the time portion of the date objects
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.2
+ */
+ public GsonBuilder setDateFormat(int dateStyle, int timeStyle) {
+ this.dateStyle = dateStyle;
+ this.timeStyle = timeStyle;
+ this.datePattern = null;
+ return this;
+ }
+
+ /**
+ * Configures Gson for custom serialization or deserialization. This method combines the
+ * registration of an {@link InstanceCreator}, {@link JsonSerializer}, and a
+ * {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements
+ * all the required interfaces for custom serialization with Gson. If an instance creator,
+ * serializer or deserializer was previously registered for the specified {@code type}, it is
+ * overwritten.
+ *
+ * @param type the type definition for the type adapter being registered
+ * @param typeAdapter This object must implement at least one of the {@link InstanceCreator},
+ * {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
+ Preconditions.checkArgument(typeAdapter instanceof JsonSerializer>
+ || typeAdapter instanceof JsonDeserializer> || typeAdapter instanceof InstanceCreator>);
+ if (typeAdapter instanceof InstanceCreator>) {
+ registerInstanceCreator(type, (InstanceCreator>) typeAdapter);
+ }
+ if (typeAdapter instanceof JsonSerializer>) {
+ registerSerializer(type, (JsonSerializer>) typeAdapter);
+ }
+ if (typeAdapter instanceof JsonDeserializer>) {
+ registerDeserializer(type, (JsonDeserializer>) typeAdapter);
+ }
+ return this;
+ }
+
+ /**
+ * Configures Gson to use a custom {@link InstanceCreator} for the specified type. If an instance
+ * creator was previously registered for the specified class, it is overwritten. Since this method
+ * takes a type instead of a Class object, it can be used to register a specific handler for a
+ * generic type corresponding to a raw type.
+ *
+ * @param the type for which instance creator is being registered
+ * @param typeOfT The Type definition for T
+ * @param instanceCreator the instance creator for T
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ private GsonBuilder registerInstanceCreator(Type typeOfT,
+ InstanceCreator extends T> instanceCreator) {
+ instanceCreators.register(typeOfT, instanceCreator);
+ return this;
+ }
+
+ /**
+ * Configures Gson to use a custom JSON serializer for the specified type. You should use this
+ * method if you want to register different serializers for different generic types corresponding
+ * to a raw type.
+ *
+ * @param the type for which the serializer is being registered
+ * @param typeOfT The type definition for T
+ * @param serializer the custom serializer
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ private GsonBuilder registerSerializer(Type typeOfT, final JsonSerializer serializer) {
+ serializers.register(typeOfT, serializer);
+ return this;
+ }
+
+ /**
+ * Configures Gson to use a custom JSON deserializer for the specified type. You should use this
+ * method if you want to register different deserializers for different generic types
+ * corresponding to a raw type.
+ *
+ * @param the type for which the deserializer is being registered
+ * @param typeOfT The type definition for T
+ * @param deserializer the custom deserializer
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ */
+ private GsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer deserializer) {
+ deserializers.register(typeOfT, new JsonDeserializerExceptionWrapper(deserializer));
+ return this;
+ }
+
+ /**
+ * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
+ * This method combines the registration of an {@link InstanceCreator}, {@link JsonSerializer},
+ * and a {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter}
+ * implements all the required interfaces for custom serialization with Gson.
+ * If an instance creator, serializer or deserializer was previously registered for the specified
+ * type hierarchy, it is overwritten. If an instance creator, serializer or deserializer is
+ * registered for a specific type in the type hierarchy, it will be invoked instead of the one
+ * registered for the type hierarchy.
+ *
+ * @param baseType the class definition for the type adapter being registered for the base class
+ * or interface
+ * @param typeAdapter This object must implement at least one of the {@link InstanceCreator},
+ * {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.7
+ */
+ GsonBuilder registerTypeHierarchyAdapter(Class> baseType, Object typeAdapter) {
+ Preconditions.checkArgument(typeAdapter instanceof JsonSerializer>
+ || typeAdapter instanceof JsonDeserializer> || typeAdapter instanceof InstanceCreator>);
+ if (typeAdapter instanceof InstanceCreator>) {
+ registerInstanceCreatorForTypeHierarchy(baseType, (InstanceCreator>) typeAdapter);
+ }
+ if (typeAdapter instanceof JsonSerializer>) {
+ registerSerializerForTypeHierarchy(baseType, (JsonSerializer>) typeAdapter);
+ }
+ if (typeAdapter instanceof JsonDeserializer>) {
+ registerDeserializerForTypeHierarchy(baseType, (JsonDeserializer>) typeAdapter);
+ }
+ return this;
+ }
+
+ private GsonBuilder registerInstanceCreatorForTypeHierarchy(Class> classOfT,
+ InstanceCreator extends T> instanceCreator) {
+ instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator);
+ return this;
+ }
+
+ private GsonBuilder registerSerializerForTypeHierarchy(Class> classOfT,
+ final JsonSerializer serializer) {
+ serializers.registerForTypeHierarchy(classOfT, serializer);
+ return this;
+ }
+
+ private GsonBuilder registerDeserializerForTypeHierarchy(Class> classOfT,
+ JsonDeserializer deserializer) {
+ deserializers.registerForTypeHierarchy(classOfT,
+ new JsonDeserializerExceptionWrapper(deserializer));
+ return this;
+ }
+
+ /**
+ * Section 2.4 of JSON specification disallows
+ * special double values (NaN, Infinity, -Infinity). However,
+ * Javascript
+ * specification (see section 4.3.20, 4.3.22, 4.3.23) allows these values as valid Javascript
+ * values. Moreover, most JavaScript engines will accept these special values in JSON without
+ * problem. So, at a practical level, it makes sense to accept these values as valid JSON even
+ * though JSON specification disallows them.
+ *
+ *
Gson always accepts these special values during deserialization. However, it outputs
+ * strictly compliant JSON. Hence, if it encounters a float value {@link Float#NaN},
+ * {@link Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, or a double value
+ * {@link Double#NaN}, {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it
+ * will throw an {@link IllegalArgumentException}. This method provides a way to override the
+ * default behavior when you know that the JSON receiver will be able to handle these special
+ * values.
+ *
+ * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+ * @since 1.3
+ */
+ public GsonBuilder serializeSpecialFloatingPointValues() {
+ this.serializeSpecialFloatingPointValues = true;
+ return this;
+ }
+
+ /**
+ * Creates a {@link Gson} instance based on the current configuration. This method is free of
+ * side-effects to this {@code GsonBuilder} instance and hence can be called multiple times.
+ *
+ * @return an instance of Gson configured with the options currently set in this builder
+ */
+ public Gson create() {
+ List serializationStrategies =
+ new LinkedList(exclusionStrategies);
+ List deserializationStrategies =
+ new LinkedList(exclusionStrategies);
+
+ serializationStrategies.add(modifierBasedExclusionStrategy);
+ deserializationStrategies.add(modifierBasedExclusionStrategy);
+
+ if (!serializeInnerClasses) {
+ serializationStrategies.add(innerClassExclusionStrategy);
+ deserializationStrategies.add(innerClassExclusionStrategy);
+ }
+ if (ignoreVersionsAfter != VersionConstants.IGNORE_VERSIONS) {
+ serializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
+ deserializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
+ }
+ if (excludeFieldsWithoutExposeAnnotation) {
+ serializationStrategies.add(exposeAnnotationSerializationExclusionStrategy);
+ deserializationStrategies.add(exposeAnnotationDeserializationExclusionStrategy);
+ }
+ ExclusionStrategy serializationExclusionStrategy =
+ new DisjunctionExclusionStrategy(serializationStrategies);
+ ExclusionStrategy deserializationExclusionStrategy =
+ new DisjunctionExclusionStrategy(deserializationStrategies);
+
+ ParameterizedTypeHandlerMap> customSerializers = serializers.copyOf();
+ ParameterizedTypeHandlerMap> customDeserializers = deserializers.copyOf();
+ addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers,
+ customDeserializers);
+
+ customSerializers.registerIfAbsent(DefaultTypeAdapters.getDefaultSerializers(
+ serializeSpecialFloatingPointValues, longSerializationPolicy));
+
+ customDeserializers.registerIfAbsent(DefaultTypeAdapters.getDefaultDeserializers());
+
+ ParameterizedTypeHandlerMap> customInstanceCreators =
+ instanceCreators.copyOf();
+ customInstanceCreators.registerIfAbsent(DefaultTypeAdapters.getDefaultInstanceCreators());
+
+ customSerializers.makeUnmodifiable();
+ customDeserializers.makeUnmodifiable();
+ instanceCreators.makeUnmodifiable();
+
+ MappedObjectConstructor objConstructor = new MappedObjectConstructor(customInstanceCreators);
+
+ Gson gson = new Gson(serializationExclusionStrategy, deserializationExclusionStrategy,
+ fieldNamingPolicy, objConstructor, serializeNulls, customSerializers,
+ customDeserializers, generateNonExecutableJson, escapeHtmlChars, prettyPrinting);
+ return gson;
+ }
+
+ private static void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
+ ParameterizedTypeHandlerMap> serializers,
+ ParameterizedTypeHandlerMap> deserializers) {
+ DefaultDateTypeAdapter dateTypeAdapter = null;
+ if (datePattern != null && !"".equals(datePattern.trim())) {
+ dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
+ } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
+ dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
+ }
+
+ if (dateTypeAdapter != null) {
+ if (!serializers.hasSpecificHandlerFor(Date.class)) {
+ serializers.register(Date.class, dateTypeAdapter);
+ }
+ if (!deserializers.hasSpecificHandlerFor(Date.class)) {
+ deserializers.register(Date.class, dateTypeAdapter);
+ }
+ }
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/InnerClassExclusionStrategy.java b/src/org/mcteam/ancientgates/gson/InnerClassExclusionStrategy.java
new file mode 100644
index 0000000..dd6a904
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/InnerClassExclusionStrategy.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * Strategy for excluding inner classes.
+ *
+ * @author Joel Leitch
+ */
+class InnerClassExclusionStrategy implements ExclusionStrategy {
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ return isInnerClass(f.getDeclaredClass());
+ }
+
+ public boolean shouldSkipClass(Class> clazz) {
+ return isInnerClass(clazz);
+ }
+
+ private boolean isInnerClass(Class> clazz) {
+ return clazz.isMemberClass() && !isStatic(clazz);
+ }
+
+ private boolean isStatic(Class> clazz) {
+ return (clazz.getModifiers() & Modifier.STATIC) != 0;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/InstanceCreator.java b/src/org/mcteam/ancientgates/gson/InstanceCreator.java
new file mode 100644
index 0000000..e8d2009
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/InstanceCreator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * This interface is implemented to create instances of a class that does not define a no-args
+ * constructor. If you can modify the class, you should instead add a private, or public
+ * no-args constructor. However, that is not possible for library classes, such as JDK classes, or
+ * a third-party library that you do not have source-code of. In such cases, you should define an
+ * instance creator for the class. Implementations of this interface should be registered with
+ * {@link GsonBuilder#registerTypeAdapter(Type, Object)} method before Gson will be able to use
+ * them.
+ *
Let us look at an example where defining an InstanceCreator might be useful. The
+ * {@code Id} class defined below does not have a default no-args constructor.
+ *
+ *
+ * public class Id<T> {
+ * private final Class<T> clazz;
+ * private final long value;
+ * public Id(Class<T> clazz, long value) {
+ * this.clazz = clazz;
+ * this.value = value;
+ * }
+ * }
+ *
+ *
+ *
If Gson encounters an object of type {@code Id} during deserialization, it will throw an
+ * exception. The easiest way to solve this problem will be to add a (public or private) no-args
+ * constructor as follows:
However, let us assume that the developer does not have access to the source-code of the
+ * {@code Id} class, or does not want to define a no-args constructor for it. The developer
+ * can solve this problem by defining an {@code InstanceCreator} for {@code Id}:
+ *
+ *
+ * class IdInstanceCreator implements InstanceCreator<Id> {
+ * public Id createInstance(Type type) {
+ * return new Id(Object.class, 0L);
+ * }
+ * }
+ *
+ *
+ *
Note that it does not matter what the fields of the created instance contain since Gson will
+ * overwrite them with the deserialized values specified in Json. You should also ensure that a
+ * new object is returned, not a common object since its fields will be overwritten.
+ * The developer will need to register {@code IdInstanceCreator} with Gson as follows:
+ *
+ *
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdInstanceCreator()).create();
+ *
+ *
+ * @param the type of object that will be created by this implementation.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface InstanceCreator {
+
+ /**
+ * Gson invokes this call-back method during deserialization to create an instance of the
+ * specified type. The fields of the returned instance are overwritten with the data present
+ * in the Json. Since the prior contents of the object are destroyed and overwritten, do not
+ * return an instance that is useful elsewhere. In particular, do not return a common instance,
+ * always use {@code new} to create a new instance.
+ *
+ * @param type the parameterized T represented as a {@link Type}.
+ * @return a default object instance of type T.
+ */
+ public T createInstance(Type type);
+}
diff --git a/src/org/mcteam/ancientgates/gson/JavaFieldNamingPolicy.java b/src/org/mcteam/ancientgates/gson/JavaFieldNamingPolicy.java
new file mode 100644
index 0000000..2b22710
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JavaFieldNamingPolicy.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * A simple implementation of the {@link FieldNamingStrategy2} interface such that it does not
+ * perform any string translation of the incoming field name.
+ *
+ *
The following is an example:
+ *
+ *
+ * class IntWrapper {
+ * public int integerField = 0;
+ * }
+ *
+ * JavaFieldNamingPolicy policy = new JavaFieldNamingPolicy();
+ * String translatedFieldName =
+ * policy.translateName(IntWrapper.class.getField("integerField"));
+ *
+ * assert("integerField".equals(translatedFieldName));
+ *
+ *
+ *
This is the default {@link FieldNamingStrategy2} used by Gson.
+ *
+ * @author Joel Leitch
+ */
+final class JavaFieldNamingPolicy extends RecursiveFieldNamingPolicy {
+
+ @Override
+ protected String translateName(String target, Type fieldType, Collection annotations) {
+ return target;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonArray.java b/src/org/mcteam/ancientgates/gson/JsonArray.java
new file mode 100644
index 0000000..425b3d5
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonArray.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A class representing an array type in Json. An array is a list of {@link JsonElement}s each of
+ * which can be of a different type. This is an ordered list, meaning that the order in which
+ * elements are added is preserved.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonArray extends JsonElement implements Iterable {
+ private final List elements;
+
+ /**
+ * Creates an empty JsonArray.
+ */
+ public JsonArray() {
+ elements = new ArrayList();
+ }
+
+ /**
+ * Adds the specified element to self.
+ *
+ * @param element the element that needs to be added to the array.
+ */
+ public void add(JsonElement element) {
+ if (element == null) {
+ element = JsonNull.createJsonNull();
+ }
+ elements.add(element);
+ }
+
+ /**
+ * Adds all the elements of the specified array to self.
+ *
+ * @param array the array whose elements need to be added to the array.
+ */
+ public void addAll(JsonArray array) {
+ elements.addAll(array.elements);
+ }
+
+ /**
+ * Reverses the elements of the array.
+ */
+ void reverse() {
+ Collections.reverse(elements);
+ }
+
+ /**
+ * Returns the number of elements in the array.
+ *
+ * @return the number of elements in the array.
+ */
+ public int size() {
+ return elements.size();
+ }
+
+ /**
+ * Returns an iterator to navigate the elemetns of the array. Since the array is an ordered list,
+ * the iterator navigates the elements in the order they were inserted.
+ *
+ * @return an iterator to navigate the elements of the array.
+ */
+ public Iterator iterator() {
+ return elements.iterator();
+ }
+
+ /**
+ * Returns the ith element of the array.
+ *
+ * @param i the index of the element that is being sought.
+ * @return the element present at the ith index.
+ * @throws IndexOutOfBoundsException if i is negative or greater than or equal to the
+ * {@link #size()} of the array.
+ */
+ public JsonElement get(int i) {
+ return elements.get(i);
+ }
+
+ /**
+ * convenience method to get this array as a {@link Number} if it contains a single element.
+ *
+ * @return get this element as a number if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+ * is not a valid Number.
+ * @throws IllegalStateException if the array has more than one element.
+ */
+ @Override
+ public Number getAsNumber() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsNumber();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as a {@link String} if it contains a single element.
+ *
+ * @return get this element as a String if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+ * is not a valid String.
+ * @throws IllegalStateException if the array has more than one element.
+ */
+ @Override
+ public String getAsString() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsString();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as a double if it contains a single element.
+ *
+ * @return get this element as a double if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+ * is not a valid double.
+ * @throws IllegalStateException if the array has more than one element.
+ */
+ @Override
+ public double getAsDouble() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsDouble();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as a {@link BigDecimal} if it contains a single element.
+ *
+ * @return get this element as a {@link BigDecimal} if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive}.
+ * @throws NumberFormatException if the element at index 0 is not a valid {@link BigDecimal}.
+ * @throws IllegalStateException if the array has more than one element.
+ * @since 1.2
+ */
+ @Override
+ public BigDecimal getAsBigDecimal() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsBigDecimal();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as a {@link BigInteger} if it contains a single element.
+ *
+ * @return get this element as a {@link BigInteger} if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive}.
+ * @throws NumberFormatException if the element at index 0 is not a valid {@link BigInteger}.
+ * @throws IllegalStateException if the array has more than one element.
+ * @since 1.2
+ */
+ @Override
+ public BigInteger getAsBigInteger() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsBigInteger();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as a float if it contains a single element.
+ *
+ * @return get this element as a float if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+ * is not a valid float.
+ * @throws IllegalStateException if the array has more than one element.
+ */
+ @Override
+ public float getAsFloat() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsFloat();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as a long if it contains a single element.
+ *
+ * @return get this element as a long if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+ * is not a valid long.
+ * @throws IllegalStateException if the array has more than one element.
+ */
+ @Override
+ public long getAsLong() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsLong();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as an integer if it contains a single element.
+ *
+ * @return get this element as an integer if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+ * is not a valid integer.
+ * @throws IllegalStateException if the array has more than one element.
+ */
+ @Override
+ public int getAsInt() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsInt();
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public byte getAsByte() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsByte();
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public char getAsCharacter() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsCharacter();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as a primitive short if it contains a single element.
+ *
+ * @return get this element as a primitive short if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+ * is not a valid short.
+ * @throws IllegalStateException if the array has more than one element.
+ */
+ @Override
+ public short getAsShort() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsShort();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as a boolean if it contains a single element.
+ *
+ * @return get this element as a boolean if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+ * is not a valid boolean.
+ * @throws IllegalStateException if the array has more than one element.
+ */
+ @Override
+ public boolean getAsBoolean() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsBoolean();
+ }
+ throw new IllegalStateException();
+ }
+
+ /**
+ * convenience method to get this array as an Object if it contains a single element.
+ *
+ * @return get this element as an Object if it is single element array.
+ * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+ * is not a valid Object.
+ * @throws IllegalStateException if the array has more than one element.
+ */
+ @Override
+ Object getAsObject() {
+ if (elements.size() == 1) {
+ return elements.get(0).getAsObject();
+ }
+ throw new IllegalStateException();
+ }
+
+ @Override
+ protected void toString(Appendable sb, Escaper escaper) throws IOException {
+ sb.append('[');
+ boolean first = true;
+ for (JsonElement element : elements) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ element.toString(sb, escaper);
+ }
+ sb.append(']');
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonArrayDeserializationVisitor.java b/src/org/mcteam/ancientgates/gson/JsonArrayDeserializationVisitor.java
new file mode 100644
index 0000000..d9c9bfa
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonArrayDeserializationVisitor.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Type;
+
+/**
+ * A visitor that populates fields of an object with data from its equivalent
+ * JSON representation
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class JsonArrayDeserializationVisitor extends JsonDeserializationVisitor {
+
+ JsonArrayDeserializationVisitor(JsonArray jsonArray, Type arrayType,
+ ObjectNavigatorFactory factory, ObjectConstructor objectConstructor,
+ ParameterizedTypeHandlerMap> deserializers,
+ JsonDeserializationContext context) {
+ super(jsonArray, arrayType, factory, objectConstructor, deserializers, context);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected T constructTarget() {
+
+ TypeInfo typeInfo = new TypeInfo(targetType);
+
+ if (!json.isJsonArray()) {
+ throw new JsonParseException("Expecting array found: " + json);
+ }
+ JsonArray jsonArray = json.getAsJsonArray();
+ if (typeInfo.isArray()) {
+ TypeInfoArray arrayTypeInfo = TypeInfoFactory.getTypeInfoForArray(targetType);
+ // We know that we are getting back an array of the required type, so
+ // this typecasting is safe.
+ return (T) objectConstructor.constructArray(arrayTypeInfo.getSecondLevelType(),
+ jsonArray.size());
+ }
+ // is a collection
+ return (T) objectConstructor.construct(typeInfo.getRawClass());
+ }
+
+ public void visitArray(Object array, Type arrayType) {
+ if (!json.isJsonArray()) {
+ throw new JsonParseException("Expecting array found: " + json);
+ }
+ JsonArray jsonArray = json.getAsJsonArray();
+ TypeInfoArray arrayTypeInfo = TypeInfoFactory.getTypeInfoForArray(arrayType);
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JsonElement jsonChild = jsonArray.get(i);
+ Object child;
+
+ if (jsonChild == null || jsonChild.isJsonNull()) {
+ child = null;
+ } else if (jsonChild instanceof JsonObject) {
+ child = visitChildAsObject(arrayTypeInfo.getComponentRawType(), jsonChild);
+ } else if (jsonChild instanceof JsonArray) {
+ child = visitChildAsArray(arrayTypeInfo.getSecondLevelType(), jsonChild.getAsJsonArray());
+ } else if (jsonChild instanceof JsonPrimitive) {
+ child = visitChildAsObject(arrayTypeInfo.getComponentRawType(),
+ jsonChild.getAsJsonPrimitive());
+ } else {
+ throw new IllegalStateException();
+ }
+ Array.set(array, i, child);
+ }
+ }
+
+ // We should not implement any other method from Visitor interface since
+ // all other methods should be invoked on JsonObjectDeserializationVisitor
+ // instead.
+
+ public void startVisitingObject(Object node) {
+ throw new JsonParseException("Expecting array but found object: " + node);
+ }
+
+ public void visitArrayField(FieldAttributes f, Type typeOfF, Object obj) {
+ throw new JsonParseException("Expecting array but found array field " + f.getName() + ": "
+ + obj);
+ }
+
+ public void visitObjectField(FieldAttributes f, Type typeOfF, Object obj) {
+ throw new JsonParseException("Expecting array but found object field " + f.getName() + ": "
+ + obj);
+ }
+
+ public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type actualTypeOfField, Object parent) {
+ throw new JsonParseException("Expecting array but found field " + f.getName() + ": "
+ + parent);
+ }
+
+ public void visitPrimitive(Object primitive) {
+ throw new JsonParseException(
+ "Type information is unavailable, and the target is not a primitive: " + json);
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonDeserializationContext.java b/src/org/mcteam/ancientgates/gson/JsonDeserializationContext.java
new file mode 100644
index 0000000..61b5aa3
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonDeserializationContext.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Context for deserialization that is passed to a custom deserializer during invocation of its
+ * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)}
+ * method.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface JsonDeserializationContext {
+
+ /**
+ * Invokes default deserialization on the specified object. It should never be invoked on
+ * the element received as a parameter of the
+ * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} method. Doing
+ * so will result in an infinite loop since Gson will in-turn call the custom deserializer again.
+
+ * @param json the parse tree.
+ * @param typeOfT type of the expected return value.
+ * @param The type of the deserialized object.
+ * @return An object of type typeOfT.
+ * @throws JsonParseException if the parse tree does not contain expected data.
+ */
+ public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException;
+}
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/gson/JsonDeserializationContextDefault.java b/src/org/mcteam/ancientgates/gson/JsonDeserializationContextDefault.java
new file mode 100644
index 0000000..8451019
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonDeserializationContextDefault.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * implementation of a deserialization context for Gson
+ *
+ * @author Inderjeet Singh
+ */
+final class JsonDeserializationContextDefault implements JsonDeserializationContext {
+
+ private final ObjectNavigatorFactory navigatorFactory;
+ private final ParameterizedTypeHandlerMap> deserializers;
+ private final MappedObjectConstructor objectConstructor;
+
+ JsonDeserializationContextDefault(ObjectNavigatorFactory navigatorFactory,
+ ParameterizedTypeHandlerMap> deserializers,
+ MappedObjectConstructor objectConstructor) {
+ this.navigatorFactory = navigatorFactory;
+ this.deserializers = deserializers;
+ this.objectConstructor = objectConstructor;
+ }
+
+ ObjectConstructor getObjectConstructor() {
+ return objectConstructor;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
+ if (json == null || json.isJsonNull()) {
+ return null;
+ } else if (json.isJsonArray()) {
+ return (T) fromJsonArray(typeOfT, json.getAsJsonArray(), this);
+ } else if (json.isJsonObject()) {
+ return (T) fromJsonObject(typeOfT, json.getAsJsonObject(), this);
+ } else if (json.isJsonPrimitive()) {
+ return (T) fromJsonPrimitive(typeOfT, json.getAsJsonPrimitive(), this);
+ } else {
+ throw new JsonParseException("Failed parsing JSON source: " + json + " to Json");
+ }
+ }
+
+ private T fromJsonArray(Type arrayType, JsonArray jsonArray,
+ JsonDeserializationContext context) throws JsonParseException {
+ JsonArrayDeserializationVisitor visitor = new JsonArrayDeserializationVisitor(
+ jsonArray, arrayType, navigatorFactory, objectConstructor, deserializers, context);
+ ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(null, arrayType, true));
+ on.accept(visitor);
+ return visitor.getTarget();
+ }
+
+ private T fromJsonObject(Type typeOfT, JsonObject jsonObject,
+ JsonDeserializationContext context) throws JsonParseException {
+ JsonObjectDeserializationVisitor visitor = new JsonObjectDeserializationVisitor(
+ jsonObject, typeOfT, navigatorFactory, objectConstructor, deserializers, context);
+ ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(null, typeOfT, true));
+ on.accept(visitor);
+ return visitor.getTarget();
+ }
+
+ @SuppressWarnings("unchecked")
+ private T fromJsonPrimitive(Type typeOfT, JsonPrimitive json,
+ JsonDeserializationContext context) throws JsonParseException {
+ JsonObjectDeserializationVisitor visitor = new JsonObjectDeserializationVisitor(
+ json, typeOfT, navigatorFactory, objectConstructor, deserializers, context);
+ ObjectNavigator on =
+ navigatorFactory.create(new ObjectTypePair(json.getAsObject(), typeOfT, true));
+ on.accept(visitor);
+ Object target = visitor.getTarget();
+ return (T) target;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonDeserializationVisitor.java b/src/org/mcteam/ancientgates/gson/JsonDeserializationVisitor.java
new file mode 100644
index 0000000..69911db
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonDeserializationVisitor.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Abstract data value container for the {@link ObjectNavigator.Visitor}
+ * implementations. This class exposes the {@link #getTarget()} method
+ * which returns the class that was visited by this object.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+abstract class JsonDeserializationVisitor implements ObjectNavigator.Visitor {
+
+ protected final ObjectNavigatorFactory factory;
+ protected final ObjectConstructor objectConstructor;
+ protected final ParameterizedTypeHandlerMap> deserializers;
+ protected T target;
+ protected final JsonElement json;
+ protected final Type targetType;
+ protected final JsonDeserializationContext context;
+ protected boolean constructed;
+
+ public JsonDeserializationVisitor(JsonElement json, Type targetType,
+ ObjectNavigatorFactory factory, ObjectConstructor objectConstructor,
+ ParameterizedTypeHandlerMap> deserializers,
+ JsonDeserializationContext context) {
+ Preconditions.checkNotNull(json);
+ this.targetType = targetType;
+ this.factory = factory;
+ this.objectConstructor = objectConstructor;
+ this.deserializers = deserializers;
+ this.json = json;
+ this.context = context;
+ this.constructed = false;
+ }
+
+ public T getTarget() {
+ if (!constructed) {
+ target = constructTarget();
+ constructed = true;
+ }
+ return target;
+ }
+
+ protected abstract T constructTarget();
+
+ public void start(ObjectTypePair node) {
+ }
+
+ public void end(ObjectTypePair node) {
+ }
+
+ @SuppressWarnings("unchecked")
+ public final boolean visitUsingCustomHandler(ObjectTypePair objTypePair) {
+ Pair, ObjectTypePair> pair = objTypePair.getMatchingHandler(deserializers);
+ if (pair == null) {
+ return false;
+ }
+ Object value = invokeCustomDeserializer(json, pair);
+ target = (T) value;
+ constructed = true;
+ return true;
+ }
+
+ protected Object invokeCustomDeserializer(JsonElement element,
+ Pair, ObjectTypePair> pair) {
+ if (element == null || element.isJsonNull()) {
+ return null;
+ }
+ Type objType = pair.second.type;
+ return (pair.first).deserialize(element, objType, context);
+ }
+
+ final Object visitChildAsObject(Type childType, JsonElement jsonChild) {
+ JsonDeserializationVisitor> childVisitor =
+ new JsonObjectDeserializationVisitor(jsonChild, childType,
+ factory, objectConstructor, deserializers, context);
+ return visitChild(childType, childVisitor);
+ }
+
+ final Object visitChildAsArray(Type childType, JsonArray jsonChild) {
+ JsonDeserializationVisitor> childVisitor =
+ new JsonArrayDeserializationVisitor(jsonChild.getAsJsonArray(), childType,
+ factory, objectConstructor, deserializers, context);
+ return visitChild(childType, childVisitor);
+ }
+
+ private Object visitChild(Type type, JsonDeserializationVisitor> childVisitor) {
+ ObjectNavigator on = factory.create(new ObjectTypePair(null, type, false));
+ on.accept(childVisitor);
+ // the underlying object may have changed during the construction phase
+ // This happens primarily because of custom deserializers
+ return childVisitor.getTarget();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonDeserializer.java b/src/org/mcteam/ancientgates/gson/JsonDeserializer.java
new file mode 100644
index 0000000..ff3e46d
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonDeserializer.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ *
Interface representing a custom deserializer for Json. You should write a custom
+ * deserializer, if you are not happy with the default deserialization done by Gson. You will
+ * also need to register this deserializer through
+ * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.
+ *
+ *
Let us look at example where defining a deserializer will be useful. The {@code Id} class
+ * defined below has two fields: {@code clazz} and {@code value}.
+ *
+ *
+ * public class Id<T> {
+ * private final Class<T> clazz;
+ * private final long value;
+ * public Id(Class<T> clazz, long value) {
+ * this.clazz = clazz;
+ * this.value = value;
+ * }
+ * public long getValue() {
+ * return value;
+ * }
+ * }
+ *
+ *
+ *
The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the
+ * Json string to be {"clazz":com.foo.MyObject,"value":20}. Suppose, you already know
+ * the type of the field that the {@code Id} will be deserialized into, and hence just want to
+ * deserialize it from a Json string {@code 20}. You can achieve that by writing a custom
+ * deserializer:
+ *
+ *
+ * class IdDeserializer implements JsonDeserializer<Id>() {
+ * public Id fromJson(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ * throws JsonParseException {
+ * return (Id) new Id((Class)typeOfT, id.getValue());
+ * }
+ *
+ *
+ *
You will also need to register {@code IdDeserializer} with Gson as follows:
+ *
+ *
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdDeserializer()).create();
+ *
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @param type for which the deserializer is being registered. It is possible that a
+ * deserializer may be asked to deserialize a specific generic type of the T.
+ */
+public interface JsonDeserializer {
+
+ /**
+ * Gson invokes this call-back method during deserialization when it encounters a field of the
+ * specified type.
+ *
In the implementation of this call-back method, you should consider invoking
+ * {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects
+ * for any non-trivial field of the returned object. However, you should never invoke it on the
+ * the same type passing {@code json} since that will cause an infinite loop (Gson will call your
+ * call-back method again).
+ *
+ * @param json The Json data being deserialized
+ * @param typeOfT The type of the Object to deserialize to
+ * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T}
+ * @throws JsonParseException if json is not in the expected format of {@code typeofT}
+ */
+ public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException;
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonDeserializerExceptionWrapper.java b/src/org/mcteam/ancientgates/gson/JsonDeserializerExceptionWrapper.java
new file mode 100644
index 0000000..266949a
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonDeserializerExceptionWrapper.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Decorators a {@code JsonDeserializer} instance with exception handling. This wrapper class
+ * ensures that a {@code JsonDeserializer} will not propagate any exception other than a
+ * {@link JsonParseException}.
+ *
+ * @param type of the deserializer being wrapped.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+class JsonDeserializerExceptionWrapper implements JsonDeserializer {
+
+ private final JsonDeserializer delegate;
+
+ /**
+ * Returns a wrapped {@link JsonDeserializer} object that has been decorated with
+ * {@link JsonParseException} handling.
+ *
+ * @param delegate the {@code JsonDeserializer} instance to be wrapped.
+ * @throws IllegalArgumentException if {@code delegate} is {@code null}.
+ */
+ JsonDeserializerExceptionWrapper(JsonDeserializer delegate) {
+ Preconditions.checkNotNull(delegate);
+ this.delegate = delegate;
+ }
+
+ public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ try {
+ return delegate.deserialize(json, typeOfT, context);
+ } catch (JsonParseException e) {
+ // just rethrow the exception
+ throw e;
+ } catch (Exception e) {
+ // rethrow as a JsonParseException
+ StringBuilder errorMsg = new StringBuilder()
+ .append("The JsonDeserializer ")
+ .append(delegate)
+ .append(" failed to deserialized json object ")
+ .append(json)
+ .append(" given the type ")
+ .append(typeOfT);
+ throw new JsonParseException(errorMsg.toString(), e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+}
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/gson/JsonElement.java b/src/org/mcteam/ancientgates/gson/JsonElement.java
new file mode 100644
index 0000000..387af9a
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonElement.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A class representing an element of Json. It could either be a {@link JsonObject}, a
+ * {@link JsonArray}, a {@link JsonPrimitive} or a {@link JsonNull}.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public abstract class JsonElement {
+ private static final Escaper BASIC_ESCAPER = new Escaper(false);
+
+ /**
+ * provides check for verifying if this element is an array or not.
+ *
+ * @return true if this element is of type {@link JsonArray}, false otherwise.
+ */
+ public boolean isJsonArray() {
+ return this instanceof JsonArray;
+ }
+
+ /**
+ * provides check for verifying if this element is a Json object or not.
+ *
+ * @return true if this element is of type {@link JsonObject}, false otherwise.
+ */
+ public boolean isJsonObject() {
+ return this instanceof JsonObject;
+ }
+
+ /**
+ * provides check for verifying if this element is a primitive or not.
+ *
+ * @return true if this element is of type {@link JsonPrimitive}, false otherwise.
+ */
+ public boolean isJsonPrimitive() {
+ return this instanceof JsonPrimitive;
+ }
+
+ /**
+ * provides check for verifying if this element represents a null value or not.
+ *
+ * @return true if this element is of type {@link JsonNull}, false otherwise.
+ * @since 1.2
+ */
+ public boolean isJsonNull() {
+ return this instanceof JsonNull;
+ }
+
+ /**
+ * convenience method to get this element as a {@link JsonObject}. If the element is of some
+ * other type, a {@link ClassCastException} will result. Hence it is best to use this method
+ * after ensuring that this element is of the desired type by calling {@link #isJsonObject()}
+ * first.
+ *
+ * @return get this element as a {@link JsonObject}.
+ * @throws IllegalStateException if the element is of another type.
+ */
+ public JsonObject getAsJsonObject() {
+ if (isJsonObject()) {
+ return (JsonObject) this;
+ }
+ throw new IllegalStateException("This is not a JSON Object.");
+ }
+
+ /**
+ * convenience method to get this element as a {@link JsonArray}. If the element is of some
+ * other type, a {@link ClassCastException} will result. Hence it is best to use this method
+ * after ensuring that this element is of the desired type by calling {@link #isJsonArray()}
+ * first.
+ *
+ * @return get this element as a {@link JsonArray}.
+ * @throws IllegalStateException if the element is of another type.
+ */
+ public JsonArray getAsJsonArray() {
+ if (isJsonArray()) {
+ return (JsonArray) this;
+ }
+ throw new IllegalStateException("This is not a JSON Array.");
+ }
+
+ /**
+ * convenience method to get this element as a {@link JsonPrimitive}. If the element is of some
+ * other type, a {@link ClassCastException} will result. Hence it is best to use this method
+ * after ensuring that this element is of the desired type by calling {@link #isJsonPrimitive()}
+ * first.
+ *
+ * @return get this element as a {@link JsonPrimitive}.
+ * @throws IllegalStateException if the element is of another type.
+ */
+ public JsonPrimitive getAsJsonPrimitive() {
+ if (isJsonPrimitive()) {
+ return (JsonPrimitive) this;
+ }
+ throw new IllegalStateException("This is not a JSON Primitive.");
+ }
+
+ /**
+ * convenience method to get this element as a {@link JsonNull}. If the element is of some
+ * other type, a {@link ClassCastException} will result. Hence it is best to use this method
+ * after ensuring that this element is of the desired type by calling {@link #isJsonNull()}
+ * first.
+ *
+ * @return get this element as a {@link JsonNull}.
+ * @throws IllegalStateException if the element is of another type.
+ * @since 1.2
+ */
+ public JsonNull getAsJsonNull() {
+ if (isJsonNull()) {
+ return (JsonNull) this;
+ }
+ throw new IllegalStateException("This is not a JSON Null.");
+ }
+
+ /**
+ * convenience method to get this element as a boolean value.
+ *
+ * @return get this element as a primitive boolean value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * boolean value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ public boolean getAsBoolean() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a {@link Boolean} value.
+ *
+ * @return get this element as a {@link Boolean} value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * boolean value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ Boolean getAsBooleanWrapper() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a {@link Number}.
+ *
+ * @return get this element as a {@link Number}.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * number.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ public Number getAsNumber() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a string value.
+ *
+ * @return get this element as a string value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * string value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ public String getAsString() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a primitive double value.
+ *
+ * @return get this element as a primitive double value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * double value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ public double getAsDouble() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a primitive float value.
+ *
+ * @return get this element as a primitive float value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * float value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ public float getAsFloat() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a primitive long value.
+ *
+ * @return get this element as a primitive long value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * long value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ public long getAsLong() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a primitive integer value.
+ *
+ * @return get this element as a primitive integer value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * integer value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ public int getAsInt() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a primitive byte value.
+ *
+ * @return get this element as a primitive byte value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * byte value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ * @since 1.3
+ */
+ public byte getAsByte() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a primitive character value.
+ *
+ * @return get this element as a primitive char value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * char value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ * @since 1.3
+ */
+ public char getAsCharacter() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a {@link BigDecimal}.
+ *
+ * @return get this element as a {@link BigDecimal}.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive}.
+ * * @throws NumberFormatException if the element is not a valid {@link BigDecimal}.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ * @since 1.2
+ */
+ public BigDecimal getAsBigDecimal() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a {@link BigInteger}.
+ *
+ * @return get this element as a {@link BigInteger}.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive}.
+ * @throws NumberFormatException if the element is not a valid {@link BigInteger}.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ * @since 1.2
+ */
+ public BigInteger getAsBigInteger() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as a primitive short value.
+ *
+ * @return get this element as a primitive short value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * short value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ public short getAsShort() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * convenience method to get this element as an {@link Object} value.
+ *
+ * @return get this element as an Object value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * Object value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ */
+ Object getAsObject() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns a String representation of this element.
+ *
+ * @return String the string representation of this element.
+ */
+ @Override
+ public String toString() {
+ try {
+ StringBuilder sb = new StringBuilder();
+ toString(sb, BASIC_ESCAPER);
+ return sb.toString();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected abstract void toString(Appendable sb, Escaper escaper) throws IOException;
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonElementVisitor.java b/src/org/mcteam/ancientgates/gson/JsonElementVisitor.java
new file mode 100644
index 0000000..fff4780
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonElementVisitor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.IOException;
+
+/**
+ * Definition of a visitor for a JsonElement tree.
+ *
+ * @author Inderjeet Singh
+ */
+interface JsonElementVisitor {
+ void visitPrimitive(JsonPrimitive primitive) throws IOException;
+ void visitNull() throws IOException;
+
+ void startArray(JsonArray array) throws IOException;
+ void visitArrayMember(JsonArray parent, JsonPrimitive member, boolean isFirst) throws IOException;
+ void visitArrayMember(JsonArray parent, JsonArray member, boolean isFirst) throws IOException;
+ void visitArrayMember(JsonArray parent, JsonObject member, boolean isFirst) throws IOException;
+ void visitNullArrayMember(JsonArray parent, boolean isFirst) throws IOException;
+ void endArray(JsonArray array) throws IOException;
+
+ void startObject(JsonObject object) throws IOException;
+ void visitObjectMember(JsonObject parent, String memberName, JsonPrimitive member,
+ boolean isFirst) throws IOException;
+ void visitObjectMember(JsonObject parent, String memberName, JsonArray member,
+ boolean isFirst) throws IOException;
+ void visitObjectMember(JsonObject parent, String memberName, JsonObject member,
+ boolean isFirst) throws IOException;
+ void visitNullObjectMember(JsonObject parent, String memberName,
+ boolean isFirst) throws IOException;
+ void endObject(JsonObject object) throws IOException;
+}
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/gson/JsonFieldNameValidator.java b/src/org/mcteam/ancientgates/gson/JsonFieldNameValidator.java
new file mode 100644
index 0000000..9be5bc8
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonFieldNameValidator.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class can be used to check the validity of a JSON field name.
+ *
+ *
The primary use of this object is to ensure that any Java fields that use the
+ * {@link org.mcteam.ancientgates.gson.annotations.SerializedName} annotation is providing valid JSON
+ * field names. This will make the code fail-fast rather than letting the invalid
+ * field name propagate to the client and it fails to parse.
+ *
+ * @author Joel Leitch
+ */
+class JsonFieldNameValidator {
+ private static final String COMMON_PATTERN = "[a-zA-Z][a-zA-Z0-9\\ \\$_\\-]*$";
+
+ private static final Pattern JSON_FIELD_NAME_PATTERN =
+ Pattern.compile("(^" + COMMON_PATTERN + ")|(^[\\$_]" + COMMON_PATTERN + ")");
+
+
+ /**
+ * Performs validation on the JSON field name to ensure it is a valid field name.
+ *
+ * @param fieldName the name of the field to validate
+ * @return {@code fieldName} if it is a valid JSON field name
+ * @throws IllegalArgumentException if the field name is an invalid JSON field name
+ */
+ public String validate(String fieldName) {
+ Preconditions.checkNotNull(fieldName);
+ Preconditions.checkArgument(!"".equals(fieldName.trim()));
+
+ Matcher matcher = JSON_FIELD_NAME_PATTERN.matcher(fieldName);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException(fieldName + " is not a valid JSON field name.");
+ }
+ return fieldName;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonIOException.java b/src/org/mcteam/ancientgates/gson/JsonIOException.java
new file mode 100644
index 0000000..b3d9a9d
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonIOException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mcteam.ancientgates.gson;
+
+/**
+ * This exception is raised when Gson was unable to read an input stream
+ * or write to one.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonIOException extends JsonParseException {
+
+ private static final long serialVersionUID = 1L;
+
+ public JsonIOException(String msg) {
+ super(msg);
+ }
+
+ public JsonIOException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ /**
+ * Creates exception with the specified cause. Consider using
+ * {@link #JsonIOException(String, Throwable)} instead if you can describe what happened.
+ *
+ * @param cause root exception that caused this exception to be thrown.
+ */
+ public JsonIOException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonNull.java b/src/org/mcteam/ancientgates/gson/JsonNull.java
new file mode 100644
index 0000000..4e69d8f
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonNull.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.IOException;
+
+/**
+ * A class representing a Json {@code null} value.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.2
+ */
+public final class JsonNull extends JsonElement {
+ private static final JsonNull INSTANCE = new JsonNull();
+
+ /**
+ * Creates a new JsonNull object.
+ */
+ public JsonNull() {
+ // Do nothing
+ }
+
+ @Override
+ protected void toString(Appendable sb, Escaper escaper) throws IOException {
+ sb.append("null");
+ }
+
+ /**
+ * All instances of JsonNull have the same hash code since they are indistinguishable
+ */
+ @Override
+ public int hashCode() {
+ return JsonNull.class.hashCode();
+ }
+
+ /**
+ * All instances of JsonNull are the same
+ */
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof JsonNull;
+ }
+
+ /**
+ * Creation method used to return an instance of a {@link JsonNull}. To reduce the memory
+ * footprint, a single object has been created for this class; therefore the same instance is
+ * being returned for each invocation of this method. This method is kept private since we
+ * prefer the users to use {@link JsonNull#JsonNull()} which is similar to how other JsonElements
+ * are created. Note that all instances of JsonNull return true for {@link #equals(Object)}
+ * when compared to each other.
+ *
+ * @return a instance of a {@link JsonNull}
+ */
+ static JsonNull createJsonNull() {
+ return INSTANCE;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonObject.java b/src/org/mcteam/ancientgates/gson/JsonObject.java
new file mode 100644
index 0000000..4d0b6db
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonObject.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A class representing an object type in Json. An object consists of name-value pairs where names
+ * are strings, and values are any other type of {@link JsonElement}. This allows for a creating a
+ * tree of JsonElements. The member elements of this object are maintained in order they were added.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonObject extends JsonElement {
+ // We are using a linked hash map because it is important to preserve
+ // the order in which elements are inserted. This is needed to ensure
+ // that the fields of an object are inserted in the order they were
+ // defined in the class.
+ private final Map members;
+
+ /**
+ * Creates an empty JsonObject.
+ */
+ public JsonObject() {
+ members = new LinkedHashMap();
+ }
+
+ /**
+ * Adds a member, which is a name-value pair, to self. The name must be a String, but the value
+ * can be an arbitrary JsonElement, thereby allowing you to build a full tree of JsonElements
+ * rooted at this node.
+ *
+ * @param property name of the member.
+ * @param value the member object.
+ */
+ public void add(String property, JsonElement value) {
+ Preconditions.checkNotNull(property);
+ if (value == null) {
+ value = JsonNull.createJsonNull();
+ }
+ members.put(property, value);
+ }
+
+ /**
+ * Removes the {@code property} from this {@link JsonObject}.
+ *
+ * @param property name of the member that should be removed.
+ * @return the {@link JsonElement} object that is being removed.
+ * @since 1.3
+ */
+ public JsonElement remove(String property) {
+ return members.remove(property);
+ }
+
+ /**
+ * Convenience method to add a primitive member. The specified value is converted to a
+ * JsonPrimitive of String.
+ *
+ * @param property name of the member.
+ * @param value the string value associated with the member.
+ */
+ public void addProperty(String property, String value) {
+ add(property, createJsonElement(value));
+ }
+
+ /**
+ * Convenience method to add a primitive member. The specified value is converted to a
+ * JsonPrimitive of Number.
+ *
+ * @param property name of the member.
+ * @param value the number value associated with the member.
+ */
+ public void addProperty(String property, Number value) {
+ add(property, createJsonElement(value));
+ }
+
+ /**
+ * Convenience method to add a boolean member. The specified value is converted to a
+ * JsonPrimitive of Boolean.
+ *
+ * @param property name of the member.
+ * @param value the number value associated with the member.
+ */
+ public void addProperty(String property, Boolean value) {
+ add(property, createJsonElement(value));
+ }
+
+ /**
+ * Convenience method to add a char member. The specified value is converted to a
+ * JsonPrimitive of Character.
+ *
+ * @param property name of the member.
+ * @param value the number value associated with the member.
+ */
+ public void addProperty(String property, Character value) {
+ add(property, createJsonElement(value));
+ }
+
+ /**
+ * Creates the proper {@link JsonElement} object from the given {@code value} object.
+ *
+ * @param value the object to generate the {@link JsonElement} for
+ * @return a {@link JsonPrimitive} if the {@code value} is not null, otherwise a {@link JsonNull}
+ */
+ private JsonElement createJsonElement(Object value) {
+ return value == null ? JsonNull.createJsonNull() : new JsonPrimitive(value);
+ }
+
+ /**
+ * Returns a set of members of this object. The set is ordered, and the order is in which the
+ * elements were added.
+ *
+ * @return a set of members of this object.
+ */
+ public Set> entrySet() {
+ return members.entrySet();
+ }
+
+ /**
+ * Convenience method to check if a member with the specified name is present in this object.
+ *
+ * @param memberName name of the member that is being checked for presence.
+ * @return true if there is a member with the specified name, false otherwise.
+ */
+ public boolean has(String memberName) {
+ return members.containsKey(memberName);
+ }
+
+ /**
+ * Returns the member with the specified name.
+ *
+ * @param memberName name of the member that is being requested.
+ * @return the member matching the name. Null if no such member exists.
+ */
+ public JsonElement get(String memberName) {
+ if (members.containsKey(memberName)) {
+ JsonElement member = members.get(memberName);
+ return member == null ? JsonNull.createJsonNull() : member;
+ }
+ return null;
+ }
+
+ /**
+ * Convenience method to get the specified member as a JsonPrimitive element.
+ *
+ * @param memberName name of the member being requested.
+ * @return the JsonPrimitive corresponding to the specified member.
+ */
+ public JsonPrimitive getAsJsonPrimitive(String memberName) {
+ return (JsonPrimitive) members.get(memberName);
+ }
+
+ /**
+ * Convenience method to get the specified member as a JsonArray.
+ *
+ * @param memberName name of the member being requested.
+ * @return the JsonArray corresponding to the specified member.
+ */
+ public JsonArray getAsJsonArray(String memberName) {
+ return (JsonArray) members.get(memberName);
+ }
+
+ /**
+ * Convenience method to get the specified member as a JsonObject.
+ *
+ * @param memberName name of the member being requested.
+ * @return the JsonObject corresponding to the specified member.
+ */
+ public JsonObject getAsJsonObject(String memberName) {
+ return (JsonObject) members.get(memberName);
+ }
+
+ @Override
+ protected void toString(Appendable sb, Escaper escaper) throws IOException {
+ sb.append('{');
+ boolean first = true;
+ for (Map.Entry entry : members.entrySet()) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append('\"');
+ sb.append(escaper.escapeJsonString(entry.getKey()));
+ sb.append("\":");
+ entry.getValue().toString(sb, escaper);
+ }
+ sb.append('}');
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonObjectDeserializationVisitor.java b/src/org/mcteam/ancientgates/gson/JsonObjectDeserializationVisitor.java
new file mode 100644
index 0000000..b8439e6
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonObjectDeserializationVisitor.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * A visitor that populates fields of an object with data from its equivalent
+ * JSON representation
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class JsonObjectDeserializationVisitor extends JsonDeserializationVisitor {
+
+ JsonObjectDeserializationVisitor(JsonElement json, Type type,
+ ObjectNavigatorFactory factory, ObjectConstructor objectConstructor,
+ ParameterizedTypeHandlerMap> deserializers,
+ JsonDeserializationContext context) {
+ super(json, type, factory, objectConstructor, deserializers, context);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected T constructTarget() {
+ return (T) objectConstructor.construct(targetType);
+ }
+
+ public void startVisitingObject(Object node) {
+ // do nothing
+ }
+
+ public void visitArray(Object array, Type componentType) {
+ // should not be called since this case should invoke JsonArrayDeserializationVisitor
+ throw new JsonParseException("Expecting object but found array: " + array);
+ }
+
+ public void visitObjectField(FieldAttributes f, Type typeOfF, Object obj) {
+ try {
+ if (!json.isJsonObject()) {
+ throw new JsonParseException("Expecting object found: " + json);
+ }
+ JsonObject jsonObject = json.getAsJsonObject();
+ String fName = getFieldName(f);
+ JsonElement jsonChild = jsonObject.get(fName);
+ if (jsonChild != null) {
+ Object child = visitChildAsObject(typeOfF, jsonChild);
+ f.set(obj, child);
+ } else {
+ f.set(obj, null);
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void visitArrayField(FieldAttributes f, Type typeOfF, Object obj) {
+ try {
+ if (!json.isJsonObject()) {
+ throw new JsonParseException("Expecting object found: " + json);
+ }
+ JsonObject jsonObject = json.getAsJsonObject();
+ String fName = getFieldName(f);
+ JsonArray jsonChild = (JsonArray) jsonObject.get(fName);
+ if (jsonChild != null) {
+ Object array = visitChildAsArray(typeOfF, jsonChild);
+ f.set(obj, array);
+ } else {
+ f.set(obj, null);
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String getFieldName(FieldAttributes f) {
+ FieldNamingStrategy2 namingPolicy = factory.getFieldNamingPolicy();
+ return namingPolicy.translateName(f);
+ }
+
+ public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type declaredTypeOfField, Object parent) {
+ try {
+ String fName = getFieldName(f);
+ if (!json.isJsonObject()) {
+ throw new JsonParseException("Expecting object found: " + json);
+ }
+ JsonElement child = json.getAsJsonObject().get(fName);
+ TypeInfo typeInfo = new TypeInfo(declaredTypeOfField);
+ if (child == null) { // Child will be null if the field wasn't present in Json
+ return true;
+ } else if (child.isJsonNull()) {
+ if (!typeInfo.isPrimitive()) {
+ f.set(parent, null);
+ }
+ return true;
+ }
+ ObjectTypePair objTypePair = new ObjectTypePair(null, declaredTypeOfField, false);
+ Pair, ObjectTypePair> pair = objTypePair.getMatchingHandler(deserializers);
+ if (pair == null) {
+ return false;
+ }
+ Object value = invokeCustomDeserializer(child, pair);
+ if (value != null || !typeInfo.isPrimitive()) {
+ f.set(parent, value);
+ }
+ return true;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void visitPrimitive(Object primitive) {
+ if (!json.isJsonPrimitive()) {
+ throw new JsonParseException(
+ "Type information is unavailable, and the target object is not a primitive: " + json);
+ }
+ JsonPrimitive prim = json.getAsJsonPrimitive();
+ target = (T) prim.getAsObject();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonParseException.java b/src/org/mcteam/ancientgates/gson/JsonParseException.java
new file mode 100644
index 0000000..f1eeb05
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonParseException.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * This exception is raised if there is a serious issue that occurs during parsing of a Json
+ * string. One of the main usages for this class is for the Gson infrastructure. If the incoming
+ * Json is bad/malicious, an instance of this exception is raised.
+ *
+ *
This exception is a {@link RuntimeException} because it is exposed to the client. Using a
+ * {@link RuntimeException} avoids bad coding practices on the client side where they catch the
+ * exception and do nothing. It is often the case that you want to blow up if there is a parsing
+ * error (i.e. often clients do not know how to recover from a {@link JsonParseException}.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class JsonParseException extends RuntimeException {
+ static final long serialVersionUID = -4086729973971783390L;
+
+ /**
+ * Creates exception with the specified message. If you are wrapping another exception, consider
+ * using {@link #JsonParseException(String, Throwable)} instead.
+ *
+ * @param msg error message describing a possible cause of this exception.
+ */
+ public JsonParseException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Creates exception with the specified message and cause.
+ *
+ * @param msg error message describing what happened.
+ * @param cause root exception that caused this exception to be thrown.
+ */
+ public JsonParseException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ /**
+ * Creates exception with the specified cause. Consider using
+ * {@link #JsonParseException(String, Throwable)} instead if you can describe what happened.
+ *
+ * @param cause root exception that caused this exception to be thrown.
+ */
+ public JsonParseException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonParser.java b/src/org/mcteam/ancientgates/gson/JsonParser.java
new file mode 100644
index 0000000..9f11c4d
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonParser.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mcteam.ancientgates.gson;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.mcteam.ancientgates.gson.stream.JsonReader;
+import org.mcteam.ancientgates.gson.stream.JsonToken;
+import org.mcteam.ancientgates.gson.stream.MalformedJsonException;
+
+/**
+ * A parser to parse Json into a parse tree of {@link JsonElement}s
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.3
+ */
+public final class JsonParser {
+
+ /**
+ * Parses the specified JSON string into a parse tree
+ *
+ * @param json JSON text
+ * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
+ * @throws JsonParseException if the specified text is not valid JSON
+ * @since 1.3
+ */
+ public JsonElement parse(String json) throws JsonSyntaxException {
+ return parse(new StringReader(json));
+ }
+
+ /**
+ * Parses the specified JSON string into a parse tree
+ *
+ * @param json JSON text
+ * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
+ * @throws JsonParseException if the specified text is not valid JSON
+ * @since 1.3
+ */
+ public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {
+ try {
+ JsonReader jsonReader = new JsonReader(json);
+ JsonElement element = parse(jsonReader);
+ if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {
+ throw new JsonSyntaxException("Did not consume the entire document.");
+ }
+ return element;
+ } catch (MalformedJsonException e) {
+ throw new JsonSyntaxException(e);
+ } catch (IOException e) {
+ throw new JsonIOException(e);
+ } catch (NumberFormatException e) {
+ throw new JsonSyntaxException(e);
+ }
+ }
+
+ /**
+ * Returns the next value from the JSON stream as a parse tree.
+ *
+ * @throws JsonParseException if there is an IOException or if the specified
+ * text is not valid JSON
+ * @since 1.6
+ */
+ public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {
+ boolean lenient = json.isLenient();
+ json.setLenient(true);
+ try {
+ return Streams.parse(json);
+ } catch (StackOverflowError e) {
+ throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
+ } catch (OutOfMemoryError e) {
+ throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
+ } catch (JsonParseException e) {
+ if (e.getCause() instanceof EOFException) {
+ return JsonNull.createJsonNull();
+ }
+ throw e;
+ } finally {
+ json.setLenient(lenient);
+ }
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonPrimitive.java b/src/org/mcteam/ancientgates/gson/JsonPrimitive.java
new file mode 100644
index 0000000..2b0abfb
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonPrimitive.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A class representing a Json primitive value. A primitive value
+ * is either a String, a Java primitive, or a Java primitive
+ * wrapper type.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonPrimitive extends JsonElement {
+ private static final Class>[] PRIMITIVE_TYPES = { int.class, long.class, short.class,
+ float.class, double.class, byte.class, boolean.class, char.class, Integer.class, Long.class,
+ Short.class, Float.class, Double.class, Byte.class, Boolean.class, Character.class };
+
+ private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
+
+ private Object value;
+
+ /**
+ * Create a primitive containing a boolean value.
+ *
+ * @param bool the value to create the primitive with.
+ */
+ public JsonPrimitive(Boolean bool) {
+ setValue(bool);
+ }
+
+ /**
+ * Create a primitive containing a {@link Number}.
+ *
+ * @param number the value to create the primitive with.
+ */
+ public JsonPrimitive(Number number) {
+ setValue(number);
+ }
+
+ /**
+ * Create a primitive containing a String value.
+ *
+ * @param string the value to create the primitive with.
+ */
+ public JsonPrimitive(String string) {
+ setValue(string);
+ }
+
+ /**
+ * Create a primitive containing a character. The character is turned into a one character String
+ * since Json only supports String.
+ *
+ * @param c the value to create the primitive with.
+ */
+ public JsonPrimitive(Character c) {
+ setValue(c);
+ }
+
+ /**
+ * Create a primitive using the specified Object. It must be an instance of {@link Number}, a
+ * Java primitive type, or a String.
+ *
+ * @param primitive the value to create the primitive with.
+ */
+ JsonPrimitive(Object primitive) {
+ setValue(primitive);
+ }
+
+ void setValue(Object primitive) {
+ if (primitive instanceof Character) {
+ // convert characters to strings since in JSON, characters are represented as a single
+ // character string
+ char c = ((Character) primitive).charValue();
+ this.value = String.valueOf(c);
+ } else {
+ Preconditions.checkArgument(primitive instanceof Number
+ || isPrimitiveOrString(primitive));
+ this.value = primitive;
+ }
+ }
+
+ /**
+ * Check whether this primitive contains a boolean value.
+ *
+ * @return true if this primitive contains a boolean value, false otherwise.
+ */
+ public boolean isBoolean() {
+ return value instanceof Boolean;
+ }
+
+ /**
+ * convenience method to get this element as a {@link Boolean}.
+ *
+ * @return get this element as a {@link Boolean}.
+ * @throws ClassCastException if the value contained is not a valid boolean value.
+ */
+ @Override
+ Boolean getAsBooleanWrapper() {
+ return (Boolean) value;
+ }
+
+ /**
+ * convenience method to get this element as a boolean value.
+ *
+ * @return get this element as a primitive boolean value.
+ * @throws ClassCastException if the value contained is not a valid boolean value.
+ */
+ @Override
+ public boolean getAsBoolean() {
+ return isBoolean() ? getAsBooleanWrapper().booleanValue() : Boolean.parseBoolean(getAsString());
+ }
+
+ /**
+ * Check whether this primitive contains a Number.
+ *
+ * @return true if this primitive contains a Number, false otherwise.
+ */
+ public boolean isNumber() {
+ return value instanceof Number;
+ }
+
+ /**
+ * convenience method to get this element as a Number.
+ *
+ * @return get this element as a Number.
+ * @throws ClassCastException if the value contained is not a valid Number.
+ */
+ @Override
+ public Number getAsNumber() {
+ return value instanceof String ? stringToNumber((String) value) : (Number) value;
+ }
+
+ static Number stringToNumber(String value) {
+ try {
+ long longValue = Long.parseLong(value);
+ if (longValue >= Integer.MIN_VALUE && longValue <= Integer.MAX_VALUE) {
+ return (int) longValue;
+ }
+ return longValue;
+ } catch (NumberFormatException ignored) {
+ }
+
+ try {
+ return new BigDecimal(value);
+ } catch (NumberFormatException ignored) {
+ return Double.parseDouble(value); // probably NaN, -Infinity or Infinity
+ }
+ }
+
+ /**
+ * Check whether this primitive contains a String value.
+ *
+ * @return true if this primitive contains a String value, false otherwise.
+ */
+ public boolean isString() {
+ return value instanceof String;
+ }
+
+ /**
+ * convenience method to get this element as a String.
+ *
+ * @return get this element as a String.
+ * @throws ClassCastException if the value contained is not a valid String.
+ */
+ @Override
+ public String getAsString() {
+ if (isNumber()) {
+ return getAsNumber().toString();
+ } else if (isBoolean()) {
+ return getAsBooleanWrapper().toString();
+ } else {
+ return (String) value;
+ }
+ }
+
+ /**
+ * convenience method to get this element as a primitive double.
+ *
+ * @return get this element as a primitive double.
+ * @throws ClassCastException if the value contained is not a valid double.
+ */
+ @Override
+ public double getAsDouble() {
+ return isNumber() ? getAsNumber().doubleValue() : Double.parseDouble(getAsString());
+ }
+
+ /**
+ * convenience method to get this element as a {@link BigDecimal}.
+ *
+ * @return get this element as a {@link BigDecimal}.
+ * @throws NumberFormatException if the value contained is not a valid {@link BigDecimal}.
+ */
+ @Override
+ public BigDecimal getAsBigDecimal() {
+ return value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(value.toString());
+ }
+
+ /**
+ * convenience method to get this element as a {@link BigInteger}.
+ *
+ * @return get this element as a {@link BigInteger}.
+ * @throws NumberFormatException if the value contained is not a valid {@link BigInteger}.
+ */
+ @Override
+ public BigInteger getAsBigInteger() {
+ return value instanceof BigInteger ? (BigInteger) value : new BigInteger(value.toString());
+ }
+
+ /**
+ * convenience method to get this element as a float.
+ *
+ * @return get this element as a float.
+ * @throws ClassCastException if the value contained is not a valid float.
+ */
+ @Override
+ public float getAsFloat() {
+ return isNumber() ? getAsNumber().floatValue() : Float.parseFloat(getAsString());
+ }
+
+ /**
+ * convenience method to get this element as a primitive long.
+ *
+ * @return get this element as a primitive long.
+ * @throws ClassCastException if the value contained is not a valid long.
+ */
+ @Override
+ public long getAsLong() {
+ return isNumber() ? getAsNumber().longValue() : Long.parseLong(getAsString());
+ }
+
+ /**
+ * convenience method to get this element as a primitive short.
+ *
+ * @return get this element as a primitive short.
+ * @throws ClassCastException if the value contained is not a valid short value.
+ */
+ @Override
+ public short getAsShort() {
+ return isNumber() ? getAsNumber().shortValue() : Short.parseShort(getAsString());
+ }
+
+ /**
+ * convenience method to get this element as a primitive integer.
+ *
+ * @return get this element as a primitive integer.
+ * @throws ClassCastException if the value contained is not a valid integer.
+ */
+ @Override
+ public int getAsInt() {
+ return isNumber() ? getAsNumber().intValue() : Integer.parseInt(getAsString());
+ }
+
+ @Override
+ public byte getAsByte() {
+ return isNumber() ? getAsNumber().byteValue() : Byte.parseByte(getAsString());
+ }
+
+ @Override
+ public char getAsCharacter() {
+ return getAsString().charAt(0);
+ }
+
+ /**
+ * convenience method to get this element as an Object.
+ *
+ * @return get this element as an Object that can be converted to a suitable value.
+ */
+ @Override
+ Object getAsObject() {
+ if (value instanceof BigInteger) {
+ BigInteger big = (BigInteger) value;
+ if (big.compareTo(INTEGER_MAX) < 0) {
+ return big.intValue();
+ } else if (big.compareTo(LONG_MAX) < 0) {
+ return big.longValue();
+ }
+ }
+ // No need to convert to float or double since those lose precision
+ return value;
+ }
+
+ @Override
+ protected void toString(Appendable sb, Escaper escaper) throws IOException {
+ if (isString()) {
+ sb.append('"');
+ sb.append(escaper.escapeJsonString(value.toString()));
+ sb.append('"');
+ } else {
+ sb.append(value.toString());
+ }
+ }
+
+ private static boolean isPrimitiveOrString(Object target) {
+ if (target instanceof String) {
+ return true;
+ }
+
+ Class> classOfPrimitive = target.getClass();
+ for (Class> standardPrimitive : PRIMITIVE_TYPES) {
+ if (standardPrimitive.isAssignableFrom(classOfPrimitive)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ if (value == null) {
+ return 31;
+ }
+ // Using recommended hashing algorithm from Effective Java for longs and doubles
+ if (isIntegral(this)) {
+ long value = getAsNumber().longValue();
+ return (int) (value ^ (value >>> 32));
+ }
+ if (isFloatingPoint(this)) {
+ long value = Double.doubleToLongBits(getAsNumber().doubleValue());
+ return (int) (value ^ (value >>> 32));
+ }
+ return value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ JsonPrimitive other = (JsonPrimitive)obj;
+ if (value == null) {
+ return other.value == null;
+ }
+ if (isIntegral(this) && isIntegral(other)) {
+ return getAsNumber().longValue() == other.getAsNumber().longValue();
+ }
+ if (isFloatingPoint(this) && isFloatingPoint(other)) {
+ return getAsNumber().doubleValue() == other.getAsNumber().doubleValue();
+ }
+ return value.equals(other.value);
+ }
+
+ /**
+ * Returns true if the specified number is an integral type
+ * (Long, Integer, Short, Byte, BigInteger)
+ */
+ private static boolean isIntegral(JsonPrimitive primitive) {
+ if (primitive.value instanceof Number) {
+ Number number = (Number) primitive.value;
+ return number instanceof BigInteger || number instanceof Long || number instanceof Integer
+ || number instanceof Short || number instanceof Byte;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the specified number is a floating point type (BigDecimal, double, float)
+ */
+ private static boolean isFloatingPoint(JsonPrimitive primitive) {
+ if (primitive.value instanceof Number) {
+ Number number = (Number) primitive.value;
+ return number instanceof BigDecimal || number instanceof Double || number instanceof Float;
+ }
+ return false;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonSerializationContext.java b/src/org/mcteam/ancientgates/gson/JsonSerializationContext.java
new file mode 100644
index 0000000..390bf27
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonSerializationContext.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Context for serialization that is passed to a custom serializer during invocation of its
+ * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface JsonSerializationContext {
+
+ /**
+ * Invokes default serialization on the specified object.
+ *
+ * @param src the object that needs to be serialized.
+ * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}.
+ */
+ public JsonElement serialize(Object src);
+
+ /**
+ * Invokes default serialization on the specified object passing the specific type information.
+ * It should never be invoked on the element received as a parameter of the
+ * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. Doing
+ * so will result in an infinite loop since Gson will in-turn call the custom serializer again.
+ *
+ * @param src the object that needs to be serialized.
+ * @param typeOfSrc the actual genericized type of src object.
+ * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}.
+ */
+ public JsonElement serialize(Object src, Type typeOfSrc);
+}
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/gson/JsonSerializationContextDefault.java b/src/org/mcteam/ancientgates/gson/JsonSerializationContextDefault.java
new file mode 100644
index 0000000..b64abfb
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonSerializationContextDefault.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * An implementation of serialization context for Gson.
+ *
+ * @author Inderjeet Singh
+ */
+final class JsonSerializationContextDefault implements JsonSerializationContext {
+
+ private final ObjectNavigatorFactory factory;
+ private final ParameterizedTypeHandlerMap> serializers;
+ private final boolean serializeNulls;
+ private final MemoryRefStack ancestors;
+
+ JsonSerializationContextDefault(ObjectNavigatorFactory factory, boolean serializeNulls,
+ ParameterizedTypeHandlerMap> serializers) {
+ this.factory = factory;
+ this.serializeNulls = serializeNulls;
+ this.serializers = serializers;
+ this.ancestors = new MemoryRefStack();
+ }
+
+ public JsonElement serialize(Object src) {
+ if (src == null) {
+ return JsonNull.createJsonNull();
+ }
+ return serialize(src, src.getClass(), true);
+ }
+
+ public JsonElement serialize(Object src, Type typeOfSrc) {
+ return serialize(src, typeOfSrc, true);
+ }
+
+ public JsonElement serialize(Object src, Type typeOfSrc, boolean preserveType) {
+ ObjectNavigator on = factory.create(new ObjectTypePair(src, typeOfSrc, preserveType));
+ JsonSerializationVisitor visitor =
+ new JsonSerializationVisitor(factory, serializeNulls, serializers, this, ancestors);
+ on.accept(visitor);
+ return visitor.getJsonElement();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonSerializationVisitor.java b/src/org/mcteam/ancientgates/gson/JsonSerializationVisitor.java
new file mode 100644
index 0000000..220a3be
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonSerializationVisitor.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Type;
+
+/**
+ * A visitor that adds JSON elements corresponding to each field of an object
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
+
+ private final ObjectNavigatorFactory factory;
+ private final ParameterizedTypeHandlerMap> serializers;
+ private final boolean serializeNulls;
+ private final JsonSerializationContext context;
+ private final MemoryRefStack ancestors;
+ private JsonElement root;
+
+ JsonSerializationVisitor(ObjectNavigatorFactory factory, boolean serializeNulls,
+ ParameterizedTypeHandlerMap> serializers, JsonSerializationContext context,
+ MemoryRefStack ancestors) {
+ this.factory = factory;
+ this.serializeNulls = serializeNulls;
+ this.serializers = serializers;
+ this.context = context;
+ this.ancestors = ancestors;
+ }
+
+ public Object getTarget() {
+ return null;
+ }
+
+ public void start(ObjectTypePair node) {
+ if (node == null) {
+ return;
+ }
+ if (ancestors.contains(node)) {
+ throw new CircularReferenceException(node);
+ }
+ ancestors.push(node);
+ }
+
+ public void end(ObjectTypePair node) {
+ if (node != null) {
+ ancestors.pop();
+ }
+ }
+
+ public void startVisitingObject(Object node) {
+ assignToRoot(new JsonObject());
+ }
+
+ public void visitArray(Object array, Type arrayType) {
+ assignToRoot(new JsonArray());
+ int length = Array.getLength(array);
+ TypeInfoArray fieldTypeInfo = TypeInfoFactory.getTypeInfoForArray(arrayType);
+ Type componentType = fieldTypeInfo.getSecondLevelType();
+ for (int i = 0; i < length; ++i) {
+ Object child = Array.get(array, i);
+ Type childType = componentType;
+ // we should not get more specific component type yet since it is possible
+ // that a custom
+ // serializer is registered for the componentType
+ addAsArrayElement(new ObjectTypePair(child, childType, false));
+ }
+ }
+
+ public void visitArrayField(FieldAttributes f, Type typeOfF, Object obj) {
+ try {
+ if (isFieldNull(f, obj)) {
+ if (serializeNulls) {
+ addChildAsElement(f, JsonNull.createJsonNull());
+ }
+ } else {
+ Object array = getFieldValue(f, obj);
+ addAsChildOfObject(f, new ObjectTypePair(array, typeOfF, false));
+ }
+ } catch (CircularReferenceException e) {
+ throw e.createDetailedException(f);
+ }
+ }
+
+ public void visitObjectField(FieldAttributes f, Type typeOfF, Object obj) {
+ try {
+ if (isFieldNull(f, obj)) {
+ if (serializeNulls) {
+ addChildAsElement(f, JsonNull.createJsonNull());
+ }
+ } else {
+ Object fieldValue = getFieldValue(f, obj);
+ // we should not get more specific component type yet since it is
+ // possible that a custom
+ // serializer is registered for the componentType
+ addAsChildOfObject(f, new ObjectTypePair(fieldValue, typeOfF, false));
+ }
+ } catch (CircularReferenceException e) {
+ throw e.createDetailedException(f);
+ }
+ }
+
+ public void visitPrimitive(Object obj) {
+ JsonElement json = obj == null ? JsonNull.createJsonNull() : new JsonPrimitive(obj);
+ assignToRoot(json);
+ }
+
+ private void addAsChildOfObject(FieldAttributes f, ObjectTypePair fieldValuePair) {
+ JsonElement childElement = getJsonElementForChild(fieldValuePair);
+ addChildAsElement(f, childElement);
+ }
+
+ private void addChildAsElement(FieldAttributes f, JsonElement childElement) {
+ FieldNamingStrategy2 namingPolicy = factory.getFieldNamingPolicy();
+ root.getAsJsonObject().add(namingPolicy.translateName(f), childElement);
+ }
+
+ private void addAsArrayElement(ObjectTypePair elementTypePair) {
+ if (elementTypePair.getObject() == null) {
+ root.getAsJsonArray().add(JsonNull.createJsonNull());
+ } else {
+ JsonElement childElement = getJsonElementForChild(elementTypePair);
+ root.getAsJsonArray().add(childElement);
+ }
+ }
+
+ private JsonElement getJsonElementForChild(ObjectTypePair fieldValueTypePair) {
+ ObjectNavigator on = factory.create(fieldValueTypePair);
+ JsonSerializationVisitor childVisitor =
+ new JsonSerializationVisitor(factory, serializeNulls, serializers, context, ancestors);
+ on.accept(childVisitor);
+ return childVisitor.getJsonElement();
+ }
+
+ public boolean visitUsingCustomHandler(ObjectTypePair objTypePair) {
+ try {
+ Object obj = objTypePair.getObject();
+ if (obj == null) {
+ if (serializeNulls) {
+ assignToRoot(JsonNull.createJsonNull());
+ }
+ return true;
+ }
+ JsonElement element = findAndInvokeCustomSerializer(objTypePair);
+ if (element != null) {
+ assignToRoot(element);
+ return true;
+ }
+ return false;
+ } catch (CircularReferenceException e) {
+ throw e.createDetailedException(null);
+ }
+ }
+
+ /**
+ * objTypePair.getObject() must not be null
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private JsonElement findAndInvokeCustomSerializer(ObjectTypePair objTypePair) {
+ Pair,ObjectTypePair> pair = objTypePair.getMatchingHandler(serializers);
+ if (pair == null) {
+ return null;
+ }
+ JsonSerializer serializer = pair.first;
+ objTypePair = pair.second;
+ start(objTypePair);
+ try {
+ JsonElement element =
+ serializer.serialize(objTypePair.getObject(), objTypePair.getType(), context);
+ return element == null ? JsonNull.createJsonNull() : element;
+ } finally {
+ end(objTypePair);
+ }
+ }
+
+ public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type declaredTypeOfField, Object parent) {
+ try {
+ Preconditions.checkState(root.isJsonObject());
+ Object obj = f.get(parent);
+ if (obj == null) {
+ if (serializeNulls) {
+ addChildAsElement(f, JsonNull.createJsonNull());
+ }
+ return true;
+ }
+ ObjectTypePair objTypePair = new ObjectTypePair(obj, declaredTypeOfField, false);
+ JsonElement child = findAndInvokeCustomSerializer(objTypePair);
+ if (child != null) {
+ addChildAsElement(f, child);
+ return true;
+ }
+ return false;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException();
+ } catch (CircularReferenceException e) {
+ throw e.createDetailedException(f);
+ }
+ }
+
+ private void assignToRoot(JsonElement newRoot) {
+ Preconditions.checkNotNull(newRoot);
+ root = newRoot;
+ }
+
+ private boolean isFieldNull(FieldAttributes f, Object obj) {
+ return getFieldValue(f, obj) == null;
+ }
+
+ private Object getFieldValue(FieldAttributes f, Object obj) {
+ try {
+ return f.get(obj);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public JsonElement getJsonElement() {
+ return root;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonSerializer.java b/src/org/mcteam/ancientgates/gson/JsonSerializer.java
new file mode 100644
index 0000000..ad06c4d
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonSerializer.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Interface representing a custom serializer for Json. You should write a custom serializer, if
+ * you are not happy with the default serialization done by Gson. You will also need to register
+ * this serializer through {@link org.mcteam.ancientgates.gson.GsonBuilder#registerTypeAdapter(Type, Object)}.
+ *
+ *
Let us look at example where defining a serializer will be useful. The {@code Id} class
+ * defined below has two fields: {@code clazz} and {@code value}.
+ *
+ *
+ * public class Id<T> {
+ * private final Class<T> clazz;
+ * private final long value;
+ *
+ * public Id(Class<T> clazz, long value) {
+ * this.clazz = clazz;
+ * this.value = value;
+ * }
+ *
+ * public long getValue() {
+ * return value;
+ * }
+ * }
+ *
+ *
+ *
The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be
+ * {"clazz":com.foo.MyObject,"value":20}. Suppose, you just want the output to be
+ * the value instead, which is {@code 20} in this case. You can achieve that by writing a custom
+ * serializer:
+ *
+ *
+ * class IdSerializer implements JsonSerializer<Id>() {
+ * public JsonElement toJson(Id id, Type typeOfId, JsonSerializationContext context) {
+ * return new JsonPrimitive(id.getValue());
+ * }
+ * }
+ *
+ *
+ *
You will also need to register {@code IdSerializer} with Gson as follows:
+ *
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdSerializer()).create();
+ *
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @param type for which the serializer is being registered. It is possible that a serializer
+ * may be asked to serialize a specific generic type of the T.
+ */
+public interface JsonSerializer {
+
+ /**
+ * Gson invokes this call-back method during serialization when it encounters a field of the
+ * specified type.
+ *
+ *
In the implementation of this call-back method, you should consider invoking
+ * {@link JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any
+ * non-trivial field of the {@code src} object. However, you should never invoke it on the
+ * {@code src} object itself since that will cause an infinite loop (Gson will call your
+ * call-back method again).
+ *
+ * @param src the object that needs to be converted to Json.
+ * @param typeOfSrc the actual type (fully genericized version) of the source object.
+ * @return a JsonElement corresponding to the specified object.
+ */
+ public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonStreamParser.java b/src/org/mcteam/ancientgates/gson/JsonStreamParser.java
new file mode 100644
index 0000000..39ada5b
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonStreamParser.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mcteam.ancientgates.gson;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.mcteam.ancientgates.gson.stream.JsonReader;
+import org.mcteam.ancientgates.gson.stream.JsonToken;
+import org.mcteam.ancientgates.gson.stream.MalformedJsonException;
+
+
+/**
+ * A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader
+ * asynchronously.
+ *
+ *
This class is conditionally thread-safe (see Item 70, Effective Java second edition). To
+ * properly use this class across multiple threads, you will need to add some external
+ * synchronization. For example:
+ *
+ *
+ * JsonStreamParser parser = new JsonStreamParser("['first'] {'second':10} 'third'");
+ * JsonElement element;
+ * synchronized (parser) { // synchronize on an object shared by threads
+ * if (parser.hasNext()) {
+ * element = parser.next();
+ * }
+ * }
+ *
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.4
+ */
+public final class JsonStreamParser implements Iterator {
+
+ private final JsonReader parser;
+ private final Object lock;
+
+ /**
+ * @param json The string containing JSON elements concatenated to each other.
+ * @since 1.4
+ */
+ public JsonStreamParser(String json) {
+ this(new StringReader(json));
+ }
+
+ /**
+ * @param reader The data stream containing JSON elements concatenated to each other.
+ * @since 1.4
+ */
+ public JsonStreamParser(Reader reader) {
+ parser = new JsonReader(reader);
+ parser.setLenient(true);
+ lock = new Object();
+ }
+
+ /**
+ * Returns the next available {@link JsonElement} on the reader. Null if none available.
+ *
+ * @return the next available {@link JsonElement} on the reader. Null if none available.
+ * @throws JsonParseException if the incoming stream is malformed JSON.
+ * @since 1.4
+ */
+ public JsonElement next() throws JsonParseException {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ try {
+ return Streams.parse(parser);
+ } catch (StackOverflowError e) {
+ throw new JsonParseException("Failed parsing JSON source to Json", e);
+ } catch (OutOfMemoryError e) {
+ throw new JsonParseException("Failed parsing JSON source to Json", e);
+ } catch (JsonParseException e) {
+ throw e.getCause() instanceof EOFException ? new NoSuchElementException() : e;
+ }
+ }
+
+ /**
+ * Returns true if a {@link JsonElement} is available on the input for consumption
+ * @return true if a {@link JsonElement} is available on the input, false otherwise
+ * @since 1.4
+ */
+ public boolean hasNext() {
+ synchronized (lock) {
+ try {
+ return parser.peek() != JsonToken.END_DOCUMENT;
+ } catch (MalformedJsonException e) {
+ throw new JsonSyntaxException(e);
+ } catch (IOException e) {
+ throw new JsonIOException(e);
+ }
+ }
+ }
+
+ /**
+ * This optional {@link Iterator} method is not relevant for stream parsing and hence is not
+ * implemented.
+ * @since 1.4
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonSyntaxException.java b/src/org/mcteam/ancientgates/gson/JsonSyntaxException.java
new file mode 100644
index 0000000..0082729
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonSyntaxException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mcteam.ancientgates.gson;
+
+/**
+ * This exception is raised when Gson attempts to read (or write) a malformed
+ * JSON element.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonSyntaxException extends JsonParseException {
+
+ private static final long serialVersionUID = 1L;
+
+ public JsonSyntaxException(String msg) {
+ super(msg);
+ }
+
+ public JsonSyntaxException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ /**
+ * Creates exception with the specified cause. Consider using
+ * {@link #JsonSyntaxException(String, Throwable)} instead if you can
+ * describe what actually happened.
+ *
+ * @param cause root exception that caused this exception to be thrown.
+ */
+ public JsonSyntaxException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/JsonTreeNavigator.java b/src/org/mcteam/ancientgates/gson/JsonTreeNavigator.java
new file mode 100644
index 0000000..67c1944
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/JsonTreeNavigator.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * A navigator to navigate a tree of JsonElement nodes in Depth-first order
+ *
+ * @author Inderjeet Singh
+ */
+final class JsonTreeNavigator {
+ private final JsonElementVisitor visitor;
+ private final boolean visitNulls;
+
+ JsonTreeNavigator(JsonElementVisitor visitor, boolean visitNulls) {
+ this.visitor = visitor;
+ this.visitNulls = visitNulls;
+ }
+
+ public void navigate(JsonElement element) throws IOException {
+ if (element.isJsonNull()) {
+ visitor.visitNull();
+ } else if (element.isJsonArray()) {
+ JsonArray array = element.getAsJsonArray();
+ visitor.startArray(array);
+ boolean isFirst = true;
+ for (JsonElement child : array) {
+ visitChild(array, child, isFirst);
+ if (isFirst) {
+ isFirst = false;
+ }
+ }
+ visitor.endArray(array);
+ } else if (element.isJsonObject()) {
+ JsonObject object = element.getAsJsonObject();
+ visitor.startObject(object);
+ boolean isFirst = true;
+ for (Map.Entry member : object.entrySet()) {
+ boolean visited = visitChild(object, member.getKey(), member.getValue(), isFirst);
+ if (visited && isFirst) {
+ isFirst = false;
+ }
+ }
+ visitor.endObject(object);
+ } else { // must be JsonPrimitive
+ visitor.visitPrimitive(element.getAsJsonPrimitive());
+ }
+ }
+
+ /**
+ * Returns true if the child was visited, false if it was skipped.
+ */
+ private boolean visitChild(JsonObject parent, String childName, JsonElement child,
+ boolean isFirst) throws IOException {
+ if (child.isJsonNull()) {
+ if (visitNulls) {
+ visitor.visitNullObjectMember(parent, childName, isFirst);
+ navigate(child.getAsJsonNull());
+ } else { // Null value is being skipped.
+ return false;
+ }
+ } else if (child.isJsonArray()) {
+ JsonArray childAsArray = child.getAsJsonArray();
+ visitor.visitObjectMember(parent, childName, childAsArray, isFirst);
+ navigate(childAsArray);
+ } else if (child.isJsonObject()) {
+ JsonObject childAsObject = child.getAsJsonObject();
+ visitor.visitObjectMember(parent, childName, childAsObject, isFirst);
+ navigate(childAsObject);
+ } else { // is a JsonPrimitive
+ visitor.visitObjectMember(parent, childName, child.getAsJsonPrimitive(), isFirst);
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if the child was visited, false if it was skipped.
+ */
+ private void visitChild(JsonArray parent, JsonElement child, boolean isFirst) throws IOException {
+ if (child.isJsonNull()) {
+ visitor.visitNullArrayMember(parent, isFirst);
+ navigate(child);
+ } else if (child.isJsonArray()) {
+ JsonArray childAsArray = child.getAsJsonArray();
+ visitor.visitArrayMember(parent, childAsArray, isFirst);
+ navigate(childAsArray);
+ } else if (child.isJsonObject()) {
+ JsonObject childAsObject = child.getAsJsonObject();
+ visitor.visitArrayMember(parent, childAsObject, isFirst);
+ navigate(childAsObject);
+ } else { // is a JsonPrimitive
+ visitor.visitArrayMember(parent, child.getAsJsonPrimitive(), isFirst);
+ }
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/LongSerializationPolicy.java b/src/org/mcteam/ancientgates/gson/LongSerializationPolicy.java
new file mode 100644
index 0000000..bbd4c42
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/LongSerializationPolicy.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * Defines the expected format for a {@code long} or {@code Long} type when its serialized.
+ *
+ * @since 1.3
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public enum LongSerializationPolicy {
+ /**
+ * This is the "default" serialization policy that will output a {@code long} object as a JSON
+ * number. For example, assume an object has a long field named "f" then the serialized output
+ * would be:
+ * {@code {"f":123}}.
+ */
+ DEFAULT(new DefaultStrategy()),
+
+ /**
+ * Serializes a long value as a quoted string. For example, assume an object has a long field
+ * named "f" then the serialized output would be:
+ * {@code {"f":"123"}}.
+ */
+ STRING(new StringStrategy());
+
+ private final Strategy strategy;
+
+ private LongSerializationPolicy(Strategy strategy) {
+ this.strategy = strategy;
+ }
+
+ /**
+ * Serialize this {@code value} using this serialization policy.
+ *
+ * @param value the long value to be serialized into a {@link JsonElement}
+ * @return the serialized version of {@code value}
+ */
+ public JsonElement serialize(Long value) {
+ return strategy.serialize(value);
+ }
+
+ private interface Strategy {
+ JsonElement serialize(Long value);
+ }
+
+ private static class DefaultStrategy implements Strategy {
+ public JsonElement serialize(Long value) {
+ return new JsonPrimitive(value);
+ }
+ }
+
+ private static class StringStrategy implements Strategy {
+ public JsonElement serialize(Long value) {
+ return new JsonPrimitive(String.valueOf(value));
+ }
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/LowerCamelCaseSeparatorNamingPolicy.java b/src/org/mcteam/ancientgates/gson/LowerCamelCaseSeparatorNamingPolicy.java
new file mode 100644
index 0000000..4157504
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/LowerCamelCaseSeparatorNamingPolicy.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * A {@link FieldNamingStrategy2} that ensures the JSON field names consist of only
+ * lower case letters and are separated by a particular {@code separatorString}.
+ *
+ *
+ *
+ * @author Joel Leitch
+ */
+final class LowerCamelCaseSeparatorNamingPolicy extends CompositionFieldNamingPolicy {
+
+ public LowerCamelCaseSeparatorNamingPolicy(String separatorString) {
+ super(new CamelCaseSeparatorNamingPolicy(separatorString), new LowerCaseNamingPolicy());
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/LowerCaseNamingPolicy.java b/src/org/mcteam/ancientgates/gson/LowerCaseNamingPolicy.java
new file mode 100644
index 0000000..680464c
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/LowerCaseNamingPolicy.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * A {@link FieldNamingStrategy2} that ensures the JSON field names consist of only
+ * lower case letters.
+ *
+ *
The following is an example:
+ *
+ * class IntWrapper {
+ * public int integerField = 0;
+ * }
+ *
+ * LowerCaseNamingPolicy policy = new LowerCaseNamingPolicy();
+ * String translatedFieldName =
+ * policy.translateName(IntWrapper.class.getField("integerField"));
+ *
+ * assert("integerfield".equals(translatedFieldName));
+ *
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class LowerCaseNamingPolicy extends RecursiveFieldNamingPolicy {
+
+ @Override
+ protected String translateName(String target, Type fieldType,
+ Collection annotations) {
+ return target.toLowerCase();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/LruCache.java b/src/org/mcteam/ancientgates/gson/LruCache.java
new file mode 100644
index 0000000..4533747
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/LruCache.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * An implementation of the {@link Cache} interface that evict objects from the cache using an
+ * LRU (least recently used) algorithm. Object start getting evicted from the cache once the
+ * {@code maxCapacity} is reached.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class LruCache extends LinkedHashMap implements Cache {
+ private static final long serialVersionUID = 1L;
+
+ private final int maxCapacity;
+
+ LruCache(int maxCapacity) {
+ super(maxCapacity, 0.7F, true);
+ this.maxCapacity = maxCapacity;
+ }
+
+ public void addElement(K key, V value) {
+ put(key, value);
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ }
+
+ public V getElement(K key) {
+ return get(key);
+ }
+
+ public V removeElement(K key) {
+ return remove(key);
+ }
+
+ @Override
+ public int size() {
+ return super.size();
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry entry) {
+ return size() > maxCapacity;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/MapAsArrayTypeAdapter.java b/src/org/mcteam/ancientgates/gson/MapAsArrayTypeAdapter.java
new file mode 100644
index 0000000..06f9e58
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/MapAsArrayTypeAdapter.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Adapts maps containing complex keys as arrays of map entries.
+ *
+ *
Maps as JSON objects
+ * The standard GSON map type adapter converts Java {@link Map Maps} to JSON
+ * Objects. This requires that map keys can be serialized as strings; this is
+ * insufficient for some key types. For example, consider a map whose keys are
+ * points on a grid. The default JSON form encodes reasonably:
+ * But GSON is unable to deserialize this value because the JSON string name is
+ * just the {@link Object#toString() toString()} of the map key. Attempting to
+ * convert the above JSON to an object fails with a parse exception:
+ *
com.bukkit.mcteam.gson.JsonParseException: Expecting object found: "(5,6)"
+ * at com.bukkit.mcteam.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
+ * at com.bukkit.mcteam.gson.ObjectNavigator.navigateClassFields
+ * ...
+ *
+ *
Maps as JSON arrays
+ * An alternative approach taken by this type adapter is to encode maps as
+ * arrays of map entries. Each map entry is a two element array containing a key
+ * and a value. This approach is more flexible because any type can be used as
+ * the map's key; not just strings. But it's also less portable because the
+ * receiver of such JSON must be aware of the map entry convention.
+ *
+ *
Register this adapter when you are creating your GSON instance.
+ *
{@code
+ * Gson gson = new GsonBuilder()
+ * .registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter())
+ * .create();
+ * }
+ * This will change the structure of the JSON emitted by the code above. Now we
+ * get an array. In this case the arrays elements are map entries:
+ *
+ * This format will serialize and deserialize just fine as long as this adapter
+ * is registered.
+ *
+ *
This adapter returns regular JSON objects for maps whose keys are not
+ * complex. A key is complex if its JSON-serialized form is an array or an
+ * object.
+ */
+public final class MapAsArrayTypeAdapter
+ implements JsonSerializer>, JsonDeserializer> {
+
+ public Map, ?> deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) throws JsonParseException {
+ Map result = new LinkedHashMap();
+ Type[] keyAndValueType = typeToTypeArguments(typeOfT);
+ if (json.isJsonArray()) {
+ JsonArray array = json.getAsJsonArray();
+ for (int i = 0; i < array.size(); i++) {
+ JsonArray entryArray = array.get(i).getAsJsonArray();
+ Object k = context.deserialize(entryArray.get(0), keyAndValueType[0]);
+ Object v = context.deserialize(entryArray.get(1), keyAndValueType[1]);
+ result.put(k, v);
+ }
+ checkSize(array, array.size(), result, result.size());
+ } else {
+ JsonObject object = json.getAsJsonObject();
+ for (Map.Entry entry : object.entrySet()) {
+ Object k = context.deserialize(new JsonPrimitive(entry.getKey()), keyAndValueType[0]);
+ Object v = context.deserialize(entry.getValue(), keyAndValueType[1]);
+ result.put(k, v);
+ }
+ checkSize(object, object.entrySet().size(), result, result.size());
+ }
+ return result;
+ }
+
+ public JsonElement serialize(Map, ?> src, Type typeOfSrc, JsonSerializationContext context) {
+ Type[] keyAndValueType = typeToTypeArguments(typeOfSrc);
+ boolean serializeAsArray = false;
+ List keysAndValues = new ArrayList();
+ for (Map.Entry, ?> entry : src.entrySet()) {
+ JsonElement key = context.serialize(entry.getKey(), keyAndValueType[0]);
+ serializeAsArray |= key.isJsonObject() || key.isJsonArray();
+ keysAndValues.add(key);
+ keysAndValues.add(context.serialize(entry.getValue(), keyAndValueType[1]));
+ }
+
+ if (serializeAsArray) {
+ JsonArray result = new JsonArray();
+ for (int i = 0; i < keysAndValues.size(); i+=2) {
+ JsonArray entryArray = new JsonArray();
+ entryArray.add(keysAndValues.get(i));
+ entryArray.add(keysAndValues.get(i + 1));
+ result.add(entryArray);
+ }
+ return result;
+ } else {
+ JsonObject result = new JsonObject();
+ for (int i = 0; i < keysAndValues.size(); i+=2) {
+ result.add(keysAndValues.get(i).getAsString(), keysAndValues.get(i + 1));
+ }
+ checkSize(src, src.size(), result, result.entrySet().size());
+ return result;
+ }
+ }
+
+ private Type[] typeToTypeArguments(Type typeOfT) {
+ if (typeOfT instanceof ParameterizedType) {
+ Type[] typeArguments = ((ParameterizedType) typeOfT).getActualTypeArguments();
+ if (typeArguments.length != 2) {
+ throw new IllegalArgumentException("MapAsArrayTypeAdapter cannot handle " + typeOfT);
+ }
+ return typeArguments;
+ }
+ return new Type[] { Object.class, Object.class };
+ }
+
+ private void checkSize(Object input, int inputSize, Object output, int outputSize) {
+ if (inputSize != outputSize) {
+ throw new JsonSyntaxException("Input size " + inputSize + " != output size " + outputSize
+ + " for input " + input + " and output " + output);
+ }
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/MappedObjectConstructor.java b/src/org/mcteam/ancientgates/gson/MappedObjectConstructor.java
new file mode 100644
index 0000000..00e14da
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/MappedObjectConstructor.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class contains a mapping of all the application specific
+ * {@link InstanceCreator} instances. Registering an {@link InstanceCreator}
+ * with this class will override the default object creation that is defined
+ * by the ObjectConstructor that this class is wrapping. Using this class
+ * with the JSON framework provides the application with "pluggable" modules
+ * to customize framework to suit the application's needs.
+ *
+ * @author Joel Leitch
+ */
+final class MappedObjectConstructor implements ObjectConstructor {
+ private static final Logger log = Logger.getLogger(MappedObjectConstructor.class.getName());
+
+ private final ParameterizedTypeHandlerMap> instanceCreatorMap;
+
+ public MappedObjectConstructor(
+ ParameterizedTypeHandlerMap> instanceCreators) {
+ instanceCreatorMap = instanceCreators;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T construct(Type typeOfT) {
+ InstanceCreator creator = (InstanceCreator) instanceCreatorMap.getHandlerFor(typeOfT);
+ if (creator != null) {
+ return creator.createInstance(typeOfT);
+ }
+ return (T) constructWithNoArgConstructor(typeOfT);
+ }
+
+ public Object constructArray(Type type, int length) {
+ return Array.newInstance(TypeUtils.toRawClass(type), length);
+ }
+
+ private T constructWithNoArgConstructor(Type typeOfT) {
+ try {
+ Constructor constructor = getNoArgsConstructor(typeOfT);
+ if (constructor == null) {
+ throw new RuntimeException(("No-args constructor for " + typeOfT + " does not exist. "
+ + "Register an InstanceCreator with Gson for this type to fix this problem."));
+ }
+ return constructor.newInstance();
+ } catch (InstantiationException e) {
+ throw new RuntimeException(("Unable to invoke no-args constructor for " + typeOfT + ". "
+ + "Register an InstanceCreator with Gson for this type may fix this problem."), e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(("Unable to invoke no-args constructor for " + typeOfT + ". "
+ + "Register an InstanceCreator with Gson for this type may fix this problem."), e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(("Unable to invoke no-args constructor for " + typeOfT + ". "
+ + "Register an InstanceCreator with Gson for this type may fix this problem."), e);
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "cast"})
+ private Constructor getNoArgsConstructor(Type typeOfT) {
+ TypeInfo typeInfo = new TypeInfo(typeOfT);
+ Class clazz = (Class) typeInfo.getRawClass();
+ Constructor[] declaredConstructors = (Constructor[]) clazz.getDeclaredConstructors();
+ AccessibleObject.setAccessible(declaredConstructors, true);
+ for (Constructor constructor : declaredConstructors) {
+ if (constructor.getParameterTypes().length == 0) {
+ return constructor;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Use this methods to register an {@link InstanceCreator} for a new type.
+ *
+ * @param the type of class to be mapped with its "creator"
+ * @param typeOfT the instance type that will be created
+ * @param creator the {@link InstanceCreator} instance to register
+ */
+ void register(Type typeOfT, InstanceCreator extends T> creator) {
+ if (instanceCreatorMap.hasSpecificHandlerFor(typeOfT)) {
+ log.log(Level.WARNING, "Overriding the existing InstanceCreator for {0}", typeOfT);
+ }
+ instanceCreatorMap.register(typeOfT, creator);
+ }
+
+ @Override
+ public String toString() {
+ return instanceCreatorMap.toString();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/MemoryRefStack.java b/src/org/mcteam/ancientgates/gson/MemoryRefStack.java
new file mode 100644
index 0000000..7de13a7
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/MemoryRefStack.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.util.Stack;
+
+/**
+ * A stack data structure that only does a memory reference comparison
+ * when looking for a particular item in the stack. This stack does
+ * not allow {@code null} values to be added.
+ *
+ * @author Joel Leitch
+ */
+final class MemoryRefStack {
+ private final Stack stack = new Stack();
+
+ /**
+ * Adds a new element to the top of the stack.
+ *
+ * @param obj the object to add to the stack
+ * @return the object that was added
+ */
+ public ObjectTypePair push(ObjectTypePair obj) {
+ Preconditions.checkNotNull(obj);
+
+ return stack.push(obj);
+ }
+
+ /**
+ * Removes the top element from the stack.
+ *
+ * @return the element being removed from the stack
+ * @throws java.util.EmptyStackException thrown if the stack is empty
+ */
+ public ObjectTypePair pop() {
+ return stack.pop();
+ }
+
+ public boolean isEmpty() {
+ return stack.isEmpty();
+ }
+
+ /**
+ * Retrieves the item from the top of the stack, but does not remove it.
+ *
+ * @return the item from the top of the stack
+ * @throws java.util.EmptyStackException thrown if the stack is empty
+ */
+ public ObjectTypePair peek() {
+ return stack.peek();
+ }
+
+ /**
+ * Performs a memory reference check to see it the {@code obj} exists in
+ * the stack.
+ *
+ * @param obj the object to search for in the stack
+ * @return true if this object is already in the stack otherwise false
+ */
+ public boolean contains(ObjectTypePair obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ for (ObjectTypePair stackObject : stack) {
+ if (stackObject.getObject() == obj.getObject()
+ && stackObject.type.equals(obj.type) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/ModifierBasedExclusionStrategy.java b/src/org/mcteam/ancientgates/gson/ModifierBasedExclusionStrategy.java
new file mode 100644
index 0000000..ddb3ac9
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ModifierBasedExclusionStrategy.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * Exclude fields based on particular field modifiers. For a list of possible
+ * modifiers, see {@link java.lang.reflect.Modifier}.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class ModifierBasedExclusionStrategy implements ExclusionStrategy {
+ private final Collection modifiers;
+
+ public ModifierBasedExclusionStrategy(int... modifiers) {
+ this.modifiers = new HashSet();
+ if (modifiers != null) {
+ for (int modifier : modifiers) {
+ this.modifiers.add(modifier);
+ }
+ }
+ }
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ for (int modifier : modifiers) {
+ if (f.hasModifier(modifier)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean shouldSkipClass(Class> clazz) {
+ return false;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/ModifyFirstLetterNamingPolicy.java b/src/org/mcteam/ancientgates/gson/ModifyFirstLetterNamingPolicy.java
new file mode 100644
index 0000000..45554a9
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ModifyFirstLetterNamingPolicy.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * A {@link FieldNamingStrategy2} that ensures the JSON field names begins with
+ * an upper case letter.
+ *
+ *
+ *
+ * @author Joel Leitch
+ */
+final class ModifyFirstLetterNamingPolicy extends RecursiveFieldNamingPolicy {
+
+ public enum LetterModifier {
+ UPPER,
+ LOWER;
+ }
+
+ private final LetterModifier letterModifier;
+
+ /**
+ * Creates a new ModifyFirstLetterNamingPolicy that will either modify the first letter of the
+ * target name to either UPPER case or LOWER case depending on the {@code modifier} parameter.
+ *
+ * @param modifier the type of modification that should be performed
+ * @throws IllegalArgumentException if {@code modifier} is null
+ */
+ public ModifyFirstLetterNamingPolicy(LetterModifier modifier) {
+ Preconditions.checkNotNull(modifier);
+ this.letterModifier = modifier;
+ }
+
+ @Override
+ protected String translateName(String target, Type fieldType,
+ Collection annotations) {
+ StringBuilder fieldNameBuilder = new StringBuilder();
+ int index = 0;
+ char firstCharacter = target.charAt(index);
+
+ while (index < target.length() - 1) {
+ if (Character.isLetter(firstCharacter)) {
+ break;
+ }
+
+ fieldNameBuilder.append(firstCharacter);
+ firstCharacter = target.charAt(++index);
+ }
+
+ if (index == target.length()) {
+ return fieldNameBuilder.toString();
+ }
+
+ boolean capitalizeFirstLetter = (letterModifier == LetterModifier.UPPER);
+ if (capitalizeFirstLetter && !Character.isUpperCase(firstCharacter)) {
+ String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), target, ++index);
+ return fieldNameBuilder.append(modifiedTarget).toString();
+ } else if (!capitalizeFirstLetter && Character.isUpperCase(firstCharacter)) {
+ String modifiedTarget = modifyString(Character.toLowerCase(firstCharacter), target, ++index);
+ return fieldNameBuilder.append(modifiedTarget).toString();
+ } else {
+ return target;
+ }
+ }
+
+ private String modifyString(char firstCharacter, String srcString, int indexOfSubstring) {
+ return indexOfSubstring < srcString.length() ?
+ firstCharacter + srcString.substring(indexOfSubstring)
+ : String.valueOf(firstCharacter);
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/NullExclusionStrategy.java b/src/org/mcteam/ancientgates/gson/NullExclusionStrategy.java
new file mode 100644
index 0000000..8e1cedc
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/NullExclusionStrategy.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+
+/**
+ * This acts as a "Null Object" pattern for the {@link ExclusionStrategy}.
+ * Passing an instance of this class into the {@link ObjectNavigator} will
+ * make the {@link ObjectNavigator} parse/visit every field of the object
+ * being navigated.
+ *
+ * @author Joel Leitch
+ */
+final class NullExclusionStrategy implements ExclusionStrategy {
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ return false;
+ }
+
+ public boolean shouldSkipClass(Class> clazz) {
+ return false;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/ObjectConstructor.java b/src/org/mcteam/ancientgates/gson/ObjectConstructor.java
new file mode 100644
index 0000000..8c8e9eb
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ObjectConstructor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Defines a generic object construction factory. The purpose of this class
+ * is to construct a default instance of a class that can be used for object
+ * navigation while deserialization from its JSON representation.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+interface ObjectConstructor {
+
+ /**
+ * Creates a new instance of the given type.
+ *
+ * @param typeOfT the class type that should be instantiated
+ * @return a default instance of the provided class.
+ */
+ public T construct(Type typeOfT);
+
+ /**
+ * Constructs an array type of the provided length.
+ *
+ * @param typeOfArrayElements type of objects in the array
+ * @param length size of the array
+ * @return new array of size length
+ */
+ public Object constructArray(Type typeOfArrayElements, int length);
+}
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/gson/ObjectNavigator.java b/src/org/mcteam/ancientgates/gson/ObjectNavigator.java
new file mode 100644
index 0000000..b5f3964
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ObjectNavigator.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+
+/**
+ * Provides ability to apply a visitor to an object and all of its fields
+ * recursively.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class ObjectNavigator {
+
+ public interface Visitor {
+ public void start(ObjectTypePair node);
+
+ public void end(ObjectTypePair node);
+
+ /**
+ * This is called before the object navigator starts visiting the current
+ * object
+ */
+ void startVisitingObject(Object node);
+
+ /**
+ * This is called to visit the current object if it is an array
+ */
+ void visitArray(Object array, Type componentType);
+
+ /**
+ * This is called to visit an object field of the current object
+ */
+ void visitObjectField(FieldAttributes f, Type typeOfF, Object obj);
+
+ /**
+ * This is called to visit an array field of the current object
+ */
+ void visitArrayField(FieldAttributes f, Type typeOfF, Object obj);
+
+ /**
+ * This is called to visit an object using a custom handler
+ *
+ * @return true if a custom handler exists, false otherwise
+ */
+ public boolean visitUsingCustomHandler(ObjectTypePair objTypePair);
+
+ /**
+ * This is called to visit a field of the current object using a custom
+ * handler
+ */
+ public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type actualTypeOfField,
+ Object parent);
+
+ /**
+ * Retrieve the current target
+ */
+ Object getTarget();
+
+ void visitPrimitive(Object primitive);
+ }
+
+ private final ExclusionStrategy exclusionStrategy;
+ private final ObjectTypePair objTypePair;
+
+ /**
+ * @param objTypePair
+ * The object,type (fully genericized) being navigated
+ * @param exclusionStrategy
+ * the concrete strategy object to be used to filter out fields of an
+ * object.
+ */
+ ObjectNavigator(ObjectTypePair objTypePair, ExclusionStrategy exclusionStrategy) {
+ Preconditions.checkNotNull(exclusionStrategy);
+
+ this.objTypePair = objTypePair;
+ this.exclusionStrategy = exclusionStrategy;
+ }
+
+ /**
+ * Navigate all the fields of the specified object. If a field is null, it
+ * does not get visited.
+ */
+ public void accept(Visitor visitor) {
+ TypeInfo objTypeInfo = new TypeInfo(objTypePair.type);
+ if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawClass())) {
+ return;
+ }
+ boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(objTypePair);
+ if (!visitedWithCustomHandler) {
+ Object obj = objTypePair.getObject();
+ Object objectToVisit = (obj == null) ? visitor.getTarget() : obj;
+ if (objectToVisit == null) {
+ return;
+ }
+ objTypePair.setObject(objectToVisit);
+ visitor.start(objTypePair);
+ try {
+ if (objTypeInfo.isArray()) {
+ visitor.visitArray(objectToVisit, objTypePair.type);
+ } else if (objTypeInfo.getActualType() == Object.class
+ && isPrimitiveOrString(objectToVisit)) {
+ // TODO(Joel): this is only used for deserialization of "primitives"
+ // we should rethink this!!!
+ visitor.visitPrimitive(objectToVisit);
+ objectToVisit = visitor.getTarget();
+ } else {
+ visitor.startVisitingObject(objectToVisit);
+ ObjectTypePair currObjTypePair = objTypePair.toMoreSpecificType();
+ Class> topLevelClass = new TypeInfo(currObjTypePair.type).getRawClass();
+ for (Class> curr = topLevelClass; curr != null && !curr.equals(Object.class); curr =
+ curr.getSuperclass()) {
+ if (!curr.isSynthetic()) {
+ navigateClassFields(objectToVisit, curr, visitor);
+ }
+ }
+ }
+ } finally {
+ visitor.end(objTypePair);
+ }
+ }
+ }
+
+ private boolean isPrimitiveOrString(Object objectToVisit) {
+ Class> realClazz = objectToVisit.getClass();
+ return realClazz == Object.class || realClazz == String.class
+ || Primitives.unwrap(realClazz).isPrimitive();
+ }
+
+ private void navigateClassFields(Object obj, Class> clazz, Visitor visitor) {
+ Field[] fields = clazz.getDeclaredFields();
+ AccessibleObject.setAccessible(fields, true);
+ for (Field f : fields) {
+ FieldAttributes fieldAttributes = new FieldAttributes(clazz, f);
+ if (exclusionStrategy.shouldSkipField(fieldAttributes)
+ || exclusionStrategy.shouldSkipClass(fieldAttributes.getDeclaredClass())) {
+ continue; // skip
+ }
+ TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objTypePair.type);
+ Type declaredTypeOfField = fieldTypeInfo.getActualType();
+ boolean visitedWithCustomHandler =
+ visitor.visitFieldUsingCustomHandler(fieldAttributes, declaredTypeOfField, obj);
+ if (!visitedWithCustomHandler) {
+ if (fieldTypeInfo.isArray()) {
+ visitor.visitArrayField(fieldAttributes, declaredTypeOfField, obj);
+ } else {
+ visitor.visitObjectField(fieldAttributes, declaredTypeOfField, obj);
+ }
+ }
+ }
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/ObjectNavigatorFactory.java b/src/org/mcteam/ancientgates/gson/ObjectNavigatorFactory.java
new file mode 100644
index 0000000..02dcff7
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ObjectNavigatorFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * A factory class used to simplify {@link ObjectNavigator} creation.
+ * This object holds on to a reference of the {@link ExclusionStrategy}
+ * that you'd like to use with the {@link ObjectNavigator}.
+ *
+ * @author Joel Leitch
+ */
+final class ObjectNavigatorFactory {
+ private final ExclusionStrategy strategy;
+ private final FieldNamingStrategy2 fieldNamingPolicy;
+
+ /**
+ * Creates a factory object that will be able to create new
+ * {@link ObjectNavigator}s with the provided {@code strategy}
+ *
+ * @param strategy the exclusion strategy to use with every instance that
+ * is created by this factory instance.
+ * @param fieldNamingPolicy the naming policy that should be applied to field
+ * names
+ */
+ public ObjectNavigatorFactory(ExclusionStrategy strategy, FieldNamingStrategy2 fieldNamingPolicy) {
+ Preconditions.checkNotNull(fieldNamingPolicy);
+ this.strategy = (strategy == null ? new NullExclusionStrategy() : strategy);
+ this.fieldNamingPolicy = fieldNamingPolicy;
+ }
+
+ /**
+ * Creates a new {@link ObjectNavigator} for this {@code srcObject},
+ * {@code type} pair.
+ *
+ * @param objTypePair The object,type (fully genericized) being navigated
+ * @return a new instance of a {@link ObjectNavigator} ready to navigate the
+ * {@code srcObject} while taking into consideration the
+ * {@code type}.
+ */
+ public ObjectNavigator create(ObjectTypePair objTypePair) {
+ return new ObjectNavigator(objTypePair, strategy);
+ }
+
+ FieldNamingStrategy2 getFieldNamingPolicy() {
+ return fieldNamingPolicy;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/ObjectTypePair.java b/src/org/mcteam/ancientgates/gson/ObjectTypePair.java
new file mode 100644
index 0000000..5f3ad99
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ObjectTypePair.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * A holder class for an object and its type
+ *
+ * @author Inderjeet Singh
+ */
+final class ObjectTypePair {
+ private Object obj;
+ final Type type;
+ private final boolean preserveType;
+
+ ObjectTypePair(Object obj, Type type, boolean preserveType) {
+ this.obj = obj;
+ this.type = type;
+ this.preserveType = preserveType;
+ }
+
+ Object getObject() {
+ return obj;
+ }
+
+ void setObject(Object obj) {
+ this.obj = obj;
+ }
+
+ Type getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("preserveType: %b, type: %s, obj: %s", preserveType, type, obj);
+ }
+
+ Pair getMatchingHandler(
+ ParameterizedTypeHandlerMap handlers) {
+ HANDLER handler = null;
+ if (!preserveType && obj != null) {
+ // First try looking up the handler for the actual type
+ ObjectTypePair moreSpecificType = toMoreSpecificType();
+ handler = handlers.getHandlerFor(moreSpecificType.type);
+ if (handler != null) {
+ return new Pair(handler, moreSpecificType);
+ }
+ }
+ // Try the specified type
+ handler = handlers.getHandlerFor(type);
+ return handler == null ? null : new Pair(handler, this);
+ }
+
+ ObjectTypePair toMoreSpecificType() {
+ if (preserveType || obj == null) {
+ return this;
+ }
+ Type actualType = getActualTypeIfMoreSpecific(type, obj.getClass());
+ if (actualType == type) {
+ return this;
+ }
+ return new ObjectTypePair(obj, actualType, preserveType);
+ }
+
+ // This takes care of situations where the field was declared as an Object, but the
+ // actual value contains something more specific. See Issue 54.
+ // TODO (inder): This solution will not work if the field is of a generic type, but
+ // the actual object is of a raw type (which is a sub-class of the generic type).
+ static Type getActualTypeIfMoreSpecific(Type type, Class> actualClass) {
+ if (type instanceof Class>) {
+ Class> typeAsClass = (Class>) type;
+ if (typeAsClass.isAssignableFrom(actualClass)) {
+ type = actualClass;
+ }
+ if (type == Object.class) {
+ type = actualClass;
+ }
+ }
+ return type;
+ }
+
+ @Override
+ public int hashCode() {
+ // Not using type.hashCode() since I am not sure if the subclasses of type reimplement
+ // hashCode() to be equal for equal types
+ return ((obj == null) ? 31 : obj.hashCode());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ObjectTypePair other = (ObjectTypePair) obj;
+ if (this.obj == null) {
+ if (other.obj != null) {
+ return false;
+ }
+ } else if (this.obj != other.obj) { // Checking for reference equality
+ return false;
+ }
+ if (type == null) {
+ if (other.type != null) {
+ return false;
+ }
+ } else if (!type.equals(other.type)) {
+ return false;
+ }
+ return preserveType == other.preserveType;
+ }
+
+ public boolean isPreserveType() {
+ return preserveType;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/Pair.java b/src/org/mcteam/ancientgates/gson/Pair.java
new file mode 100644
index 0000000..ff95ae8
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/Pair.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * A simple object that holds onto a pair of object references, first and second.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @param
+ * @param
+ */
+final class Pair {
+
+ final FIRST first;
+ final SECOND second;
+
+ Pair(FIRST first, SECOND second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ @Override
+ public int hashCode() {
+ return 17 * ((first != null) ? first.hashCode() : 0)
+ + 17 * ((second != null) ? second.hashCode() : 0);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Pair, ?>)) {
+ return false;
+ }
+
+ Pair, ?> that = (Pair, ?>) o;
+ return equal(this.first, that.first) && equal(this.second, that.second);
+ }
+
+ private static boolean equal(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{%s,%s}", first, second);
+ }
+}
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/gson/ParameterizedTypeHandlerMap.java b/src/org/mcteam/ancientgates/gson/ParameterizedTypeHandlerMap.java
new file mode 100644
index 0000000..ebb5a31
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ParameterizedTypeHandlerMap.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A map that provides ability to associate handlers for a specific type or all
+ * of its sub-types
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @param The handler that will be looked up by type
+ */
+final class ParameterizedTypeHandlerMap {
+ private static final Logger logger =
+ Logger.getLogger(ParameterizedTypeHandlerMap.class.getName());
+ private final Map map = new HashMap();
+ private final List, T>> typeHierarchyList = new ArrayList, T>>();
+ private boolean modifiable = true;
+
+ public synchronized void registerForTypeHierarchy(Class> typeOfT, T value) {
+ Pair, T> pair = new Pair, T>(typeOfT, value);
+ registerForTypeHierarchy(pair);
+ }
+
+ public synchronized void registerForTypeHierarchy(Pair, T> pair) {
+ if (!modifiable) {
+ throw new IllegalStateException("Attempted to modify an unmodifiable map.");
+ }
+ int index = getIndexOfSpecificHandlerForTypeHierarchy(pair.first);
+ if (index >= 0) {
+ logger.log(Level.WARNING, "Overriding the existing type handler for {0}", pair.first);
+ typeHierarchyList.remove(index);
+ }
+ index = getIndexOfAnOverriddenHandler(pair.first);
+ if (index >= 0) {
+ throw new IllegalArgumentException("The specified type handler for type " + pair.first
+ + " hides the previously registered type hierarchy handler for "
+ + typeHierarchyList.get(index).first + ". Gson does not allow this.");
+ }
+ // We want stack behavior for adding to this list. A type adapter added subsequently should
+ // override a previously registered one.
+ typeHierarchyList.add(0, pair);
+ }
+
+ private int getIndexOfAnOverriddenHandler(Class> type) {
+ for (int i = typeHierarchyList.size()-1; i >= 0; --i) {
+ Pair, T> entry = typeHierarchyList.get(i);
+ if (type.isAssignableFrom(entry.first)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public synchronized void register(Type typeOfT, T value) {
+ if (!modifiable) {
+ throw new IllegalStateException("Attempted to modify an unmodifiable map.");
+ }
+ if (hasSpecificHandlerFor(typeOfT)) {
+ logger.log(Level.WARNING, "Overriding the existing type handler for {0}", typeOfT);
+ }
+ map.put(typeOfT, value);
+ }
+
+ public synchronized void registerIfAbsent(ParameterizedTypeHandlerMap other) {
+ if (!modifiable) {
+ throw new IllegalStateException("Attempted to modify an unmodifiable map.");
+ }
+ for (Map.Entry entry : other.map.entrySet()) {
+ if (!map.containsKey(entry.getKey())) {
+ register(entry.getKey(), entry.getValue());
+ }
+ }
+ // Quite important to traverse the typeHierarchyList from stack bottom first since
+ // we want to register the handlers in the same order to preserve priority order
+ for (int i = other.typeHierarchyList.size()-1; i >= 0; --i) {
+ Pair, T> entry = other.typeHierarchyList.get(i);
+ int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first);
+ if (index < 0) {
+ registerForTypeHierarchy(entry);
+ }
+ }
+ }
+
+ public synchronized void registerIfAbsent(Type typeOfT, T value) {
+ if (!modifiable) {
+ throw new IllegalStateException("Attempted to modify an unmodifiable map.");
+ }
+ if (!map.containsKey(typeOfT)) {
+ register(typeOfT, value);
+ }
+ }
+
+ public synchronized void makeUnmodifiable() {
+ modifiable = false;
+ }
+
+ public synchronized T getHandlerFor(Type type) {
+ T handler = map.get(type);
+ if (handler == null) {
+ Class> rawClass = TypeUtils.toRawClass(type);
+ if (rawClass != type) {
+ handler = getHandlerFor(rawClass);
+ }
+ if (handler == null) {
+ // check if something registered for type hierarchy
+ handler = getHandlerForTypeHierarchy(rawClass);
+ }
+ }
+ return handler;
+ }
+
+ private T getHandlerForTypeHierarchy(Class> type) {
+ for (Pair, T> entry : typeHierarchyList) {
+ if (entry.first.isAssignableFrom(type)) {
+ return entry.second;
+ }
+ }
+ return null;
+ }
+
+ public synchronized boolean hasSpecificHandlerFor(Type type) {
+ return map.containsKey(type);
+ }
+
+ private synchronized int getIndexOfSpecificHandlerForTypeHierarchy(Class> type) {
+ for (int i = typeHierarchyList.size()-1; i >= 0; --i) {
+ if (type.equals(typeHierarchyList.get(i).first)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public synchronized ParameterizedTypeHandlerMap copyOf() {
+ ParameterizedTypeHandlerMap copy = new ParameterizedTypeHandlerMap();
+ for (Map.Entry entry : map.entrySet()) {
+ copy.register(entry.getKey(), entry.getValue());
+ }
+ for (Pair, T> entry : typeHierarchyList) {
+ copy.registerForTypeHierarchy(entry);
+ }
+ return copy;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("{mapForTypeHierarchy:{");
+ boolean first = true;
+ for (Pair, T> entry : typeHierarchyList) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(typeToString(entry.first)).append(':');
+ sb.append(entry.second);
+ }
+ sb.append("},map:{");
+ first = true;
+ for (Map.Entry entry : map.entrySet()) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(typeToString(entry.getKey())).append(':');
+ sb.append(entry.getValue());
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private String typeToString(Type type) {
+ return TypeUtils.toRawClass(type).getSimpleName();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/ParameterizedTypeImpl.java b/src/org/mcteam/ancientgates/gson/ParameterizedTypeImpl.java
new file mode 100644
index 0000000..891df01
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/ParameterizedTypeImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+/**
+ * An immutable implementation of the {@link ParameterizedType} interface. This object allows
+ * us to build a reflective {@link Type} objects on demand. This object is used to support
+ * serialization and deserialization of classes with an {@code ParameterizedType} field where
+ * as least one of the actual type parameters is a {@code TypeVariable}.
+ *
+ *
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class ParameterizedTypeImpl implements ParameterizedType {
+
+ private final Type rawType;
+ private final Type[] actualTypeArguments;
+ private final Type owner;
+
+ public ParameterizedTypeImpl(Type rawType, Type[] actualTypeArguments, Type owner) {
+ this.rawType = rawType;
+ this.actualTypeArguments = actualTypeArguments;
+ this.owner = owner;
+ }
+
+ public Type getRawType() {
+ return rawType;
+ }
+
+ public Type[] getActualTypeArguments() {
+ return actualTypeArguments;
+ }
+
+ public Type getOwnerType() {
+ return owner;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ParameterizedType)) {
+ return false;
+ }
+ // Check that information is equivalent
+ ParameterizedType that = (ParameterizedType) o;
+ if (this == that) {
+ return true;
+ }
+ Type thatOwner = that.getOwnerType();
+ Type thatRawType = that.getRawType();
+
+ return (owner == null ? thatOwner == null : owner.equals(thatOwner))
+ && (rawType == null ? thatRawType == null : rawType.equals(thatRawType))
+ && Arrays.equals(actualTypeArguments, that.getActualTypeArguments());
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(actualTypeArguments)
+ ^ (owner == null ? 0 : owner.hashCode())
+ ^ (rawType == null ? 0 : rawType.hashCode());
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/Preconditions.java b/src/org/mcteam/ancientgates/gson/Preconditions.java
new file mode 100644
index 0000000..178d3af
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/Preconditions.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * A simple utility class used to check method Preconditions.
+ *
+ *
+ */
+ public static Class wrap(Class type) {
+ checkNotNull(type);
+
+ // cast is safe: long.class and Long.class are both of type Class
+ @SuppressWarnings("unchecked")
+ Class wrapped = (Class) PRIMITIVE_TO_WRAPPER_TYPE.get(type);
+ return (wrapped == null) ? type : wrapped;
+ }
+
+ /**
+ * Returns the corresponding primitive type of {@code type} if it is a
+ * wrapper type; otherwise returns {@code type} itself. Idempotent.
+ *
+ */
+ public static Class unwrap(Class type) {
+ checkNotNull(type);
+
+ // cast is safe: long.class and Long.class are both of type Class
+ @SuppressWarnings("unchecked")
+ Class unwrapped = (Class) WRAPPER_TO_PRIMITIVE_TYPE.get(type);
+ return (unwrapped == null) ? type : unwrapped;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/RecursiveFieldNamingPolicy.java b/src/org/mcteam/ancientgates/gson/RecursiveFieldNamingPolicy.java
new file mode 100644
index 0000000..1d0062a
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/RecursiveFieldNamingPolicy.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * A mechanism for providing custom field naming in Gson. This allows the client code to translate
+ * field names into a particular convention that is not supported as a normal Java field
+ * declaration rules. For example, Java does not support "-" characters in a field name.
+ *
+ * @author Joel Leitch
+ */
+abstract class RecursiveFieldNamingPolicy implements FieldNamingStrategy2 {
+
+ public final String translateName(FieldAttributes f) {
+ Preconditions.checkNotNull(f);
+ return translateName(f.getName(), f.getDeclaredType(), f.getAnnotations());
+ }
+
+ /**
+ * Performs the specific string translation.
+ *
+ * @param target the string object that will be manipulation/translated
+ * @param fieldType the actual type value of the field
+ * @param annotations the annotations set on the field
+ * @return the translated field name
+ */
+ protected abstract String translateName(String target, Type fieldType, Collection annotations);
+}
diff --git a/src/org/mcteam/ancientgates/gson/SerializedNameAnnotationInterceptingNamingPolicy.java b/src/org/mcteam/ancientgates/gson/SerializedNameAnnotationInterceptingNamingPolicy.java
new file mode 100644
index 0000000..6ab6756
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/SerializedNameAnnotationInterceptingNamingPolicy.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import org.mcteam.ancientgates.gson.annotations.SerializedName;
+
+/**
+ * A {@link FieldNamingStrategy2} that acts as a chain of responsibility. If the
+ * {@link org.mcteam.ancientgates.gson.annotations.SerializedName} annotation is applied to a field then this
+ * strategy will translate the name to the {@code serializedName.value()}; otherwise it delegates
+ * to the wrapped {@link FieldNamingStrategy2}.
+ *
+ *
NOTE: this class performs JSON field name validation for any of the fields marked with
+ * an {@code @SerializedName} annotation.
+ *
+ * @see SerializedName
+ *
+ * @author Joel Leitch
+ */
+final class SerializedNameAnnotationInterceptingNamingPolicy implements FieldNamingStrategy2 {
+ private static final JsonFieldNameValidator fieldNameValidator = new JsonFieldNameValidator();
+ private final FieldNamingStrategy2 delegate;
+
+ public SerializedNameAnnotationInterceptingNamingPolicy(FieldNamingStrategy2 delegate) {
+ this.delegate = delegate;
+ }
+
+ public String translateName(FieldAttributes f) {
+ Preconditions.checkNotNull(f);
+ SerializedName serializedName = f.getAnnotation(SerializedName.class);
+ return serializedName == null ? delegate.translateName(f)
+ : fieldNameValidator.validate(serializedName.value());
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/Streams.java b/src/org/mcteam/ancientgates/gson/Streams.java
new file mode 100644
index 0000000..2b77d84
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/Streams.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+import org.mcteam.ancientgates.gson.stream.JsonReader;
+import org.mcteam.ancientgates.gson.stream.JsonWriter;
+import org.mcteam.ancientgates.gson.stream.MalformedJsonException;
+
+/**
+ * Reads and writes GSON parse trees over streams.
+ */
+final class Streams {
+
+ /**
+ * Takes a reader in any state and returns the next value as a JsonElement.
+ */
+ static JsonElement parse(JsonReader reader) throws JsonParseException {
+ boolean isEmpty = true;
+ try {
+ reader.peek();
+ isEmpty = false;
+ return parseRecursive(reader);
+ } catch (EOFException e) {
+ /*
+ * For compatibility with JSON 1.5 and earlier, we return a JsonNull for
+ * empty documents instead of throwing.
+ */
+ if (isEmpty) {
+ return JsonNull.createJsonNull();
+ }
+ throw new JsonIOException(e);
+ } catch (MalformedJsonException e) {
+ throw new JsonSyntaxException(e);
+ } catch (IOException e) {
+ throw new JsonIOException(e);
+ } catch (NumberFormatException e) {
+ throw new JsonSyntaxException(e);
+ }
+ }
+
+ private static JsonElement parseRecursive(JsonReader reader) throws IOException {
+ switch (reader.peek()) {
+ case STRING:
+ return new JsonPrimitive(reader.nextString());
+ case NUMBER:
+ String number = reader.nextString();
+ return new JsonPrimitive(JsonPrimitive.stringToNumber(number));
+ case BOOLEAN:
+ return new JsonPrimitive(reader.nextBoolean());
+ case NULL:
+ reader.nextNull();
+ return JsonNull.createJsonNull();
+ case BEGIN_ARRAY:
+ JsonArray array = new JsonArray();
+ reader.beginArray();
+ while (reader.hasNext()) {
+ array.add(parseRecursive(reader));
+ }
+ reader.endArray();
+ return array;
+ case BEGIN_OBJECT:
+ JsonObject object = new JsonObject();
+ reader.beginObject();
+ while (reader.hasNext()) {
+ object.add(reader.nextName(), parseRecursive(reader));
+ }
+ reader.endObject();
+ return object;
+ case END_DOCUMENT:
+ case NAME:
+ case END_OBJECT:
+ case END_ARRAY:
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Writes the JSON element to the writer, recursively.
+ */
+ static void write(JsonElement element, boolean serializeNulls, JsonWriter writer)
+ throws IOException {
+ if (element == null || element.isJsonNull()) {
+ if (serializeNulls) {
+ writer.nullValue();
+ }
+
+ } else if (element.isJsonPrimitive()) {
+ JsonPrimitive primitive = element.getAsJsonPrimitive();
+ if (primitive.isNumber()) {
+ writer.value(primitive.getAsNumber());
+ } else if (primitive.isBoolean()) {
+ writer.value(primitive.getAsBoolean());
+ } else {
+ writer.value(primitive.getAsString());
+ }
+
+ } else if (element.isJsonArray()) {
+ writer.beginArray();
+ for (JsonElement e : element.getAsJsonArray()) {
+ /* always print null when its parent element is an array! */
+ if (e.isJsonNull()) {
+ writer.nullValue();
+ continue;
+ }
+ write(e, serializeNulls, writer);
+ }
+ writer.endArray();
+
+ } else if (element.isJsonObject()) {
+ writer.beginObject();
+ for (Map.Entry e : element.getAsJsonObject().entrySet()) {
+ JsonElement value = e.getValue();
+ if (!serializeNulls && value.isJsonNull()) {
+ continue;
+ }
+ writer.name(e.getKey());
+ write(value, serializeNulls, writer);
+ }
+ writer.endObject();
+
+ } else {
+ throw new IllegalArgumentException("Couldn't write " + element.getClass());
+ }
+ }
+
+ static Writer writerForAppendable(Appendable appendable) {
+ return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
+ }
+
+ /**
+ * Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
+ * is used.
+ */
+ private static class AppendableWriter extends Writer {
+ private final Appendable appendable;
+ private final CurrentWrite currentWrite = new CurrentWrite();
+
+ private AppendableWriter(Appendable appendable) {
+ this.appendable = appendable;
+ }
+
+ @Override public void write(char[] chars, int offset, int length) throws IOException {
+ currentWrite.chars = chars;
+ appendable.append(currentWrite, offset, offset + length);
+ }
+
+ @Override public void write(int i) throws IOException {
+ appendable.append((char) i);
+ }
+
+ @Override public void flush() {}
+ @Override public void close() {}
+
+ /**
+ * A mutable char sequence pointing at a single char[].
+ */
+ static class CurrentWrite implements CharSequence {
+ char[] chars;
+ public int length() {
+ return chars.length;
+ }
+ public char charAt(int i) {
+ return chars[i];
+ }
+ public CharSequence subSequence(int start, int end) {
+ return new String(chars, start, end - start);
+ }
+ }
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/SyntheticFieldExclusionStrategy.java b/src/org/mcteam/ancientgates/gson/SyntheticFieldExclusionStrategy.java
new file mode 100644
index 0000000..ff61e82
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/SyntheticFieldExclusionStrategy.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * A data object that stores attributes of a field.
+ *
+ *
This class is immutable; therefore, it can be safely shared across threads.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @since 1.4
+ */
+class SyntheticFieldExclusionStrategy implements ExclusionStrategy {
+ private final boolean skipSyntheticFields;
+
+ SyntheticFieldExclusionStrategy(boolean skipSyntheticFields) {
+ this.skipSyntheticFields = skipSyntheticFields;
+ }
+
+ public boolean shouldSkipClass(Class> clazz) {
+ return false;
+ }
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ return skipSyntheticFields && f.isSynthetic();
+ }
+
+}
diff --git a/src/org/mcteam/ancientgates/gson/TypeAdapter.java b/src/org/mcteam/ancientgates/gson/TypeAdapter.java
new file mode 100644
index 0000000..d06248c
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/TypeAdapter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * This class is responsible for adapting/converting an particular "from"
+ * instance to an instance of type "to".
+ *
+ * @author Joel Leitch
+ */
+interface TypeAdapter {
+
+ /**
+ * Adapts an object instance "from" to and instance of type "to".
+ *
+ * @param from the object to adapt
+ * @param to the Type/Class which this will convert to
+ * @return the converted "from" instance to type "to"
+ */
+ public T adaptType(Object from, Class to);
+}
diff --git a/src/org/mcteam/ancientgates/gson/TypeInfo.java b/src/org/mcteam/ancientgates/gson/TypeInfo.java
new file mode 100644
index 0000000..d292e39
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/TypeInfo.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * Class that provides information relevant to different parts of a type.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+class TypeInfo {
+ protected final Type actualType;
+ protected final Class> rawClass;
+
+ TypeInfo(Type actualType) {
+ this.actualType = actualType;
+ rawClass = TypeUtils.toRawClass(actualType);
+ }
+
+ public final Type getActualType() {
+ return actualType;
+ }
+
+ /**
+ * Returns the corresponding wrapper type of {@code type} if it is a primitive
+ * type; otherwise returns {@code type} itself. Idempotent.
+ *
+ * TypeUtils.getActualTypeForFirstTypeVariable(fooType) will return Integer.class.
+ */
+ static Type getActualTypeForFirstTypeVariable(Type type) {
+ if (type instanceof Class>) {
+ return Object.class;
+ } else if (type instanceof ParameterizedType) {
+ return ((ParameterizedType)type).getActualTypeArguments()[0];
+ } else if (type instanceof GenericArrayType) {
+ return getActualTypeForFirstTypeVariable(((GenericArrayType)type).getGenericComponentType());
+ } else {
+ throw new IllegalArgumentException("Type \'" + type + "\' is not a Class, "
+ + "ParameterizedType, or GenericArrayType. Can't extract class.");
+ }
+ }
+
+ static boolean isArray(Type type) {
+ if (type instanceof Class>) {
+ return ((Class>)type).isArray();
+ } else if (type instanceof GenericArrayType) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This method returns the actual raw class associated with the specified type.
+ */
+ static Class> toRawClass(Type type) {
+ if (type instanceof Class>) {
+ return (Class>) type;
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType actualType = (ParameterizedType)type;
+ return toRawClass(actualType.getRawType());
+ } else if (type instanceof GenericArrayType) {
+ GenericArrayType actualType = (GenericArrayType) type;
+ Class> rawClass = toRawClass(actualType.getGenericComponentType());
+ return wrapWithArray(rawClass);
+ } else if (type instanceof WildcardType) {
+ WildcardType castedType = (WildcardType) type;
+ return toRawClass(castedType.getUpperBounds()[0]);
+ } else {
+ throw new IllegalArgumentException("Type \'" + type + "\' is not a Class, "
+ + "ParameterizedType, or GenericArrayType. Can't extract class.");
+ }
+ }
+
+ static Class> wrapWithArray(Class> rawClass) {
+ return Array.newInstance(rawClass, 0).getClass();
+ }
+
+ private TypeUtils() {
+ // Class with just some static utility methods, should not be instantiated
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/UpperCamelCaseSeparatorNamingPolicy.java b/src/org/mcteam/ancientgates/gson/UpperCamelCaseSeparatorNamingPolicy.java
new file mode 100644
index 0000000..ddc8cdd
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/UpperCamelCaseSeparatorNamingPolicy.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * A {@link FieldNamingStrategy2} that ensures the JSON field names consist of mixed
+ * case letters starting with a capital and are separated by a particular
+ * {@code separatorString}.
+ *
+ *
+ *
+ * @author Joel Leitch
+ */
+final class UpperCamelCaseSeparatorNamingPolicy extends CompositionFieldNamingPolicy {
+
+ public UpperCamelCaseSeparatorNamingPolicy(String separatorString) {
+ super(new CamelCaseSeparatorNamingPolicy(separatorString),
+ new ModifyFirstLetterNamingPolicy(ModifyFirstLetterNamingPolicy.LetterModifier.UPPER));
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/UpperCaseNamingPolicy.java b/src/org/mcteam/ancientgates/gson/UpperCaseNamingPolicy.java
new file mode 100644
index 0000000..53efe3f
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/UpperCaseNamingPolicy.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * A {@link FieldNamingStrategy2} that ensures the JSON field names consist of only
+ * upper case letters.
+ *
+ *
The following is an example:
+ *
+ * class IntWrapper {
+ * public int integerField = 0;
+ * }
+ *
+ * UpperCaseNamingPolicy policy = new UpperCaseNamingPolicy();
+ * String translatedFieldName =
+ * policy.translateName(IntWrapper.class.getField("integerField"));
+ *
+ * assert("INTEGERFIELD".equals(translatedFieldName));
+ *
+ *
+ * @author Joel Leitch
+ */
+final class UpperCaseNamingPolicy extends RecursiveFieldNamingPolicy {
+
+ @Override
+ protected String translateName(String target, Type fieldType, Collection annotations) {
+ return target.toUpperCase();
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/VersionConstants.java b/src/org/mcteam/ancientgates/gson/VersionConstants.java
new file mode 100644
index 0000000..f1f96b0
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/VersionConstants.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+/**
+ * Class contain all constants for versioning support.
+ *
+ * @author Joel Leitch
+ */
+final class VersionConstants {
+ // Prevent instantiation
+ private VersionConstants() { }
+
+ static final double IGNORE_VERSIONS = -1D;
+}
diff --git a/src/org/mcteam/ancientgates/gson/VersionExclusionStrategy.java b/src/org/mcteam/ancientgates/gson/VersionExclusionStrategy.java
new file mode 100644
index 0000000..6e48122
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/VersionExclusionStrategy.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson;
+
+import org.mcteam.ancientgates.gson.annotations.Since;
+import org.mcteam.ancientgates.gson.annotations.Until;
+
+/**
+ * This strategy will exclude any files and/or class that are passed the
+ * {@link #version} value.
+ *
+ * @author Joel Leitch
+ */
+final class VersionExclusionStrategy implements ExclusionStrategy {
+ private final double version;
+
+ public VersionExclusionStrategy(double version) {
+ Preconditions.checkArgument(version >= 0.0D);
+ this.version = version;
+ }
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ return !isValidVersion(f.getAnnotation(Since.class), f.getAnnotation(Until.class));
+ }
+
+ public boolean shouldSkipClass(Class> clazz) {
+ return !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class));
+ }
+
+ private boolean isValidVersion(Since since, Until until) {
+ return (isValidSince(since) && isValidUntil(until));
+ }
+
+ private boolean isValidSince(Since annotation) {
+ if (annotation != null) {
+ double annotationVersion = annotation.value();
+ if (annotationVersion > version) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isValidUntil(Until annotation) {
+ if (annotation != null) {
+ double annotationVersion = annotation.value();
+ if (annotationVersion <= version) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/annotations/Expose.java b/src/org/mcteam/ancientgates/gson/annotations/Expose.java
new file mode 100644
index 0000000..e6889f6
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/annotations/Expose.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates this member should be exposed for JSON
+ * serialization or deserialization.
+ *
+ *
This annotation has no effect unless you build {@link org.mcteam.ancientgates.gson.Gson}
+ * with a {@link org.mcteam.ancientgates.gson.GsonBuilder} and invoke
+ * {@link org.mcteam.ancientgates.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()}
+ * method.
+ *
+ *
Here is an example of how this annotation is meant to be used:
+ *
+ * If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
+ * methods will use the {@code password} field along-with {@code firstName}, {@code lastName},
+ * and {@code emailAddress} for serialization and deserialization. However, if you created Gson
+ * with {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()}
+ * then the {@code toJson()} and {@code fromJson()} methods of Gson will exclude the
+ * {@code password} field. This is because the {@code password} field is not marked with the
+ * {@code @Expose} annotation. Gson will also exclude {@code lastName} and {@code emailAddress}
+ * from serialization since {@code serialize} is set to {@code false}. Similarly, Gson will
+ * exclude {@code emailAddress} from deserialization since {@code deserialize} is set to false.
+ *
+ *
Note that another way to achieve the same effect would have been to just mark the
+ * {@code password} field as {@code transient}, and Gson would have excluded it even with default
+ * settings. The {@code @Expose} annotation is useful in a style of programming where you want to
+ * explicitly specify all fields that should get considered for serialization or deserialization.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Expose {
+
+ /**
+ * If {@code true}, the field marked with this annotation is written out in the JSON while
+ * serializing. If {@code false}, the field marked with this annotation is skipped from the
+ * serialized output. Defaults to {@code true}.
+ * @since 1.4
+ */
+ public boolean serialize() default true;
+
+ /**
+ * If {@code true}, the field marked with this annotation is deserialized from the JSON.
+ * If {@code false}, the field marked with this annotation is skipped during deserialization.
+ * Defaults to {@code true}.
+ * @since 1.4
+ */
+ public boolean deserialize() default true;
+}
diff --git a/src/org/mcteam/ancientgates/gson/annotations/SerializedName.java b/src/org/mcteam/ancientgates/gson/annotations/SerializedName.java
new file mode 100644
index 0000000..d7c3881
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/annotations/SerializedName.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates this member should be serialized to JSON with
+ * the provided name value as its field name.
+ *
+ *
This annotation will override any {@link org.mcteam.ancientgates.gson.FieldNamingPolicy}, including
+ * the default field naming policy, that may have been set on the {@link org.mcteam.ancientgates.gson.Gson}
+ * instance. A different naming policy can set using the {@code GsonBuilder} class. See
+ * {@link org.mcteam.ancientgates.gson.GsonBuilder#setFieldNamingPolicy(org.mcteam.ancientgates.gson.FieldNamingPolicy)}
+ * for more information.
+ *
+ *
Here is an example of how this annotation is meant to be used:
+ *
+ * public class SomeClassWithFields {
+ * @SerializedName("name") private final String someField;
+ * private final String someOtherField;
+ *
+ * public SomeClassWithFields(String a, String b) {
+ * this.someField = a;
+ * this.someOtherField = b;
+ * }
+ * }
+ *
+ *
+ *
The following shows the output that is generated when serializing an instance of the
+ * above example class:
NOTE: The value you specify in this annotation must be a valid JSON field name.
+ *
+ * @see org.mcteam.ancientgates.gson.FieldNamingPolicy
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface SerializedName {
+
+ /**
+ * @return the desired name of the field when it is serialized
+ */
+ String value();
+}
diff --git a/src/org/mcteam/ancientgates/gson/annotations/Since.java b/src/org/mcteam/ancientgates/gson/annotations/Since.java
new file mode 100644
index 0000000..abeb317
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/annotations/Since.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates the version number since a member or a type has been present.
+ * This annotation is useful to manage versioning of your Json classes for a web-service.
+ *
+ *
+ * This annotation has no effect unless you build {@link org.mcteam.ancientgates.gson.Gson} with a
+ * {@link org.mcteam.ancientgates.gson.GsonBuilder} and invoke
+ * {@link org.mcteam.ancientgates.gson.GsonBuilder#setVersion(double)} method.
+ *
+ *
Here is an example of how this annotation is meant to be used:
If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
+ * methods will use all the fields for serialization and deserialization. However, if you created
+ * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.0).create()} then the
+ * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code address} field
+ * since it's version number is set to {@code 1.1}.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.TYPE})
+public @interface Since {
+ /**
+ * the value indicating a version number since this member
+ * or type has been present.
+ */
+ double value();
+}
diff --git a/src/org/mcteam/ancientgates/gson/annotations/Until.java b/src/org/mcteam/ancientgates/gson/annotations/Until.java
new file mode 100644
index 0000000..36c1db0
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/annotations/Until.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates the version number until a member or a type should be present.
+ * Basically, if Gson is created with a version number that exceeds the value stored in the
+ * {@code Until} annotation then the field will be ignored from the JSON output. This annotation
+ * is useful to manage versioning of your JSON classes for a web-service.
+ *
+ *
+ * This annotation has no effect unless you build {@link org.mcteam.ancientgates.gson.Gson} with a
+ * {@link org.mcteam.ancientgates.gson.GsonBuilder} and invoke
+ * {@link org.mcteam.ancientgates.gson.GsonBuilder#setVersion(double)} method.
+ *
+ *
Here is an example of how this annotation is meant to be used:
If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
+ * methods will use all the fields for serialization and deserialization. However, if you created
+ * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.2).create()} then the
+ * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code emailAddress}
+ * and {@code password} fields from the example above, because the version number passed to the
+ * GsonBuilder, {@code 1.2}, exceeds the version number set on the {@code Until} annotation,
+ * {@code 1.1}, for those fields.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.3
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.TYPE})
+public @interface Until {
+
+ /**
+ * the value indicating a version number until this member
+ * or type should be ignored.
+ */
+ double value();
+}
diff --git a/src/org/mcteam/ancientgates/gson/annotations/package-info.java b/src/org/mcteam/ancientgates/gson/annotations/package-info.java
new file mode 100644
index 0000000..0d6165d
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/annotations/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * This package provides annotations that can be used with {@link org.mcteam.ancientgates.gson.Gson}.
+ *
+ * @author Inderjeet Singh, Joel Leitch
+ */
+package org.mcteam.ancientgates.gson.annotations;
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/gson/package-info.java b/src/org/mcteam/ancientgates/gson/package-info.java
new file mode 100644
index 0000000..84f87fa
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/package-info.java
@@ -0,0 +1,11 @@
+/**
+ * This package provides the {@link com.bukkit.mcteam.gson.Gson} class to convert Json to Java and
+ * vice-versa.
+ *
+ *
The primary class to use is {@link com.bukkit.mcteam.gson.Gson} which can be constructed with
+ * {@code new Gson()} (using default settings) or by using {@link com.bukkit.mcteam.gson.GsonBuilder}
+ * (to configure various options such as using versioning and so on).
+ *
+ * @author Inderjeet Singh, Joel Leitch
+ */
+package org.mcteam.ancientgates.gson;
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/gson/reflect/TypeToken.java b/src/org/mcteam/ancientgates/gson/reflect/TypeToken.java
new file mode 100644
index 0000000..8f890f7
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/reflect/TypeToken.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson.reflect;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents a generic type {@code T}.
+ *
+ * You can use this class to get the generic type for a class. For example,
+ * to get the generic type for Collection<Foo>, you can use:
+ *
+ * Type typeOfCollectionOfFoo = new TypeToken<Collection<Foo>>(){}.getType()
+ *
+ *
+ *
Assumes {@code Type} implements {@code equals()} and {@code hashCode()}
+ * as a value (as opposed to identity) comparison.
+ *
+ * Also implements {@link #isAssignableFrom(Type)} to check type-safe
+ * assignability.
+ *
+ * @author Bob Lee
+ * @author Sven Mawson
+ */
+public abstract class TypeToken {
+
+ final Class super T> rawType;
+ final Type type;
+
+ /**
+ * Constructs a new type token. Derives represented class from type
+ * parameter.
+ *
+ *
Clients create an empty anonymous subclass. Doing so embeds the type
+ * parameter in the anonymous class's type hierarchy so we can reconstitute
+ * it at runtime despite erasure.
+ *
+ *
For example:
+ *
+ * {@literal TypeToken> t = new TypeToken>}(){}
+ *
+ */
+ @SuppressWarnings("unchecked")
+ protected TypeToken() {
+ this.type = getSuperclassTypeParameter(getClass());
+ this.rawType = (Class super T>) getRawType(type);
+ }
+
+ /**
+ * Unsafe. Constructs a type token manually.
+ */
+ @SuppressWarnings({"unchecked"})
+ private TypeToken(Type type) {
+ this.rawType = (Class super T>) getRawType(nonNull(type, "type"));
+ this.type = type;
+ }
+
+ private static T nonNull(T o, String message) {
+ if (o == null) {
+ throw new NullPointerException(message);
+ }
+ return o;
+ }
+
+ /**
+ * Gets type from super class's type parameter.
+ */
+ static Type getSuperclassTypeParameter(Class> subclass) {
+ Type superclass = subclass.getGenericSuperclass();
+ if (superclass instanceof Class>) {
+ throw new RuntimeException("Missing type parameter.");
+ }
+ return ((ParameterizedType) superclass).getActualTypeArguments()[0];
+ }
+
+ /**
+ * Gets type token from super class's type parameter.
+ */
+ static TypeToken> fromSuperclassTypeParameter(Class> subclass) {
+ return new SimpleTypeToken(subclass);
+ }
+
+ private static Class> getRawType(Type type) {
+ if (type instanceof Class>) {
+ // type is a normal class.
+ return (Class>) type;
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+
+ // I'm not exactly sure why getRawType() returns Type instead of Class.
+ // Neal isn't either but suspects some pathological case related
+ // to nested classes exists.
+ Type rawType = parameterizedType.getRawType();
+ if (rawType instanceof Class>) {
+ return (Class>) rawType;
+ }
+ throw buildUnexpectedTypeError(rawType, Class.class);
+ } else if (type instanceof GenericArrayType) {
+ GenericArrayType genericArrayType = (GenericArrayType) type;
+
+ // TODO(jleitch): This is not the most efficient way to handle generic
+ // arrays, but is there another way to extract the array class in a
+ // non-hacky way (i.e. using String value class names- "[L...")?
+ Object rawArrayType = Array.newInstance(
+ getRawType(genericArrayType.getGenericComponentType()), 0);
+ return rawArrayType.getClass();
+ } else {
+ throw buildUnexpectedTypeError(
+ type, ParameterizedType.class, GenericArrayType.class);
+ }
+ }
+
+ /**
+ * Gets the raw type.
+ */
+ public Class super T> getRawType() {
+ return rawType;
+ }
+
+ /**
+ * Gets underlying {@code Type} instance.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Check if this type is assignable from the given class object.
+ */
+ public boolean isAssignableFrom(Class> cls) {
+ return isAssignableFrom((Type) cls);
+ }
+
+ /**
+ * Check if this type is assignable from the given Type.
+ */
+ public boolean isAssignableFrom(Type from) {
+ if (from == null) {
+ return false;
+ }
+
+ if (type.equals(from)) {
+ return true;
+ }
+
+ if (type instanceof Class>) {
+ return rawType.isAssignableFrom(getRawType(from));
+ } else if (type instanceof ParameterizedType) {
+ return isAssignableFrom(from, (ParameterizedType) type,
+ new HashMap());
+ } else if (type instanceof GenericArrayType) {
+ return rawType.isAssignableFrom(getRawType(from))
+ && isAssignableFrom(from, (GenericArrayType) type);
+ } else {
+ throw buildUnexpectedTypeError(
+ type, Class.class, ParameterizedType.class, GenericArrayType.class);
+ }
+ }
+
+ /**
+ * Check if this type is assignable from the given type token.
+ */
+ public boolean isAssignableFrom(TypeToken> token) {
+ return isAssignableFrom(token.getType());
+ }
+
+ /**
+ * Private helper function that performs some assignability checks for
+ * the provided GenericArrayType.
+ */
+ private static boolean isAssignableFrom(Type from, GenericArrayType to) {
+ Type toGenericComponentType = to.getGenericComponentType();
+ if (toGenericComponentType instanceof ParameterizedType) {
+ Type t = from;
+ if (from instanceof GenericArrayType) {
+ t = ((GenericArrayType) from).getGenericComponentType();
+ } else if (from instanceof Class>) {
+ Class> classType = (Class>) from;
+ while (classType.isArray()) {
+ classType = classType.getComponentType();
+ }
+ t = classType;
+ }
+ return isAssignableFrom(t, (ParameterizedType) toGenericComponentType,
+ new HashMap());
+ }
+ // No generic defined on "to"; therefore, return true and let other
+ // checks determine assignability
+ return true;
+ }
+
+ /**
+ * Private recursive helper function to actually do the type-safe checking
+ * of assignability.
+ */
+ private static boolean isAssignableFrom(Type from, ParameterizedType to,
+ Map typeVarMap) {
+
+ if (from == null) {
+ return false;
+ }
+
+ if (to.equals(from)) {
+ return true;
+ }
+
+ // First figure out the class and any type information.
+ Class> clazz = getRawType(from);
+ ParameterizedType ptype = null;
+ if (from instanceof ParameterizedType) {
+ ptype = (ParameterizedType) from;
+ }
+
+ // Load up parameterized variable info if it was parameterized.
+ if (ptype != null) {
+ Type[] tArgs = ptype.getActualTypeArguments();
+ TypeVariable>[] tParams = clazz.getTypeParameters();
+ for (int i = 0; i < tArgs.length; i++) {
+ Type arg = tArgs[i];
+ TypeVariable> var = tParams[i];
+ while (arg instanceof TypeVariable>) {
+ TypeVariable> v = (TypeVariable>) arg;
+ arg = typeVarMap.get(v.getName());
+ }
+ typeVarMap.put(var.getName(), arg);
+ }
+
+ // check if they are equivalent under our current mapping.
+ if (typeEquals(ptype, to, typeVarMap)) {
+ return true;
+ }
+ }
+
+ for (Type itype : clazz.getGenericInterfaces()) {
+ if (isAssignableFrom(itype, to, new HashMap(typeVarMap))) {
+ return true;
+ }
+ }
+
+ // Interfaces didn't work, try the superclass.
+ Type sType = clazz.getGenericSuperclass();
+ if (isAssignableFrom(sType, to, new HashMap(typeVarMap))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if two parameterized types are exactly equal, under the variable
+ * replacement described in the typeVarMap.
+ */
+ private static boolean typeEquals(ParameterizedType from,
+ ParameterizedType to, Map typeVarMap) {
+ if (from.getRawType().equals(to.getRawType())) {
+ Type[] fromArgs = from.getActualTypeArguments();
+ Type[] toArgs = to.getActualTypeArguments();
+ for (int i = 0; i < fromArgs.length; i++) {
+ if (!matches(fromArgs[i], toArgs[i], typeVarMap)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if two types are the same or are equivalent under a variable mapping
+ * given in the type map that was provided.
+ */
+ private static boolean matches(Type from, Type to,
+ Map typeMap) {
+ if (to.equals(from)) return true;
+
+ if (from instanceof TypeVariable>) {
+ return to.equals(typeMap.get(((TypeVariable>)from).getName()));
+ }
+
+ return false;
+ }
+
+ /**
+ * Hashcode for this object.
+ * @return hashcode for this object.
+ */
+ @Override public int hashCode() {
+ return type.hashCode();
+ }
+
+ /**
+ * Method to test equality.
+ *
+ * @return true if this object is logically equal to the specified object, false otherwise.
+ */
+ @Override public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof TypeToken>)) {
+ return false;
+ }
+ TypeToken> t = (TypeToken>) o;
+ return type.equals(t.type);
+ }
+
+ /**
+ * Returns a string representation of this object.
+ * @return a string representation of this object.
+ */
+ @Override public String toString() {
+ return type instanceof Class>
+ ? ((Class>) type).getName()
+ : type.toString();
+ }
+
+ private static AssertionError buildUnexpectedTypeError(
+ Type token, Class>... expected) {
+
+ // Build exception message
+ StringBuilder exceptionMessage =
+ new StringBuilder("Unexpected type. Expected one of: ");
+ for (Class> clazz : expected) {
+ exceptionMessage.append(clazz.getName()).append(", ");
+ }
+ exceptionMessage.append("but got: ").append(token.getClass().getName())
+ .append(", for type token: ").append(token.toString()).append('.');
+
+ return new AssertionError(exceptionMessage.toString());
+ }
+
+ /**
+ * Gets type token for the given {@code Type} instance.
+ */
+ public static TypeToken> get(Type type) {
+ return new SimpleTypeToken(type);
+ }
+
+ /**
+ * Gets type token for the given {@code Class} instance.
+ */
+ public static TypeToken get(Class type) {
+ return new SimpleTypeToken(type);
+ }
+
+ /**
+ * Private static class to not create more anonymous classes than
+ * necessary.
+ */
+ private static class SimpleTypeToken extends TypeToken {
+ public SimpleTypeToken(Type type) {
+ super(type);
+ }
+ }
+}
diff --git a/src/org/mcteam/ancientgates/gson/reflect/package-info.java b/src/org/mcteam/ancientgates/gson/reflect/package-info.java
new file mode 100644
index 0000000..c4c5752
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/reflect/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * This package provides utility classes for finding type information for generic types.
+ *
+ * @author Inderjeet Singh, Joel Leitch
+ */
+package org.mcteam.ancientgates.gson.reflect;
\ No newline at end of file
diff --git a/src/org/mcteam/ancientgates/gson/stream/JsonReader.java b/src/org/mcteam/ancientgates/gson/stream/JsonReader.java
new file mode 100644
index 0000000..e276efc
--- /dev/null
+++ b/src/org/mcteam/ancientgates/gson/stream/JsonReader.java
@@ -0,0 +1,1121 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mcteam.ancientgates.gson.stream;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads a JSON (RFC 4627)
+ * encoded value as a stream of tokens. This stream includes both literal
+ * values (strings, numbers, booleans, and nulls) as well as the begin and
+ * end delimiters of objects and arrays. The tokens are traversed in
+ * depth-first order, the same order that they appear in the JSON document.
+ * Within JSON objects, name/value pairs are represented by a single token.
+ *
+ *
Parsing JSON
+ * To create a recursive descent parser your own JSON streams, first create an
+ * entry point method that creates a {@code JsonReader}.
+ *
+ *
Next, create handler methods for each structure in your JSON text. You'll
+ * need a method for each object type and for each array type.
+ *
+ *
Within array handling methods, first call {@link
+ * #beginArray} to consume the array's opening bracket. Then create a
+ * while loop that accumulates values, terminating when {@link #hasNext}
+ * is false. Finally, read the array's closing bracket by calling {@link
+ * #endArray}.
+ *
Within object handling methods, first call {@link
+ * #beginObject} to consume the object's opening brace. Then create a
+ * while loop that assigns values to local variables based on their name.
+ * This loop should terminate when {@link #hasNext} is false. Finally,
+ * read the object's closing brace by calling {@link #endObject}.
+ *
+ *
When a nested object or array is encountered, delegate to the
+ * corresponding handler method.
+ *
+ *
When an unknown name is encountered, strict parsers should fail with an
+ * exception. Lenient parsers should call {@link #skipValue()} to recursively
+ * skip the value's nested tokens, which may otherwise conflict.
+ *
+ *
If a value may be null, you should first check using {@link #peek()}.
+ * Null literals can be consumed using either {@link #nextNull()} or {@link
+ * #skipValue()}.
+ *
+ *
Example
+ * Suppose we'd like to parse a stream of messages such as the following:
+ * This code implements the parser for the above structure:
{@code
+ *
+ * public List readJsonStream(InputStream in) throws IOException {
+ * JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+ * return readMessagesArray(reader);
+ * }
+ *
+ * public List readMessagesArray(JsonReader reader) throws IOException {
+ * List messages = new ArrayList();
+ *
+ * reader.beginArray();
+ * while (reader.hasNext()) {
+ * messages.add(readMessage(reader));
+ * }
+ * reader.endArray();
+ * return messages;
+ * }
+ *
+ * public Message readMessage(JsonReader reader) throws IOException {
+ * long id = -1;
+ * String text = null;
+ * User user = null;
+ * List geo = null;
+ *
+ * reader.beginObject();
+ * while (reader.hasNext()) {
+ * String name = reader.nextName();
+ * if (name.equals("id")) {
+ * id = reader.nextLong();
+ * } else if (name.equals("text")) {
+ * text = reader.nextString();
+ * } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
+ * geo = readDoublesArray(reader);
+ * } else if (name.equals("user")) {
+ * user = readUser(reader);
+ * } else {
+ * reader.skipValue();
+ * }
+ * }
+ * reader.endObject();
+ * return new Message(id, text, user, geo);
+ * }
+ *
+ * public List readDoublesArray(JsonReader reader) throws IOException {
+ * List doubles = new ArrayList();
+ *
+ * reader.beginArray();
+ * while (reader.hasNext()) {
+ * doubles.add(reader.nextDouble());
+ * }
+ * reader.endArray();
+ * return doubles;
+ * }
+ *
+ * public User readUser(JsonReader reader) throws IOException {
+ * String username = null;
+ * int followersCount = -1;
+ *
+ * reader.beginObject();
+ * while (reader.hasNext()) {
+ * String name = reader.nextName();
+ * if (name.equals("name")) {
+ * username = reader.nextString();
+ * } else if (name.equals("followers_count")) {
+ * followersCount = reader.nextInt();
+ * } else {
+ * reader.skipValue();
+ * }
+ * }
+ * reader.endObject();
+ * return new User(username, followersCount);
+ * }}
+ *
+ *
Number Handling
+ * This reader permits numeric values to be read as strings and string values to
+ * be read as numbers. For example, both elements of the JSON array {@code
+ * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
+ * This behavior is intended to prevent lossy numeric conversions: double is
+ * JavaScript's only numeric type and very large values like {@code
+ * 9007199254740993} cannot be represented exactly on that platform. To minimize
+ * precision loss, extremely large values should be written and read as strings
+ * in JSON.
+ *
+ *
Non-Execute Prefix
+ * Web servers that serve private data using JSON may be vulnerable to Cross-site
+ * request forgery attacks. In such an attack, a malicious site gains access
+ * to a private JSON file by executing it with an HTML {@code