From: Chris Jaekl Date: Fri, 12 Dec 2014 05:36:40 +0000 (-0500) Subject: Initial X-Git-Url: http://jaekl.net/gitweb/?p=frank.git;a=commitdiff_plain;h=24f097b0fe78fa44f99b9f6f2e51a8e689d86a4a Initial --- 24f097b0fe78fa44f99b9f6f2e51a8e689d86a4a diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..10926e8 --- /dev/null +++ b/COPYING @@ -0,0 +1,675 @@ + 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 b/README new file mode 100644 index 0000000..3a7f751 --- /dev/null +++ b/README @@ -0,0 +1,55 @@ +------------ +Introduction +------------ + +This is the source code for Frank, a simple Java Servlet that +will query the OC Transpo (www.octranspo.com) GPS data API. + +Frank uses parts of QuanDocs, which is released under the GPL +(GNU Public Licence version 3 or, at your option, any later +version) and, thus, Frank is also available under GPLv3-or-later. +Please see the file COPYING for the full terms of this licence. + +------------ +Dependencies +------------ + +Frank's production code depends on the following external libraries: + + - Jenkins-WinStone (LGPL, Debian package libjenkins-winstone-java) + You could substitute a different servlet engine if you modif go.sh + - Apache HttpComponents 4.3 (Apache Licence, packaged with Frank + because as of this writing Debian stable doesn't yet provide it) + +The Unit Tests (invoked via cov.sh) rely on a couple of additional +libraries: + + - JUnit 4 (Eclipse Public Licence, Debian package junit4) + the same reason as HttpComponents 4.3) + - Oracle jcov (GPLv2 + classpath exception, also packaged with + Frank because Debian doesn't yet include it) + +If you are not building on Debian Linux, then you'll need to +download WinStone and JUnit4 separately, and edit setcp.sh to +point to the correct paths. + +--------------------- +Compiling and Running +--------------------- + +To build the source code, run its unit tests, and generate +a code coverage report: + + $ . ./setcp.sh + $ ./cov.sh + $ www-browser ./report/index.html + +To build the source code and run it in the WinStone Servlet Engine: + + $ . ./setcp.sh + $ ./go.sh + +Note that the OC Transpo server will refuse to answer unless you have +a valid AppID + APIKey combination, but my APIKey is not included in this +source distribution. You'll need to get your own from octranspo.com. + diff --git a/WEB-INF/classes/frank.properties b/WEB-INF/classes/frank.properties new file mode 100644 index 0000000..e508d3f --- /dev/null +++ b/WEB-INF/classes/frank.properties @@ -0,0 +1,12 @@ +data.collected=Data collected 0m 0s ago at {0}. +destination=Destination +error.page=Error Page +eta=ETA +frank=Frank +gps.off=GPS off +gps.read=GPS Read +m=m +remain=Remain +route=Route +s=s +unexpected.error=Unexpected Error diff --git a/WEB-INF/lib/commons-codec-1.6.jar b/WEB-INF/lib/commons-codec-1.6.jar new file mode 100644 index 0000000..ee1bc49 Binary files /dev/null and b/WEB-INF/lib/commons-codec-1.6.jar differ diff --git a/WEB-INF/lib/commons-logging-1.1.3.jar b/WEB-INF/lib/commons-logging-1.1.3.jar new file mode 100644 index 0000000..ab51254 Binary files /dev/null and b/WEB-INF/lib/commons-logging-1.1.3.jar differ diff --git a/WEB-INF/lib/fluent-hc-4.3.5.jar b/WEB-INF/lib/fluent-hc-4.3.5.jar new file mode 100644 index 0000000..83fc91e Binary files /dev/null and b/WEB-INF/lib/fluent-hc-4.3.5.jar differ diff --git a/WEB-INF/lib/httpclient-4.3.5.jar b/WEB-INF/lib/httpclient-4.3.5.jar new file mode 100644 index 0000000..1db1225 Binary files /dev/null and b/WEB-INF/lib/httpclient-4.3.5.jar differ diff --git a/WEB-INF/lib/httpclient-cache-4.3.5.jar b/WEB-INF/lib/httpclient-cache-4.3.5.jar new file mode 100644 index 0000000..8f923b5 Binary files /dev/null and b/WEB-INF/lib/httpclient-cache-4.3.5.jar differ diff --git a/WEB-INF/lib/httpcore-4.3.2.jar b/WEB-INF/lib/httpcore-4.3.2.jar new file mode 100644 index 0000000..813ec23 Binary files /dev/null and b/WEB-INF/lib/httpcore-4.3.2.jar differ diff --git a/WEB-INF/lib/httpmime-4.3.5.jar b/WEB-INF/lib/httpmime-4.3.5.jar new file mode 100644 index 0000000..8a02dd2 Binary files /dev/null and b/WEB-INF/lib/httpmime-4.3.5.jar differ diff --git a/WEB-INF/lib/xml-apis.jar b/WEB-INF/lib/xml-apis.jar new file mode 100644 index 0000000..89c72d4 Binary files /dev/null and b/WEB-INF/lib/xml-apis.jar differ diff --git a/WEB-INF/web.xml b/WEB-INF/web.xml new file mode 100644 index 0000000..a6d2aef --- /dev/null +++ b/WEB-INF/web.xml @@ -0,0 +1,26 @@ + + + + Welcome to our server + Welcome to our server + + ViewSchedule + net.jaekl.frank.ViewSchedule + + + + ViewSchedule + /ViewSchedule + + + + + diff --git a/cov.sh b/cov.sh new file mode 100755 index 0000000..4404b78 --- /dev/null +++ b/cov.sh @@ -0,0 +1,47 @@ +#!/bin/bash +WEB_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +INSTR_DIR="${WEB_ROOT}/../instr" + +##################### +echo Compiling... +find "${WEB_ROOT}/prod" -name "*.java" | xargs javac +find "${WEB_ROOT}/test" -name "*.java" | xargs javac -classpath ${WEB_ROOT}/prod:${CLASSPATH} + +##################### +echo Cleaning old coverage files... +for x in "${INSTR_DIR}" report +do + if [ -d "${x}" ]; then + rm -rf "${x}" + fi + mkdir -p "${x}" +done +for x in result.xml template.xml +do + if [ -w ${x} ]; then + rm ${x} + fi +done + +##################### +echo Instrumenting... +java -classpath "${CLASSPATH}" -jar `pwd`/jcov/jcov.jar Instr -t template.xml -o "${INSTR_DIR}" -type all "${WEB_ROOT}/prod" + +##################### +echo Running unit tests... + +TESTS="" +for x in `cd ${WEB_ROOT}/test; find . -name '*Test.class'` +do + #echo CANDIDATE $x + TEST_CLASS=`echo ${x} | sed s:\^./:: | cut -d . -f 1 | sed s:/:.:g` + #echo TEST_CLASS ${TEST_CLASS} + TESTS="${TEST_CLASS} ${TESTS}" +done + +java -Djcov.template=${WEB_ROOT}/template.xml -Djcov.file=${WEB_ROOT}/result.xml -classpath "${INSTR_DIR}:${WEB_ROOT}/test:${CLASSPATH}:/usr/share/java/junit.jar:${WEB_ROOT}/jcov/jcov_file_saver.jar" org.junit.runner.JUnitCore ${TESTS} + +##################### +echo Generating HTML Report... + +java -jar "${WEB_ROOT}/jcov/jcov.jar" RepGen -sourcepath "${WEB_ROOT}/prod" -log.level FINE result.xml diff --git a/go.sh b/go.sh new file mode 100755 index 0000000..5822733 --- /dev/null +++ b/go.sh @@ -0,0 +1,8 @@ +#!/bin/bash +WEB_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +echo Compiling... +find "${WEB_ROOT}/prod" -name "*.java" | xargs javac -classpath ${WEB_ROOT}/prod:${CLASSPATH} +cp -r ${WEB_ROOT}/prod/* ${WEB_ROOT}/WEB-INF/classes/ +find "${WEB_ROOT}/prod" -name '*.class' -exec rm {} \; +echo Launching... +java -jar /usr/share/java/jenkins-winstone.jar "${WEB_ROOT}" diff --git a/index.html b/index.html new file mode 100644 index 0000000..c110dd3 --- /dev/null +++ b/index.html @@ -0,0 +1,33 @@ + + + Frank Welcome Page + + +

Welcome to Frank

+

Choose a preselected stop:

+

+ Bronson at Sunnyside, NB
+ Bronson at Sunnyside, SB
+ Sunnyside at Seneca, EB
+ Bank at Sunnyside, NB
+ Bank at Hopewell, SB
+ Riverdale at Sunnyside, NB
+ Billings Bridge Station (All Directions)
+ Carleton U (All Directions)
+ Rideau Centre (All Directions)
+

+

+ Somerset at LeBreton EB
+ Somerset at Bronson EB
+ Bronson at Somerset NB
+ Bronson at Somerset SB
+

+

Or, enter your stop number:

+

+

+ + +
+

+ + diff --git a/jcov/jcov.jar b/jcov/jcov.jar new file mode 100644 index 0000000..ff0ab73 Binary files /dev/null and b/jcov/jcov.jar differ diff --git a/jcov/jcov_file_saver.jar b/jcov/jcov_file_saver.jar new file mode 100644 index 0000000..c6f986f Binary files /dev/null and b/jcov/jcov_file_saver.jar differ diff --git a/jcov/jcov_network_saver.jar b/jcov/jcov_network_saver.jar new file mode 100644 index 0000000..ef29e13 Binary files /dev/null and b/jcov/jcov_network_saver.jar differ diff --git a/jcov/jtobserver.jar b/jcov/jtobserver.jar new file mode 100644 index 0000000..7823085 Binary files /dev/null and b/jcov/jtobserver.jar differ diff --git a/prod/frank.properties b/prod/frank.properties new file mode 100644 index 0000000..e508d3f --- /dev/null +++ b/prod/frank.properties @@ -0,0 +1,12 @@ +data.collected=Data collected 0m 0s ago at {0}. +destination=Destination +error.page=Error Page +eta=ETA +frank=Frank +gps.off=GPS off +gps.read=GPS Read +m=m +remain=Remain +route=Route +s=s +unexpected.error=Unexpected Error diff --git a/prod/net/jaekl/frank/FrankBundle.java b/prod/net/jaekl/frank/FrankBundle.java new file mode 100644 index 0000000..f0889ba --- /dev/null +++ b/prod/net/jaekl/frank/FrankBundle.java @@ -0,0 +1,61 @@ +package net.jaekl.frank; + +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; + +import net.jaekl.qd.QDBundleFactory; + +public class FrankBundle { + public static final String DATA_COLLECTED = "data.collected"; + public static final String DESTINATION = "destination"; + public static final String ERROR_PAGE = "error.page"; + public static final String ETA = "eta"; + public static final String FRANK = "frank"; + public static final String GPS_OFF = "gps.off"; + public static final String GPS_READ = "gps.read"; + public static final String MINUTES = "m"; // suffix (abbreviated) for minutes + public static final String REMAIN = "remain"; + public static final String ROUTE = "route"; + public static final String SECONDS = "s"; + public static final String UNEXPECTED_ERROR = "unexpected.error"; + + final static String BUNDLE_NAME = "frank"; + + static ConcurrentHashMap m_bundleMap = new ConcurrentHashMap(); + + ResourceBundle m_bundle; + + public static FrankBundle getInst(Locale locale) { + FrankBundle result = m_bundleMap.get(locale); + if (null == result) { + synchronized(FrankBundle.class) { + result = m_bundleMap.get(locale); + if (null == result) { + result = new FrankBundle(locale); + } + m_bundleMap.put(locale, result); + } + } + return result; + } + + private FrankBundle(Locale locale) { + m_bundle = QDBundleFactory.getInst().getBundle(BUNDLE_NAME, locale); + } + + public String get(String key) { + try { + if (null != m_bundle) { + return m_bundle.getString(key); + } + } + catch (MissingResourceException e) { + // Make it clear that something has gone wrong. + e.printStackTrace(); + // Fall through to the fallback behaviour below + } + return "[" + key + "]"; + } +} diff --git a/prod/net/jaekl/frank/FrankException.java b/prod/net/jaekl/frank/FrankException.java new file mode 100644 index 0000000..e7175fc --- /dev/null +++ b/prod/net/jaekl/frank/FrankException.java @@ -0,0 +1,11 @@ +package net.jaekl.frank; + +import java.lang.Exception; + +public class FrankException extends Exception { + private static final long serialVersionUID = 1L; + + public FrankException(Throwable cause) { + super(cause); + } +} diff --git a/prod/net/jaekl/frank/Schedule.java b/prod/net/jaekl/frank/Schedule.java new file mode 100644 index 0000000..c7fd70b --- /dev/null +++ b/prod/net/jaekl/frank/Schedule.java @@ -0,0 +1,148 @@ +package net.jaekl.frank; + +import java.io.PrintWriter; +import java.text.DateFormat; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import net.jaekl.frank.octranspo.Route; +import net.jaekl.frank.octranspo.StopInfo; +import net.jaekl.frank.octranspo.Trip; + +public class Schedule { + Locale m_locale; + FrankBundle m_bundle; + DateFormat m_hourMinFmt; + DateFormat m_hourMinSecFmt; + + public Schedule(Locale locale) { + m_locale = locale; + m_bundle = FrankBundle.getInst(locale); + m_hourMinFmt = new SimpleDateFormat("hh:mma", locale); + m_hourMinSecFmt = new SimpleDateFormat("hh:mm:ssa", locale); + } + + String trans(String key) { + return m_bundle.get(key); + } + + String mapUrl(double latitude, double longitude) { + return "http://www.openstreetmap.org/?mlat=" + latitude + "&mlon=" + longitude + "&zoom=15"; + } + + void writeStyle(PrintWriter pw) { + pw.println(""); + } + + // Countdown timer that updates time remaining until each bus is expected. + void writeScript(PrintWriter pw, String remainArray, int remainCount) { + String min = trans(FrankBundle.MINUTES); + String sec = trans(FrankBundle.SECONDS); + + pw.println(""); + + } + + void writeHeader(PrintWriter pw, String title) { + pw.println(""); + pw.println(""); + pw.println("" + title + ""); + writeStyle(pw); + pw.println(""); + } + + void writePage(PrintWriter pw, StopInfo stopInfo) + { + StringBuilder remainArray = new StringBuilder("[ "); + int remainCount = 0; + + String title = stopInfo.getDescr() + " (" + stopInfo.getStopNo() + ")"; + String min = trans(FrankBundle.MINUTES); + + writeHeader(pw, trans(FrankBundle.FRANK) + ": " + title); + + pw.println(""); + pw.println(" "); + pw.println(" "); + pw.println(" "); + + for (int routeIdx = 0; routeIdx < stopInfo.getNumRoutes(); ++routeIdx) { + Route route = stopInfo.getRoute(routeIdx); + for (int tripIdx = 0; tripIdx < route.getNumTrips(); ++tripIdx) { + Trip trip = route.getTrip(tripIdx); + boolean isGhost = ((-1) == trip.getAdjAge()); + if (isGhost) { + // GPS is off. This bus may not exist. + pw.println(" "); + } else { + pw.println(" "); + } + pw.println(" "); + pw.println(" "); + pw.println(" "); + + pw.println(" "); + if (remainCount > 0) { + remainArray.append(", "); + } + remainArray.append(trip.getAdjTime()); + remainCount++; + + if (trip.getAdjAge() < 0) { + pw.println(" "); + } + else { + pw.println(" "); + } + pw.println(" "); + } + } + + pw.println("
" + title + "
" + + trans(FrankBundle.ROUTE) + + "" + + trans(FrankBundle.DESTINATION) + + "" + + trans(FrankBundle.ETA) + + "" + + trans(FrankBundle.REMAIN) + + "" + + trans(FrankBundle.GPS_READ) + + "
" + route.getRouteNo() + "" + trip.getDest() + "" + m_hourMinFmt.format(trip.getETA()) + ""+ trip.getAdjTime()+min+"" + trans(FrankBundle.GPS_OFF) + "" + m_hourMinSecFmt.format(trip.getGPSTime()) + "
"); + + String dataCollectedFormat = trans(FrankBundle.DATA_COLLECTED); + String dataCollected = MessageFormat.format(dataCollectedFormat, m_hourMinSecFmt.format(new Date())); + pw.println("

" + dataCollected + "

"); + + remainArray.append(" ]"); + writeScript(pw, remainArray.toString(), remainCount); + pw.println(""); + } +} diff --git a/prod/net/jaekl/frank/ViewSchedule.java b/prod/net/jaekl/frank/ViewSchedule.java new file mode 100644 index 0000000..e044f70 --- /dev/null +++ b/prod/net/jaekl/frank/ViewSchedule.java @@ -0,0 +1,125 @@ +package net.jaekl.frank; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Locale; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import net.jaekl.frank.octranspo.Server; +import net.jaekl.frank.octranspo.StopInfo; +import net.jaekl.qd.util.ExceptionUtils; + +public class ViewSchedule extends HttpServlet { + private static final long serialVersionUID = 1L; + + static final String BUNDLE_NAME = "frank"; + static final String STOP = "stop"; + static final String ROUTE = "route"; + static final String LANG = "lang"; + + int getParamInt(HttpServletRequest req, String paramName) { + String valueStr = getParamString(req, paramName); + try { + return Integer.parseInt(valueStr); + } + catch (NumberFormatException exc) { + // TODO: Error page + return 0; + } + } + + String getParamString(HttpServletRequest req, String paramName) { + String valueStr = req.getParameter(paramName); + if (null == valueStr|| valueStr.equals("")) { + // TODO: Error page + return null; + } + return valueStr; + } + + Locale getLocale(HttpServletRequest req) { + Locale result = null; + String lang = getParamString(req, LANG); + if (null != lang) { + result = new Locale(lang); + } + if (null == result) { + result = Locale.getDefault(); + } + return result; + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse res) + throws ServletException, IOException + { + res.setContentType("text/html"); + PrintWriter pw = res.getWriter(); + + Locale locale = getLocale(req); + int stop = getParamInt(req, STOP); + int route = getParamInt(req, ROUTE); + + try { + FileInputStream fis = new FileInputStream("apikey.txt"); + try { + Server server = new Server("192f31d2", fis); + StopInfo stopInfo = null; + + if (0 == route) { + stopInfo = server.getNextTripsForStopAllRoutes(stop); + } + else { + stopInfo = server.getNextTripsForStop(stop, route); + } + Schedule schedule = new Schedule(locale); + + schedule.writePage(pw, stopInfo); + } + finally { + ExceptionUtils.tryClose(fis); + } + } + catch (Throwable t) { + writeErrorPage(pw, t, locale); + } + } + + void writeErrorPage(PrintWriter pw, Throwable t, Locale locale) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos); + FrankBundle bundle = FrankBundle.getInst(locale); + + pw.println("" + + bundle.get(FrankBundle.FRANK) + ": " + + bundle.get(FrankBundle.ERROR_PAGE) + + ""); + pw.println("

" + + bundle.get(FrankBundle.FRANK) + ": " + + bundle.get(FrankBundle.UNEXPECTED_ERROR) + + "

");
+
+		// Note that, if we cared about security, we would log this stack trace to a
+		// server log, and only report a cross-reference to the log file back to the 
+		// end user's browser, to avoid potentially exposing internal info that we 
+		// don't want to share.
+		// At least at this point, we don't care (that much), and trade off a 
+		// potential information leak in favour of reducing our code complexity
+		// and the administrator's workload.
+		t.printStackTrace(ps);
+		String stackTrace = baos.toString(); 
+		pw.println(stackTrace);
+				
+		pw.println("

"); + pw.println(""); + } + + +} diff --git a/prod/net/jaekl/frank/octranspo/NextTrips.java b/prod/net/jaekl/frank/octranspo/NextTrips.java new file mode 100644 index 0000000..c72a651 --- /dev/null +++ b/prod/net/jaekl/frank/octranspo/NextTrips.java @@ -0,0 +1,11 @@ +package net.jaekl.frank.octranspo; + +public class NextTrips extends StopInfo { + static final String ROOT_TAG = "GetNextTripsForStopResult"; + static final String[] INTERNAL = { STOP_NO, STOP_LABEL, ERROR, ROUTES }; + static final Object[][] EXTERNAL = { { ROUTE, Route.class} }; + + public NextTrips() { + super(ROOT_TAG, INTERNAL, EXTERNAL); + } +} diff --git a/prod/net/jaekl/frank/octranspo/Route.java b/prod/net/jaekl/frank/octranspo/Route.java new file mode 100644 index 0000000..5bd8834 --- /dev/null +++ b/prod/net/jaekl/frank/octranspo/Route.java @@ -0,0 +1,72 @@ +package net.jaekl.frank.octranspo; + +import java.util.ArrayList; +import net.jaekl.qd.xml.ParseResult; +import net.jaekl.qd.xml.XmlParseException; + +public class Route extends ParseResult { + static final String ROUTE = "Route"; + + static final String ROUTE_NO = "RouteNo"; + static final String DIRECTION_ID = "DirectionID"; + static final String DIRECTION = "Direction"; + static final String ROUTE_HEADING = "RouteHeading"; + static final String ROUTE_LABEL = "RouteLabel"; // What GetNextTripsForStop calls + static final String TRIPS = "Trips"; + + static final String TRIP = "Trip"; + + static final String[] INTERNAL = { ROUTE_NO, DIRECTION_ID, DIRECTION, ROUTE_HEADING, ROUTE_LABEL, TRIPS }; + static final Object[][] EXTERNAL = { { TRIP, Trip.class } }; + + int m_routeNo; + int m_directionID; + String m_direction; + String m_routeHeading; + ArrayList m_trips; + + public Route() { + super(ROUTE, INTERNAL, EXTERNAL); + m_routeNo = 0; + m_directionID = 0; + m_direction = ""; + m_routeHeading = ""; + m_trips = new ArrayList(); + } + + public int getRouteNo() { return m_routeNo; } + public int getDirectionID() { return m_directionID; } + public String getDirection() { return m_direction; } + public String getRouteHeading() { return m_routeHeading; } + public int getNumTrips() { return m_trips.size(); } + public Trip getTrip(int idx) { return new Trip(m_trips.get(idx)); } + + @Override + public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException + { + if (ROUTE_NO.equals(localName)) { + m_routeNo = Integer.parseInt(chars); + } + else if (DIRECTION_ID.equals(localName)) { + m_directionID = Integer.parseInt(chars); + } + else if (DIRECTION.equals(localName)) { + m_direction = chars; + } + else if (ROUTE_HEADING.equals(localName) || ROUTE_LABEL.equals(localName)) { + m_routeHeading = chars; + } + } + + @Override + public void endExternal(String uri, String localName, String qName) throws XmlParseException + { + if (localName.equals(TRIP)) { + ParseResult[] collected = collectParsedChildren(Trip.class); + for (ParseResult pr : collected) { + assert(pr instanceof Trip); + m_trips.add((Trip)pr); + } + } + } +} diff --git a/prod/net/jaekl/frank/octranspo/RouteSummary.java b/prod/net/jaekl/frank/octranspo/RouteSummary.java new file mode 100644 index 0000000..9689a77 --- /dev/null +++ b/prod/net/jaekl/frank/octranspo/RouteSummary.java @@ -0,0 +1,11 @@ +package net.jaekl.frank.octranspo; + +public class RouteSummary extends StopInfo { + static final String ROOT_TAG = "GetRouteSummaryForStopResult"; + static final String[] INTERNAL = { STOP_NO, DESCRIPTION, ERROR, ROUTES }; + static final Object[][] EXTERNAL = { { ROUTE, Route.class} }; + + public RouteSummary() { + super(ROOT_TAG, INTERNAL, EXTERNAL); + } +} diff --git a/prod/net/jaekl/frank/octranspo/Server.java b/prod/net/jaekl/frank/octranspo/Server.java new file mode 100644 index 0000000..9c77da4 --- /dev/null +++ b/prod/net/jaekl/frank/octranspo/Server.java @@ -0,0 +1,87 @@ +package net.jaekl.frank.octranspo; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.util.ArrayList; + +import net.jaekl.frank.FrankException; +import net.jaekl.qd.QDException; +import net.jaekl.qd.http.RequestBroker; + +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; + +public class Server +{ + static final String API_KEY = "apiKey"; + static final String APP_ID = "appID"; + static final String GATEWAY_URL = "https://api.octranspo1.com/v1.2/"; + static final String ROUTE_NO = "routeNo"; + static final String STOP_NO = "stopNo"; + static final String GET_ROUTE_SUMMARY_FOR_STOP = "GetRouteSummaryForStop"; + static final String GET_NEXT_TRIPS_FOR_STOP = "GetNextTripsForStop"; + static final String GET_NEXT_TRIPS_FOR_STOP_ALL_ROUTES = "GetNextTripsForStopAllRoutes"; + + RequestBroker m_broker; + ArrayList m_baseParams; + + // Constructor + // appID The appID for our application + // is InputStream from which to read the apiKey + public Server(String appID, InputStream is) throws IOException { + assert (null != appID); + assert (null != is); + + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String apiKey = br.readLine(); + + m_baseParams = new ArrayList(); + m_baseParams.add(new BasicNameValuePair(APP_ID, appID)); + m_baseParams.add(new BasicNameValuePair(API_KEY, apiKey)); + + m_broker = new RequestBroker(GATEWAY_URL, m_baseParams); + } + + public StopInfo getRouteSummaryForStop(int stopNo) + throws FrankException + { + try { + ArrayList params = new ArrayList(); + params.add(new BasicNameValuePair(STOP_NO, Integer.valueOf(stopNo).toString())); + return (StopInfo) m_broker.submitAndParse(GET_ROUTE_SUMMARY_FOR_STOP, params, RouteSummary.class); + } + catch (QDException e) { + throw new FrankException(e); + } + } + + public StopInfo getNextTripsForStop(int stopNo, int routeNo) + throws FrankException + { + try { + ArrayList params = new ArrayList(); + params.add(new BasicNameValuePair(STOP_NO, Integer.valueOf(stopNo).toString())); + params.add(new BasicNameValuePair(ROUTE_NO, Integer.valueOf(routeNo).toString())); + return (StopInfo) m_broker.submitAndParse(GET_NEXT_TRIPS_FOR_STOP, params, NextTrips.class); + } + catch (QDException e) { + throw new FrankException(e); + } + } + + public StopInfo getNextTripsForStopAllRoutes(int stopNo) + throws FrankException + { + try { + ArrayList params = new ArrayList(); + params.add(new BasicNameValuePair(STOP_NO, Integer.valueOf(stopNo).toString())); + return (StopInfo) m_broker.submitAndParse(GET_NEXT_TRIPS_FOR_STOP_ALL_ROUTES, params, RouteSummary.class); + } + catch (QDException e) { + throw new FrankException(e); + } + } +} + diff --git a/prod/net/jaekl/frank/octranspo/StopInfo.java b/prod/net/jaekl/frank/octranspo/StopInfo.java new file mode 100644 index 0000000..88bc95c --- /dev/null +++ b/prod/net/jaekl/frank/octranspo/StopInfo.java @@ -0,0 +1,76 @@ +package net.jaekl.frank.octranspo; + +import java.util.ArrayList; + +import net.jaekl.qd.xml.ParseResult; +import net.jaekl.qd.xml.XmlParseException; + +public class StopInfo extends ParseResult +{ + // potential child tag names + public static String STOP_NO = "StopNo"; + public static String DESCRIPTION = "StopDescription"; // Present in RouteSummary + public static String STOP_LABEL = "StopLabel"; // What NextTrips calls StopDescription + public static String ERROR = "Error"; + public static String ROUTES = "Routes"; + public static String ROUTE = "Route"; + + // data returned inside our element + int m_stopNo; + String m_descr; + String m_error; + ArrayList m_routes; + + // Constructor + public StopInfo(String rootTagName, String[] internal, Object[][] external) { + super(rootTagName, internal, external); + + m_stopNo = 0; + m_descr = ""; + m_error = ""; + m_routes = new ArrayList(); + } + + // ----------------------------- + // Public methods to access data + + public int getStopNo() { return m_stopNo; } + public String getDescr() { return m_descr; } + public String getError() { return m_error; } + public int getNumRoutes() { return m_routes.size(); } + public Route getRoute(int idx) { return m_routes.get(idx); } + + // -------------------------- + // ParseResult implementation + + @Override + public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException + { + assert (null != localName); + + if (localName.equals(STOP_NO)) { + m_stopNo = Integer.parseInt(chars); + } + else if (localName.equals(DESCRIPTION) || localName.equals(STOP_LABEL)) { + m_descr = chars; + } + else if (localName.equals(ERROR)) { + m_error = chars; + } + } + + @Override + public void endExternal(String uri, String localName, String qName) throws XmlParseException + { + if (localName.equals(ROUTE)) { + ParseResult[] collected = collectParsedChildren(Route.class); + for (ParseResult pr : collected) { + assert (pr instanceof Route); + m_routes.add((Route)pr); + } + } + } + + +} + diff --git a/prod/net/jaekl/frank/octranspo/Trip.java b/prod/net/jaekl/frank/octranspo/Trip.java new file mode 100644 index 0000000..7dfbb5d --- /dev/null +++ b/prod/net/jaekl/frank/octranspo/Trip.java @@ -0,0 +1,140 @@ +package net.jaekl.frank.octranspo; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import net.jaekl.qd.util.ParseUtils; +import net.jaekl.qd.xml.ParseResult; +import net.jaekl.qd.xml.XmlParseException; + +public class Trip extends ParseResult { + static final String TRIP = "Trip"; + + static final String TRIP_DESTINATION = "TripDestination"; + static final String TRIP_START_TIME = "TripStartTime"; + static final String ADJUSTED_SCHEDULE_TIME = "AdjustedScheduleTime"; + static final String ADJUSTMENT_AGE = "AdjustmentAge"; + static final String LAST_TRIP_OF_SCHEDULE = "LastTripOfSchedule"; + static final String BUS_TYPE = "BusType"; + static final String GPS_SPEED = "GPSSpeed"; + static final String LATITUDE = "Latitude"; + static final String LONGITUDE = "Longitude"; + + static final String[] INTERNAL = { TRIP_DESTINATION, + TRIP_START_TIME, + ADJUSTED_SCHEDULE_TIME, + ADJUSTMENT_AGE, + LAST_TRIP_OF_SCHEDULE, + BUS_TYPE, + GPS_SPEED, + LATITUDE, + LONGITUDE }; + static final Object[][] EXTERNAL = {}; + + String m_dest; // destination + Date m_start; // time at which the trip started / is scheduled to start + int m_adjTime; // minutes until bus is predicted to arrive + double m_adjAge; // time since the last GPS data was received, in minutes (possibly fractional) + boolean m_lastTrip; // is this the last scheduled trip of the day? + String m_busType; // type of bus + double m_speed; // speed (km/h) when last polled + double m_long; // longitude + double m_lat; // latitude + + DateFormat m_dateFormat; + Date m_constructed; // DateTime when this object was constructed + + public Trip() { + super(TRIP, INTERNAL, EXTERNAL); + m_dest = ""; + m_start = new Date(); + m_adjTime = 0; + m_adjAge = 0.0; + m_lastTrip = false; + m_busType = ""; + m_speed = 0.0; + m_long = 0.0; + m_lat = 0.0; + + m_dateFormat = new SimpleDateFormat("hh:mm"); + m_constructed = new Date(); + } + + public Trip(Trip other) { + super(TRIP, INTERNAL, EXTERNAL); + m_dest = other.m_dest; + m_start = other.m_start; + m_adjTime = other.m_adjTime; + m_adjAge = other.m_adjAge; + m_lastTrip = other.m_lastTrip; + m_busType = other.m_busType; + m_speed = other.m_speed; + m_long = other.m_long; + m_lat = other.m_lat; + m_constructed = other.m_constructed; + } + + public String getDest() { return m_dest; } + public Date getStart() { return m_start; } + public int getAdjTime() { return m_adjTime; } + public double getAdjAge() { return m_adjAge; } + public boolean isLastTrip() { return m_lastTrip; } + public String getBusType() { return m_busType; } + public double getSpeed() { return m_speed; } + public double getLongitude() { return m_long; } + public double getLatitude() { return m_lat; } + + // Estimated (Date)Time of Arrival of this trip at the stop + public Date getETA() { return new Date(m_constructed.getTime() + (long)(1000 * 60 * m_adjTime)); } + + // (Date)Time when the GPS for this bus was last read + public Date getGPSTime() { return new Date(m_constructed.getTime() - (long)(1000 * 60 * m_adjAge)); } + + // --------------------------- + // ParseResult implementation: + + @Override + public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException + { + try { + if (TRIP_DESTINATION.equals(localName)) { + m_dest = chars; + } + else if (TRIP_START_TIME.equals(localName)) { + m_start = m_dateFormat.parse(chars); + } + else if (ADJUSTED_SCHEDULE_TIME.equals(localName)) { + m_adjTime = ParseUtils.parseInt(chars); + } + else if (ADJUSTMENT_AGE.equals(localName)) { + m_adjAge = ParseUtils.parseDouble(chars); + } + else if (LAST_TRIP_OF_SCHEDULE.equals(localName)) { + m_lastTrip = Boolean.parseBoolean(chars); + } + else if (BUS_TYPE.equals(localName)) { + m_busType = chars; + } + else if (GPS_SPEED.equals(localName)) { + m_speed = ParseUtils.parseDouble(chars); + } + else if (LONGITUDE.equals(localName)) { + m_long = ParseUtils.parseDouble(chars); + } + else if (LATITUDE.equals(localName)) { + m_lat = ParseUtils.parseDouble(chars); + } + } catch (ParseException pe) { + throw new XmlParseException(pe); + } + } + + @Override + public void endExternal(String uri, String localName, String qName) throws XmlParseException + { + // no externally-parsed children + } +} + diff --git a/prod/net/jaekl/qd/QDBundleFactory.java b/prod/net/jaekl/qd/QDBundleFactory.java new file mode 100644 index 0000000..e53c878 --- /dev/null +++ b/prod/net/jaekl/qd/QDBundleFactory.java @@ -0,0 +1,40 @@ +// Copyright (C) 2004, 2014 Christian Jaekl + +// Central spot from which to access ResourceBundles. +// This made more sense with earlier versions of Java, where the specification did not +// guarantee that ResourceBundles would be cached. Java 7 and later cache by default, +// but it still seems prudent to centralize accesses to resources here so that we have +// control in case we want to implement our own cache, or override certain behaviours. +// +// Note that we rely on the JVM's caching, to avoid unnecessary overhead. +// See http://java2go.blogspot.ca/2010/03/dont-be-smart-never-implement-resource.html + +package net.jaekl.qd; + +import java.util.Locale; +import java.util.ResourceBundle; + +public class QDBundleFactory { + static volatile QDBundleFactory m_inst; // singleton instance + + private QDBundleFactory() { + // no-op + } + + public static QDBundleFactory getInst() { + QDBundleFactory result = m_inst; + if (null == result) { + synchronized(QDBundleFactory.class) { + if (null == m_inst) { + m_inst = new QDBundleFactory(); + } + result = m_inst; + } + } + return result; + } + + public ResourceBundle getBundle(String baseName, Locale locale) { + return ResourceBundle.getBundle(baseName, locale); + } +} diff --git a/prod/net/jaekl/qd/QDException.java b/prod/net/jaekl/qd/QDException.java new file mode 100644 index 0000000..f5478c1 --- /dev/null +++ b/prod/net/jaekl/qd/QDException.java @@ -0,0 +1,14 @@ +package net.jaekl.qd; + +public class QDException extends Exception +{ + private static final long serialVersionUID = 1L; + + public QDException() { + super(); + } + + public QDException(Throwable t) { + super(t); + } +} diff --git a/prod/net/jaekl/qd/http/RequestBroker.java b/prod/net/jaekl/qd/http/RequestBroker.java new file mode 100644 index 0000000..1a4ba4c --- /dev/null +++ b/prod/net/jaekl/qd/http/RequestBroker.java @@ -0,0 +1,152 @@ +// Copyright (C) 2004, 2014 Christian Jaekl + +package net.jaekl.qd.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; + +import net.jaekl.qd.QDException; +import net.jaekl.qd.util.ExceptionUtils; +import net.jaekl.qd.xml.ParseHandler; +import net.jaekl.qd.xml.ParseResult; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.HttpClientBuilder; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +public class RequestBroker +{ + final String UTF_8 = "UTF-8"; + final int TIMEOUT_MSEC = 5000; // Allow at most 5 seconds before we declare the server to be unresponsive + + String m_gatewayUrl; + ArrayList m_baseParams; + + public RequestBroker(String gatewayUrl, ArrayList baseParams) + { + m_gatewayUrl = gatewayUrl; + m_baseParams = new ArrayList(baseParams); + } + + // NB: Caller is responsible for close()ing the returned InputStream + // + InputStream doSubmit(String method, ArrayList passedParams) throws QDException + { + ArrayList params = new ArrayList(m_baseParams); + params.addAll(passedParams); + + try { + RequestConfig.Builder requestBuilder = RequestConfig.custom(); + requestBuilder = requestBuilder.setConnectTimeout(TIMEOUT_MSEC); + requestBuilder = requestBuilder.setSocketTimeout(TIMEOUT_MSEC); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setDefaultRequestConfig(requestBuilder.build()); + HttpClient httpClient = builder.build(); + HttpPost httpPost = new HttpPost(m_gatewayUrl + "/" + method + "/"); + httpPost.setEntity(new UrlEncodedFormEntity(params, UTF_8)); + HttpResponse response = httpClient.execute(httpPost); + HttpEntity entity = response.getEntity(); + + if (null != entity) { + InputStream is = entity.getContent(); + return is; + } + } + catch (UnsupportedEncodingException uee) { + // We should actually be guaranteed that this never happens, + // because all JVMs are required to support UTF-8 + assert(false); + throw new Error(uee); + } + catch (IOException ioe) { + throw new QDException(ioe); + } + + return null; + } + + public String submit(String method, ArrayList passedParams) throws QDException + { + StringBuilder sb = new StringBuilder(); + InputStream is = null; + + try { + is = doSubmit(method, passedParams); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String line = br.readLine(); + while (null != line) { + sb.append(line).append("\n"); + line = br.readLine(); + } + } + catch (IOException ioe) { + throw new QDException(ioe); + } + finally { + ExceptionUtils.tryClose(is); + } + + return sb.toString(); + } + + public ParseResult submitAndParse(String method, + ArrayList passedParams, + Class rootParserClass) + throws QDException + { + return submitAndParse(method, passedParams, rootParserClass, null); + } + + public ParseResult submitAndParse(String method, + ArrayList passedParams, + Class rootParserClass, + String rootTagName) + throws QDException + { + ParseResult result = null; + InputStream is = null; + + try { + if (null == rootTagName) { + result = (ParseResult) rootParserClass.newInstance(); + } + else { + result = (ParseResult) rootParserClass.getDeclaredConstructor(String.class).newInstance(rootTagName); + } + is = doSubmit(method, passedParams); + XMLReader reader = XMLReaderFactory.createXMLReader(); + ParseHandler ph = new ParseHandler(result); + reader.setContentHandler(ph); + reader.parse(new InputSource(is)); + } + catch ( InstantiationException + | InvocationTargetException + | IllegalAccessException + | IOException + | NoSuchMethodException + | SAXException + e ) + { + throw new QDException(e); + } + finally { + ExceptionUtils.tryClose(is); + } + + return result; + } +} diff --git a/prod/net/jaekl/qd/util/ExceptionUtils.java b/prod/net/jaekl/qd/util/ExceptionUtils.java new file mode 100644 index 0000000..5e74948 --- /dev/null +++ b/prod/net/jaekl/qd/util/ExceptionUtils.java @@ -0,0 +1,19 @@ +package net.jaekl.qd.util; + +import java.io.Closeable; +import java.io.IOException; + +import net.jaekl.qd.QDException; + +public class ExceptionUtils { + public static void tryClose(Closeable closeable) throws QDException { + try { + if (null != closeable) { + closeable.close(); + } + } + catch (IOException ioe) { + throw new QDException(ioe); + } + } +} diff --git a/prod/net/jaekl/qd/util/ParseUtils.java b/prod/net/jaekl/qd/util/ParseUtils.java new file mode 100644 index 0000000..ad663c0 --- /dev/null +++ b/prod/net/jaekl/qd/util/ParseUtils.java @@ -0,0 +1,30 @@ +package net.jaekl.qd.util; + +public class ParseUtils { + // Attempt to parse the string as a double. + // Treat errors as a zero value. + public static double parseDouble(String string) { + if ((null == string) || ("".equals(string))) { + return 0.0; + } + try { + return Double.parseDouble(string); + } + catch (NumberFormatException exc) { + return 0.0; + } + } + + public static int parseInt(String string) { + if ((null == string) || ("".equals(string))) { + return 0; + } + try { + return Integer.parseInt(string); + } + catch (NumberFormatException exc) { + return 0; + } + + } +} diff --git a/prod/net/jaekl/qd/util/StringUtils.java b/prod/net/jaekl/qd/util/StringUtils.java new file mode 100644 index 0000000..5d32728 --- /dev/null +++ b/prod/net/jaekl/qd/util/StringUtils.java @@ -0,0 +1,10 @@ +package net.jaekl.qd.util; + +public class StringUtils { + public static boolean areEqual(String a, String b) { + if (null == a) { + return (null == b); + } + return a.equals(b); + } +} diff --git a/prod/net/jaekl/qd/xml/MismatchedTagsException.java b/prod/net/jaekl/qd/xml/MismatchedTagsException.java new file mode 100644 index 0000000..9c8b69c --- /dev/null +++ b/prod/net/jaekl/qd/xml/MismatchedTagsException.java @@ -0,0 +1,21 @@ +// Copyright (C) 2004, 2014 Christian Jaekl + +package net.jaekl.qd.xml; + + +public class MismatchedTagsException extends XmlParseException +{ + private static final long serialVersionUID = 1L; + + String m_expected; + String m_actual; + + public MismatchedTagsException(String expected, String actual) { + super(); + m_expected = expected; + m_actual = actual; + } + + public String getExpected() { return m_expected; } + public String getActual() { return m_actual; } +} diff --git a/prod/net/jaekl/qd/xml/MissingInfoException.java b/prod/net/jaekl/qd/xml/MissingInfoException.java new file mode 100644 index 0000000..1def37f --- /dev/null +++ b/prod/net/jaekl/qd/xml/MissingInfoException.java @@ -0,0 +1,47 @@ +// Copyright (C) 2004, 2014 Christian Jaekl + +package net.jaekl.qd.xml; + +import java.util.ArrayList; + +public class MissingInfoException extends XmlParseException +{ + private static final long serialVersionUID = 1L; + + String m_tagName; + ArrayList m_missingAttributes; + ArrayList m_missingChildTags; + + public MissingInfoException(String tagName) { + super(); + m_tagName = tagName; + m_missingAttributes = new ArrayList(); + m_missingChildTags = new ArrayList(); + } + + public void addMissingAttribute(String name) { + m_missingAttributes.add(name); + } + + public void addMissingChild(String name) { + m_missingChildTags.add(name); + } + + public String getTagName() { return m_tagName; } + + @Override + public String getMessage() { + StringBuilder sb = new StringBuilder(); + + sb.append("Tag: \"" + getTagName() + "\""); + + for (String attr : m_missingAttributes) { + sb.append("\n Attribute: \"" + attr + "\""); + } + + for (String child : m_missingChildTags) { + sb.append("\n Child tag: \"" + child + "\""); + } + return sb.toString(); + } +} diff --git a/prod/net/jaekl/qd/xml/ParseHandler.java b/prod/net/jaekl/qd/xml/ParseHandler.java new file mode 100644 index 0000000..4a921ad --- /dev/null +++ b/prod/net/jaekl/qd/xml/ParseHandler.java @@ -0,0 +1,123 @@ +// Copyright (C) 2004, 2014 Christian Jaekl + +package net.jaekl.qd.xml; + +import java.util.Stack; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; + +public class ParseHandler implements ContentHandler +{ + Stack m_stack; + + public ParseHandler(ParseResult root) { + m_stack = new Stack(); + m_stack.push(root); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException + { + if (m_stack.isEmpty()) { + return; + } + + try { + m_stack.peek().characters(ch, start, length); + } + catch (XmlParseException xpe) { + throw new SAXException(xpe); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException + { + try { + if (m_stack.isEmpty()) { + return; + } + + boolean pop = m_stack.peek().endElement(uri, localName, qName); + if (pop) { + m_stack.pop(); + + if (m_stack.isEmpty()) { + return; + } + + m_stack.peek().endExternal(uri, localName, qName); + } + } + catch (XmlParseException xpe) { + throw new SAXException(xpe); + } + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException + { + try { + ParseResult current = m_stack.peek(); + ParseResult next = current.startElement(uri, localName, qName, attributes); + if (next != current) { + m_stack.push(next); + } + } + catch (XmlParseException xpe) { + throw new SAXException(xpe); + } + } + + @Override + public void endDocument() throws SAXException { + if (! m_stack.isEmpty()) { + String missingTag = m_stack.peek().getTagName(); + throw new SAXException(new MissingInfoException(missingTag)); + } + } + + @Override + public void endPrefixMapping(String prefix) throws SAXException { + // no-op + } + + @Override + public void ignorableWhitespace(char[] ch, int start, int length) + throws SAXException + { + // no-op + } + + @Override + public void processingInstruction(String target, String data) + throws SAXException + { + // no-op + } + + @Override + public void setDocumentLocator(Locator locator) { + // no-op + } + + @Override + public void skippedEntity(String name) throws SAXException { + // no-op + } + + @Override + public void startDocument() throws SAXException { + // no-op + } + + @Override + public void startPrefixMapping(String prefix, String uri) + throws SAXException + { + // no-op + } +} diff --git a/prod/net/jaekl/qd/xml/ParseResult.java b/prod/net/jaekl/qd/xml/ParseResult.java new file mode 100644 index 0000000..2baa374 --- /dev/null +++ b/prod/net/jaekl/qd/xml/ParseResult.java @@ -0,0 +1,166 @@ +// Copyright (C) 2004, 2014 Christian Jaekl + +// Abstract class representing the result of parsing an XML Element. +// A class derived from this one will know how to parse a subtree inside an XML file, and +// will contain the result of that parse within itself when the parse has completed. +// +// Note that this code will need to be augmented and fixed if XML namespace support is desired. + +package net.jaekl.qd.xml; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Stack; + +import org.xml.sax.Attributes; + +public abstract class ParseResult +{ + Stack m_current; // Name of the element that we're currently inside + StringBuilder m_chars; // character content of m_current.peek() + ArrayList m_childParsers; // Set of all child parsers + boolean m_haveSeenMyTag; // Have I encountered my own (root) tag yet? + + String m_tagName; // Name of the (root) element tag that I'm parsing + HashSet m_internal; // Tags that we will store as members of this class instance + HashMap> m_external; // Tags that we will store as child ParseResult-derived objects + + @SuppressWarnings("unchecked") + public ParseResult(String tagName, String[] internalMemberTags, Object[][] externalParserTags) + { + m_current = new Stack(); + m_chars = new StringBuilder(); + m_childParsers = new ArrayList(); + m_haveSeenMyTag = false; + + m_tagName = tagName; + m_internal = new HashSet(); + m_external = new HashMap>(); + + for (String internalTag : internalMemberTags) { + m_internal.add(internalTag); + } + + for (int idx = 0; idx < externalParserTags.length; ++idx) { + String externalTag = (String)externalParserTags[idx][0]; + Class parserClass = (Class)externalParserTags[idx][1]; + m_external.put(externalTag, parserClass); + } + } + + public abstract void endContents(String uri, String localName, String qName, String chars) throws XmlParseException; + public abstract void endExternal(String uri, String localName, String qName) throws XmlParseException; + + public String getTagName() { return m_tagName; } + public boolean haveSeenMyTag() { return m_haveSeenMyTag; } + + public void characters(char[] ch, int start, int length) throws XmlParseException + { + m_chars.append(ch, start, length); + } + + protected ParseResult[] collectParsedChildren(Class cls) { + ArrayList collection = new ArrayList(); + Iterator iter = m_childParsers.iterator(); + while (iter.hasNext()) { + ParseResult pr = iter.next(); + if (pr.getClass().isAssignableFrom(cls)) { + collection.add(pr); + iter.remove(); + } + } + + ParseResult[] result = new ParseResult[collection.size()]; + return collection.toArray(result); + } + + // returns true if this ParseResult's context has ended with this endElement() call + public boolean endElement(String uri, String localName, String qName) throws XmlParseException + { + assert (null != localName); + + boolean isInternal = m_internal.contains(localName); + + if (! m_haveSeenMyTag) { + // We're in some unrecognised prologue. Ignore it and move on. + return false; + } + + if (m_tagName.equals(localName)) { + validate(); + return true; + } + + if (!isInternal) { + // Unrecognized tag. Ignore it. + return false; + } + + String tag = m_current.pop(); + if ( ! tag.equals(localName) ) { + throw new MismatchedTagsException(tag, localName); + } + + String chars = m_chars.toString(); + endContents(uri, localName, qName, chars); + + return false; + } + + // returns either itself, or a new ParseResult-derived object, whichever should handle parsing the inside of this element + public ParseResult startElement(String uri, String localName, String qName, Attributes attributes) + throws XmlParseException + { + assert (null != localName); + + m_chars.setLength(0); + + if (! m_haveSeenMyTag) { + // Have we opened our own (root) tag yet? + + if (m_tagName.equals(localName)) { + m_haveSeenMyTag = true; + return this; + } + else { + // One of two things has happened here. + // Either (a) we've got some sort of wrapper here, and have not yet reach our own tag, + // or (b) we're parsing XML that doesn't match expectations. + // In either case, we're going to ignore this tag, and scan forward looking for our own root. + return this; + } + } + + if (m_internal.contains(localName)) { + m_current.push(localName); + return this; + } + + Class parserClass = m_external.get(localName); + if (null != parserClass) { + try { + ParseResult childParser = (ParseResult) parserClass.newInstance(); + m_childParsers.add(childParser); + return childParser.startElement(uri, localName, qName, attributes); + } + catch (IllegalAccessException iae) { + throw new XmlParseException(iae); + } + catch (InstantiationException ie) { + throw new XmlParseException(ie); + } + } + + // Not a recognized tag. Ignore it, rather than complaining. + return this; + } + + public void validate() throws XmlParseException + { + // Default implementation is a no-op. + // Override if you want to validate on endElement() + } +} + diff --git a/prod/net/jaekl/qd/xml/XmlParseException.java b/prod/net/jaekl/qd/xml/XmlParseException.java new file mode 100644 index 0000000..ada3fe5 --- /dev/null +++ b/prod/net/jaekl/qd/xml/XmlParseException.java @@ -0,0 +1,17 @@ +package net.jaekl.qd.xml; + +import net.jaekl.qd.QDException; + +public class XmlParseException extends QDException +{ + private static final long serialVersionUID = 1L; + + public XmlParseException() { + // no-op + } + + public XmlParseException(Throwable t) { + super(t); + } + +} diff --git a/report/allclasses-frame.html b/report/allclasses-frame.html new file mode 100644 index 0000000..8f1c1f6 --- /dev/null +++ b/report/allclasses-frame.html @@ -0,0 +1,36 @@ + + +coverage report + + + +All classes + + + + +
+ExceptionUtils  50% (1/2)
+FrankBundle  100% (4/4)
+FrankException  0% (0/1)
+MismatchedTagsException  0% (0/3)
+MissingInfoException  100% (5/5)
+NextTrips  100% (2/2)
+ParseHandler  75% (9/12)
+ParseResult  89% (8/9)
+ParseUtils  67% (2/3)
+QDBundleFactory  100% (3/3)
+QDException  100% (2/2)
+RequestBroker  83% (5/6)
+Route  100% (10/10)
+RouteSummary  100% (2/2)
+Schedule  100% (7/7)
+Server  100% (5/5)
+StopInfo  100% (9/9)
+StringUtils  50% (1/2)
+Trip  94% (15/16)
+ViewSchedule  67% (4/6)
+XmlParseException  50% (1/2)
+
+ + diff --git a/report/index.html b/report/index.html new file mode 100644 index 0000000..30f922f --- /dev/null +++ b/report/index.html @@ -0,0 +1,62 @@ + + +Coverage report + + + + + + + + + +This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. +<BR> +Link to<A HREF="overview-summary.html">Non-frame version.</A> + + + diff --git a/report/net/jaekl/frank/FrankBundle.html b/report/net/jaekl/frank/FrankBundle.html new file mode 100644 index 0000000..d44f652 --- /dev/null +++ b/report/net/jaekl/frank/FrankBundle.html @@ -0,0 +1,423 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.FrankBundle
100%(4/4)
69%(9/13)
67%(4/6)
79%(15/19)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<clinit>[static]void <clinit>()
3<init>[private]void <init>(java.util.Locale)
34get[public]java.lang.String get(java.lang.String)
11getInst[public, static]net.jaekl.frank.FrankBundle getInst(java.util.Locale)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank;
 2 
 
 3 
 import java.util.Locale;
 4 
 import java.util.MissingResourceException;
 5 
 import java.util.ResourceBundle;
 6 
 import java.util.concurrent.ConcurrentHashMap;
 7 
 
 8 
 import net.jaekl.qd.QDBundleFactory;
 9 
 
 10 
 public class FrankBundle {
 11 
 	public static final String DATA_COLLECTED = "data.collected";
 12 
 	public static final String DESTINATION = "destination";
 13 
 	public static final String ERROR_PAGE = "error.page";
 14 
 	public static final String ETA = "eta";
 15 
 	public static final String FRANK = "frank";
 16 
 	public static final String GPS_OFF = "gps.off";
 17 
 	public static final String GPS_READ = "gps.read";
 18 
 	public static final String MINUTES = "m";	// suffix (abbreviated) for minutes
 19 
 	public static final String REMAIN = "remain";
 20 
 	public static final String ROUTE = "route";
 21 
 	public static final String SECONDS = "s";
 22 
 	public static final String UNEXPECTED_ERROR = "unexpected.error";
 23 
 	
 24 
 	final static String BUNDLE_NAME = "frank";
 25 
 	
 26 Block: 1/1 
 	static ConcurrentHashMap<Locale, FrankBundle> m_bundleMap = new ConcurrentHashMap<Locale, FrankBundle>();
 27 
 	
 28 
 	ResourceBundle m_bundle;
 29 
 	
 30 
 	public static FrankBundle getInst(Locale locale) {
 31 Block: 1/1 
 		FrankBundle result = m_bundleMap.get(locale);
 32 
 		if (null == result) {
 33 Block: 1/1 Branch: 1/1 
 			synchronized(FrankBundle.class) {
 34 
 				result = m_bundleMap.get(locale);
 35 
 				if (null == result) {
 36 Block: 1/1 Branch: 1/1 
 					result = new FrankBundle(locale); 
 37 
 				}
 38 Block: 1/1 Branch: 0/1 
 				m_bundleMap.put(locale, result);
 39 
 			}
 40 
 		}
 41 Block: 1/1 Branch: 1/1 
 		return result;
 42 
 	}
 43 
 	
 44 Block: 1/1 
 	private FrankBundle(Locale locale) {
 45 
 		m_bundle = QDBundleFactory.getInst().getBundle(BUNDLE_NAME, locale); 
 46 
 	}
 47 
 	
 48 
 	public String get(String key) {
 49 
 		try {
 50 Block: 1/1 
 			if (null != m_bundle) {
 51 Block: 1/1 Branch: 1/1 
 				return m_bundle.getString(key);
 52 
 			}
 53 
 		}
 54 Block: 0/1 
 		catch (MissingResourceException e) {
 55 
 			// Make it clear that something has gone wrong.
 56 
 			e.printStackTrace();	
 57 
 			// Fall through to the fallback behaviour below
 58 Block: 0/1 Branch: 0/1 
 		}
 59 Block: 0/1 
 		return "[" + key + "]";
 60 
 	}
 61 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/FrankException.html b/report/net/jaekl/frank/FrankException.html new file mode 100644 index 0000000..6bf8d52 --- /dev/null +++ b/report/net/jaekl/frank/FrankException.html @@ -0,0 +1,155 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.FrankException
0%(0/1)
0%(0/1)
-%
0%(0/2)
+

+

+ + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
0<init>[public]void <init>(java.lang.Throwable)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank;
 2 
 
 3 
 import java.lang.Exception;
 4 
 
 5 
 public class FrankException extends Exception {
 6 
 	private static final long serialVersionUID = 1L;
 7 
 
 8 
 	public FrankException(Throwable cause) {
 9 Block: 0/1 
 		super(cause);
 10 
 	}
 11 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/Schedule.html b/report/net/jaekl/frank/Schedule.html new file mode 100644 index 0000000..3e971d1 --- /dev/null +++ b/report/net/jaekl/frank/Schedule.html @@ -0,0 +1,876 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.Schedule
100%(7/7)
88%(21/24)
75%(9/12)
98%(79/81)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
8<init>[public]void <init>(java.util.Locale)
6mapUrl[]java.lang.String mapUrl(double,double)
22trans[]java.lang.String trans(java.lang.String)
11writeHeader[]void writeHeader(java.io.PrintWriter,java.lang.String)
2writePage[]void writePage(java.io.PrintWriter,net.jaekl.frank.octranspo.StopInfo)
2writeScript[]void writeScript(java.io.PrintWriter,java.lang.String,int)
14writeStyle[]void writeStyle(java.io.PrintWriter)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank;
 2 
 
 3 
 import java.io.PrintWriter;
 4 
 import java.text.DateFormat;
 5 
 import java.text.MessageFormat;
 6 
 import java.text.SimpleDateFormat;
 7 
 import java.util.Date;
 8 
 import java.util.Locale;
 9 
 
 10 
 import net.jaekl.frank.octranspo.Route;
 11 
 import net.jaekl.frank.octranspo.StopInfo;
 12 
 import net.jaekl.frank.octranspo.Trip;
 13 
 
 14 
 public class Schedule {
 15 
 	Locale m_locale;
 16 
 	FrankBundle m_bundle;
 17 
 	DateFormat m_hourMinFmt;
 18 
 	DateFormat m_hourMinSecFmt;
 19 
 	
 20 Block: 1/1 
 	public Schedule(Locale locale) {
 21 
 		m_locale = locale;
 22 
 		m_bundle = FrankBundle.getInst(locale);
 23 
 		m_hourMinFmt = new SimpleDateFormat("hh:mma", locale);
 24 
 		m_hourMinSecFmt = new SimpleDateFormat("hh:mm:ssa", locale);
 25 
 	}
 26 
 	
 27 
 	String trans(String key) {
 28 Block: 1/1 
 		return m_bundle.get(key);
 29 
 	}
 30 
 	
 31 
 	String mapUrl(double latitude, double longitude) {
 32 Block: 1/1 
 		return "http://www.openstreetmap.org/?mlat=" + latitude + "&mlon=" + longitude + "&zoom=15";
 33 
 	}
 34 
 	
 35 
 	void writeStyle(PrintWriter pw) {
 36 Block: 1/1 
 		pw.println("<STYLE>");
 37 
 		pw.println("  body {background-color: #F0F0C0; font-size: 1.5em; }");
 38 
 		pw.println("  #trips {border-collapse: collapse; font-size: 1.5em; }");
 39 
 		pw.println("  #trips td, #trips th {border: 1px solid #600000; padding: 3px 3px 3px 3px; text-align: center;}");
 40 
 		pw.println("  #trips th {background-color: #800000; color: #FFFFFF; }");
 41 
 		pw.println("  #trips tr.ghost td {background-color: #C0C0C0;}");
 42 
 		pw.println("</STYLE>");
 43 
 	}
 44 
 	
 45 
 	// Countdown timer that updates time remaining until each bus is expected.
 46 
 	void writeScript(PrintWriter pw, String remainArray, int remainCount) {
 47 Block: 1/1 
 		String min = trans(FrankBundle.MINUTES);
 48 
 		String sec = trans(FrankBundle.SECONDS);
 49 
 		
 50 
 		pw.println("<SCRIPT>");
 51 
 	    pw.println("var start; var interval; var remain;");
 52 
 	    pw.println("window.onload = function() { start = new Date(); interval = setInterval(\"Tick()\", 1000); remain = " + remainArray + "; }");
 53 
 	    pw.println("function Tick() { ");
 54 
 	    pw.println("  var now = new Date(); var delta = Math.floor((now.getTime() - start.getTime()) / 1000); ");
 55 
 	    pw.println("  var secs = delta % 60; var mins = Math.floor(delta / 60);");
 56 
 	    pw.println("  document.getElementById(\"elapsed\").innerHTML = mins + \"" + min + " \" + secs + \"" + sec + "\"");
 57 
 	    pw.println("  for (var i = 0; i < " + remainCount + "; ++i) {");
 58 
 	    pw.println("    var id = \"in_\" + i;");
 59 
 	    pw.println("    var element = document.getElementById(id);");
 60 
 	    pw.println("    var remain_delta = (remain[i] * 60) + 30 - delta;");
 61 
 	    pw.println("    if (remain_delta <= 0) {");
 62 
 	    pw.println("      element.innerHTML = \"----\"");
 63 
 	    pw.println("    } else {");
 64 
 	    pw.println("      element.innerHTML = (Math.floor(remain_delta / 60)) + \"" + trans(min) + "\"");
 65 
 	    pw.println("    }");
 66 
 	    pw.println("  }");
 67 
 	    pw.println("}");
 68 
 	    pw.println("</SCRIPT>");
 69 
 
 70 
 	}
 71 
 	
 72 
 	void writeHeader(PrintWriter pw, String title) {
 73 Block: 1/1 
 		pw.println("<HTML>");
 74 
 		pw.println("<HEAD>");
 75 
 		pw.println("<TITLE>" + title + "</TITLE>");
 76 
 		writeStyle(pw);
 77 
 		pw.println("</HEAD>");
 78 
 	}
 79 
 	
 80 
 	void writePage(PrintWriter pw, StopInfo stopInfo)
 81 
 	{
 82 Block: 1/1 
 		StringBuilder remainArray = new StringBuilder("[ ");
 83 
 		int           remainCount = 0;
 84 
 		
 85 
 		String title = stopInfo.getDescr() + " (" + stopInfo.getStopNo() + ")";
 86 
 		String min = trans(FrankBundle.MINUTES);
 87 
 		
 88 
 		writeHeader(pw, trans(FrankBundle.FRANK) + ": " + title);
 89 
 		
 90 
 		pw.println("<BODY>");
 91 
 		pw.println("  <TABLE ID=\"trips\">");
 92 
 		pw.println("    <TR><TH COLSPAN=\ř\">" + title + "</TH></TR>");
 93 
 		pw.println("    <TR><TH>" +
 94 
 		        trans(FrankBundle.ROUTE) + 
 95 
 				"</TH><TH>" +
 96 
 				trans(FrankBundle.DESTINATION) +
 97 
 				"</TH><TH>" +
 98 
 				trans(FrankBundle.ETA) +
 99 
 				"</TH><TH>" +
 100 
 				trans(FrankBundle.REMAIN) +  
 101 
 				"</TH><TH>" +
 102 
 				trans(FrankBundle.GPS_READ) +
 103 
 				"</TH>");
 104 
 		
 105 Block: 1/1 Branch: 1/1 
 		for (int routeIdx = 0; routeIdx < stopInfo.getNumRoutes(); ++routeIdx) {
 106 Block: 1/1 Branch: 1/1 
 			Route route = stopInfo.getRoute(routeIdx);
 107 
 			for (int tripIdx = 0; tripIdx < route.getNumTrips(); ++tripIdx) {
 108 Block: 1/1 Branch: 1/1 
 				Trip trip = route.getTrip(tripIdx);
 109 
 				boolean isGhost = ((-1) == trip.getAdjAge());
 110 Block: 1/1 
 				if (isGhost) {
 111 
 					// GPS is off.  This bus may not exist.
 112 Block: 0/1 Branch: 0/1 
 					pw.println("    <TR CLASS=\"ghost\">");
 113 
 				} else {
 114 Block: 1/1 Branch: 1/1 
 					pw.println("    <TR>");
 115 
 				}
 116 Block: 1/1 
 				pw.println("      <TD>" + route.getRouteNo() + "</TD>");
 117 
 				pw.println("      <TD>" + trip.getDest() + "</TD>");
 118 
 				pw.println("      <TD>" + m_hourMinFmt.format(trip.getETA()) + "</TD>");
 119 
 				
 120 
 				pw.println("      <TD><SPAN ID=\"in_" + remainCount + "\">"+ trip.getAdjTime()+min+"</SPAN></TD>");
 121 
 				if (remainCount > 0) {
 122 Block: 1/1 Branch: 1/1 
 					remainArray.append(", ");
 123 
 				}
 124 Block: 1/1 Branch: 1/1 
 				remainArray.append(trip.getAdjTime());
 125 
 				remainCount++;
 126 
 				
 127 
 				if (trip.getAdjAge() < 0) {
 128 Block: 0/1 Branch: 0/1 
 					pw.println("      <TD>" + trans(FrankBundle.GPS_OFF) + "</TD>");
 129 
 				}
 130 
 				else {
 131 Block: 1/1 Branch: 1/1 
 					pw.println("      <TD><A HREF=\"" + mapUrl(trip.getLatitude(), trip.getLongitude()) + 
 132 
 							   "\">" + m_hourMinSecFmt.format(trip.getGPSTime()) + "</A></TD>");
 133 
 				}
 134 Block: 1/1 
 				pw.println("    </TR>");
 135 
 			}
 136 
 		}
 137 
 		
 138 Block: 1/1 Branch: 1/1 
 		pw.println("  </TABLE>");
 139 
 		
 140 
 		String dataCollectedFormat = trans(FrankBundle.DATA_COLLECTED);
 141 
 		String dataCollected = MessageFormat.format(dataCollectedFormat, m_hourMinSecFmt.format(new Date()));
 142 
 		pw.println("  <P>" + dataCollected + "</P>");
 143 
 		
 144 
 		remainArray.append(" ]");
 145 
 		writeScript(pw, remainArray.toString(), remainCount);
 146 
 		pw.println("</BODY></HTML>");
 147 
 	}
 148 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/ViewSchedule.html b/report/net/jaekl/frank/ViewSchedule.html new file mode 100644 index 0000000..66255f7 --- /dev/null +++ b/report/net/jaekl/frank/ViewSchedule.html @@ -0,0 +1,755 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.ViewSchedule
67%(4/6)
38%(8/21)
30%(3/10)
43%(20/46)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
3<init>[public]void <init>()
0doGet[public]void doGet(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
0getLocale[]java.util.Locale getLocale(javax.servlet.http.HttpServletRequest)
4getParamInt[]int getParamInt(javax.servlet.http.HttpServletRequest,java.lang.String)
8getParamString[]java.lang.String getParamString(javax.servlet.http.HttpServletRequest,java.lang.String)
3writeErrorPage[]void writeErrorPage(java.io.PrintWriter,java.lang.Throwable,java.util.Locale)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank;
 2 
 
 3 
 import java.io.ByteArrayOutputStream;
 4 
 import java.io.FileInputStream;
 5 
 import java.io.IOException;
 6 
 import java.io.PrintStream;
 7 
 import java.io.PrintWriter;
 8 
 import java.util.Locale;
 9 
 
 10 
 import javax.servlet.ServletException;
 11 
 import javax.servlet.http.HttpServlet;
 12 
 import javax.servlet.http.HttpServletRequest;
 13 
 import javax.servlet.http.HttpServletResponse;
 14 
 
 15 
 import net.jaekl.frank.octranspo.Server;
 16 
 import net.jaekl.frank.octranspo.StopInfo;
 17 
 import net.jaekl.qd.util.ExceptionUtils;
 18 
 
 19 Block: 1/1 
 public class ViewSchedule extends HttpServlet {
 20 
 	private static final long serialVersionUID = 1L;
 21 
 	
 22 
 	static final String BUNDLE_NAME = "frank";
 23 
 	static final String STOP = "stop";
 24 
 	static final String ROUTE = "route";
 25 
 	static final String LANG = "lang";
 26 
 	
 27 
 	int getParamInt(HttpServletRequest req, String paramName) {
 28 Block: 1/1 
 		String valueStr = getParamString(req, paramName);
 29 
 		try {
 30 
 			return Integer.parseInt(valueStr);
 31 
 		}
 32 Block: 1/1 
 		catch (NumberFormatException exc) {
 33 
 			// TODO:  Error page
 34 
 			return 0;
 35 
 		}
 36 
 	}
 37 
 	
 38 
 	String getParamString(HttpServletRequest req, String paramName) {
 39 Block: 1/1 
 		String valueStr = req.getParameter(paramName);
 40 
 		if (null == valueStr|| valueStr.equals("")) {
 41 
 			// TODO:  Error page
 42 Block: 1/1 Branch: 1/2 
 			return null;
 43 
 		}
 44 Block: 1/1 Branch: 1/1 
 		return valueStr;
 45 
 	}
 46 
 	
 47 
 	Locale getLocale(HttpServletRequest req) {
 48 Block: 0/1 
 		Locale result = null;
 49 
 		String lang = getParamString(req, LANG);
 50 
 		if (null != lang) {
 51 Block: 0/1 Branch: 0/1 
 			result = new Locale(lang);
 52 
 		}
 53 Block: 0/1 Branch: 0/1 
 		if (null == result) {
 54 Block: 0/1 Branch: 0/1 
 			result = Locale.getDefault();
 55 
 		}
 56 Block: 0/1 Branch: 0/1 
 		return result;
 57 
 	}
 58 
 
 59 
 	@Override
 60 
 	public void doGet(HttpServletRequest req, HttpServletResponse res)
 61 
 	throws ServletException, IOException
 62 
 	{
 63 Block: 0/1 
 		res.setContentType("text/html");
 64 
 		PrintWriter pw = res.getWriter();
 65 
 		
 66 
 		Locale locale = getLocale(req);
 67 
 		int stop = getParamInt(req, STOP);
 68 
 		int route = getParamInt(req, ROUTE);
 69 
 		
 70 
 		try {
 71 
 			FileInputStream fis = new FileInputStream("apikey.txt");
 72 
 			try {
 73 
 				Server server = new Server("192f31d2", fis);
 74 
 				StopInfo stopInfo = null;
 75 
 				
 76 
 				if (0 == route) {
 77 Block: 0/1 Branch: 0/1 
 					stopInfo = server.getNextTripsForStopAllRoutes(stop);
 78 
 				}
 79 
 				else {
 80 Block: 0/1 Branch: 0/1 
 					stopInfo = server.getNextTripsForStop(stop, route);
 81 
 				}
 82 Block: 0/1 
 				Schedule schedule = new Schedule(locale);
 83 
 				
 84 
 				schedule.writePage(pw, stopInfo);
 85 
 			}
 86 
 			finally {
 87 Block: 0/1 
 				ExceptionUtils.tryClose(fis);
 88 
 			}
 89 
 		}
 90 Block: 0/1 
 		catch (Throwable t) {
 91 
 			writeErrorPage(pw, t, locale);
 92 Block: 0/1 
 		}
 93 Block: 0/1 
 	}
 94 
 	
 95 
 	void writeErrorPage(PrintWriter pw, Throwable t, Locale locale) {
 96 Block: 1/1 
 		ByteArrayOutputStream baos = new ByteArrayOutputStream();
 97 
 		PrintStream ps = new PrintStream(baos);
 98 
 		FrankBundle bundle = FrankBundle.getInst(locale);
 99 
 		
 100 
 		pw.println("<HTML><HEAD><TITLE>" + 
 101 
 				bundle.get(FrankBundle.FRANK) + ": " +
 102 
 				bundle.get(FrankBundle.ERROR_PAGE) + 
 103 
 				"</TITLE></HEAD>");
 104 
 		pw.println("<BODY><H1>" +
 105 
 				bundle.get(FrankBundle.FRANK) + ": " +
 106 
 				bundle.get(FrankBundle.UNEXPECTED_ERROR) + 
 107 
 				"</H1><P><PRE>");
 108 
 
 109 
 		// Note that, if we cared about security, we would log this stack trace to a
 110 
 		// server log, and only report a cross-reference to the log file back to the 
 111 
 		// end user's browser, to avoid potentially exposing internal info that we 
 112 
 		// don't want to share.
 113 
 		// At least at this point, we don't care (that much), and trade off a 
 114 
 		// potential information leak in favour of reducing our code complexity
 115 
 		// and the administrator's workload.
 116 
 		t.printStackTrace(ps);
 117 
 		String stackTrace = baos.toString(); 
 118 
 		pw.println(stackTrace);
 119 
 				
 120 
 		pw.println("</PRE></P></BODY>");
 121 
 		pw.println("</HTML>");
 122 
 	}
 123 
 	
 124 
 
 125 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/octranspo/NextTrips.html b/report/net/jaekl/frank/octranspo/NextTrips.html new file mode 100644 index 0000000..555730c --- /dev/null +++ b/report/net/jaekl/frank/octranspo/NextTrips.html @@ -0,0 +1,161 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.octranspo.NextTrips
100%(2/2)
100%(2/2)
-%
100%(4/4)
+

+

+ + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<clinit>[static]void <clinit>()
1<init>[public]void <init>()
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank.octranspo;
 2 
 
 3 
 public class NextTrips extends StopInfo {
 4 
 	static final String ROOT_TAG = "GetNextTripsForStopResult";
 5 Block: 1/1 
 	static final String[] INTERNAL = { STOP_NO, STOP_LABEL, ERROR, ROUTES };
 6 
 	static final Object[][] EXTERNAL = { { ROUTE, Route.class} };
 7 
 
 8 
 	public NextTrips() {
 9 Block: 1/1 
 		super(ROOT_TAG, INTERNAL, EXTERNAL);
 10 
 	}
 11 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/octranspo/Route.html b/report/net/jaekl/frank/octranspo/Route.html new file mode 100644 index 0000000..3957995 --- /dev/null +++ b/report/net/jaekl/frank/octranspo/Route.html @@ -0,0 +1,514 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.octranspo.Route
100%(10/10)
90%(26/29)
75%(15/20)
100%(31/31)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<clinit>[static]void <clinit>()
7<init>[public]void <init>()
22endContents[public]void endContents(java.lang.String,java.lang.String,java.lang.String,java.lang.String)
9endExternal[public]void endExternal(java.lang.String,java.lang.String,java.lang.String)
4getDirection[public]java.lang.String getDirection()
2getDirectionID[public]int getDirectionID()
8getNumTrips[public]int getNumTrips()
4getRouteHeading[public]java.lang.String getRouteHeading()
8getRouteNo[public]int getRouteNo()
8getTrip[public]net.jaekl.frank.octranspo.Trip getTrip(int)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank.octranspo;
 2 
 
 3 
 import java.util.ArrayList;
 4 
 import net.jaekl.qd.xml.ParseResult;
 5 
 import net.jaekl.qd.xml.XmlParseException;
 6 
 
 7 Block: 1/1 
 public class Route extends ParseResult {
 8 
 	static final String ROUTE = "Route";
 9 
 	
 10 
 	static final String ROUTE_NO = "RouteNo";
 11 
 	static final String DIRECTION_ID = "DirectionID";
 12 
 	static final String DIRECTION = "Direction";
 13 
 	static final String ROUTE_HEADING = "RouteHeading";
 14 
 	static final String ROUTE_LABEL = "RouteLabel";	// What GetNextTripsForStop calls <RouteHeading>	
 15 
 	static final String TRIPS = "Trips";
 16 
 	
 17 
 	static final String TRIP = "Trip";
 18 
 	
 19 Block: 1/1 
 	static final String[] INTERNAL = { ROUTE_NO, DIRECTION_ID, DIRECTION, ROUTE_HEADING, ROUTE_LABEL, TRIPS };
 20 
 	static final Object[][] EXTERNAL = { { TRIP, Trip.class } };
 21 
 	
 22 
 	int m_routeNo;
 23 
 	int m_directionID;
 24 
 	String m_direction;
 25 
 	String m_routeHeading;
 26 
 	ArrayList<Trip> m_trips;
 27 
 
 28 
 	public Route() {
 29 Block: 1/1 
 		super(ROUTE, INTERNAL, EXTERNAL);
 30 
 		m_routeNo = 0;
 31 
 		m_directionID = 0;
 32 
 		m_direction = "";
 33 
 		m_routeHeading = "";
 34 
 		m_trips = new ArrayList<Trip>();
 35 
 	}
 36 
 
 37 Block: 1/1 
 	public int getRouteNo() { return m_routeNo; }
 38 Block: 1/1 
 	public int getDirectionID() { return m_directionID; }
 39 Block: 1/1 
 	public String getDirection() { return m_direction; }
 40 Block: 1/1 
 	public String getRouteHeading() { return m_routeHeading; } 
 41 Block: 1/1 
 	public int getNumTrips() { return m_trips.size(); }
 42 Block: 1/1 
 	public Trip getTrip(int idx) { return new Trip(m_trips.get(idx)); }
 43 
 
 44 
 	@Override
 45 
 	public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException 
 46 
 	{
 47 Block: 1/1 
 		if (ROUTE_NO.equals(localName)) {
 48 Block: 1/1 Branch: 1/1 
 			m_routeNo = Integer.parseInt(chars);
 49 
 		}
 50 Block: 1/1 Branch: 1/1 
 		else if (DIRECTION_ID.equals(localName)) {
 51 Block: 1/1 Branch: 1/1 
 			m_directionID = Integer.parseInt(chars);
 52 
 		}
 53 Block: 1/1 Branch: 1/1 
 		else if (DIRECTION.equals(localName)) {
 54 Block: 1/1 Branch: 1/1 
 			m_direction = chars;
 55 
 		}
 56 Block: 1/1 Branch: 1/1 
 		else if (ROUTE_HEADING.equals(localName) || ROUTE_LABEL.equals(localName)) {
 57 Block: 1/1 Branch: 2/2 
 			m_routeHeading = chars;
 58 
 		}
 59 Block: 1/1 Branch: 1/1 
 	}
 60 
 	
 61 
 	@Override
 62 
 	public void endExternal(String uri, String localName, String qName) throws XmlParseException
 63 
 	{
 64 Block: 1/1 
 		if (localName.equals(TRIP)) {
 65 Block: 1/1 Branch: 1/1 
 			ParseResult[] collected = collectParsedChildren(Trip.class);
 66 
 			for (ParseResult pr : collected) {
 67 Block: 1/1 Branch: 1/1 
 				assert(pr instanceof Trip);
 68 Block: 1/1 Branch: 1/2 
 				m_trips.add((Trip)pr);
 69 
 			}
 70 
 		}
 71 Block: 1/1 Branch: 1/2 
 	}
 72 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/octranspo/RouteSummary.html b/report/net/jaekl/frank/octranspo/RouteSummary.html new file mode 100644 index 0000000..dfb84a0 --- /dev/null +++ b/report/net/jaekl/frank/octranspo/RouteSummary.html @@ -0,0 +1,161 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.octranspo.RouteSummary
100%(2/2)
100%(2/2)
-%
100%(4/4)
+

+

+ + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<clinit>[static]void <clinit>()
4<init>[public]void <init>()
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank.octranspo;
 2 
 
 3 
 public class RouteSummary extends StopInfo {
 4 
 	static final String ROOT_TAG = "GetRouteSummaryForStopResult";
 5 Block: 1/1 
 	static final String[] INTERNAL = { STOP_NO, DESCRIPTION, ERROR, ROUTES };
 6 
 	static final Object[][] EXTERNAL = { { ROUTE, Route.class} };
 7 
 
 8 
 	public RouteSummary() {
 9 Block: 1/1 
 		super(ROOT_TAG, INTERNAL, EXTERNAL);
 10 
 	}
 11 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/octranspo/Server.html b/report/net/jaekl/frank/octranspo/Server.html new file mode 100644 index 0000000..439a6eb --- /dev/null +++ b/report/net/jaekl/frank/octranspo/Server.html @@ -0,0 +1,559 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.octranspo.Server
100%(5/5)
53%(9/17)
30%(3/10)
78%(21/27)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<clinit>[static]void <clinit>()
3<init>[public]void <init>(java.lang.String,java.io.InputStream)
1getNextTripsForStop[public]net.jaekl.frank.octranspo.StopInfo getNextTripsForStop(int,int)
1getNextTripsForStopAllRoutes[public]net.jaekl.frank.octranspo.StopInfo getNextTripsForStopAllRoutes(int)
1getRouteSummaryForStop[public]net.jaekl.frank.octranspo.StopInfo getRouteSummaryForStop(int)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank.octranspo;
 2 
 
 3 
 import java.io.BufferedReader;
 4 
 import java.io.InputStream;
 5 
 import java.io.InputStreamReader;
 6 
 import java.io.IOException;
 7 
 import java.util.ArrayList;
 8 
 
 9 
 import net.jaekl.frank.FrankException;
 10 
 import net.jaekl.qd.QDException;
 11 
 import net.jaekl.qd.http.RequestBroker;
 12 
 
 13 
 import org.apache.http.NameValuePair;
 14 
 import org.apache.http.message.BasicNameValuePair;
 15 
 
 16 Block: 1/1 
 public class Server
 17 
 {
 18 
 	static final String API_KEY = "apiKey";
 19 
 	static final String APP_ID = "appID";
 20 
 	static final String GATEWAY_URL = "https://api.octranspo1.com/v1.2/";
 21 
 	static final String ROUTE_NO = "routeNo";
 22 
 	static final String STOP_NO = "stopNo";
 23 
 	static final String GET_ROUTE_SUMMARY_FOR_STOP = "GetRouteSummaryForStop";
 24 
 	static final String GET_NEXT_TRIPS_FOR_STOP = "GetNextTripsForStop";
 25 
 	static final String GET_NEXT_TRIPS_FOR_STOP_ALL_ROUTES = "GetNextTripsForStopAllRoutes";
 26 
 
 27 
 	RequestBroker m_broker;
 28 
 	ArrayList<NameValuePair> m_baseParams;
 29 
 
 30 
 	// Constructor
 31 
 	// appID   The appID for our application
 32 
 	// is      InputStream from which to read the apiKey
 33 Block: 1/1 
 	public Server(String appID, InputStream is) throws IOException {
 34 
 		assert (null != appID);
 35 Block: 1/1 Branch: 1/2 
 		assert (null != is);
 36 
 
 37 Block: 1/1 Branch: 1/2 
 		BufferedReader br = new BufferedReader(new InputStreamReader(is));
 38 
 		String apiKey = br.readLine();
 39 
 
 40 
 		m_baseParams = new ArrayList<NameValuePair>();
 41 
 		m_baseParams.add(new BasicNameValuePair(APP_ID, appID));
 42 
 		m_baseParams.add(new BasicNameValuePair(API_KEY, apiKey));
 43 
 
 44 
 		m_broker = new RequestBroker(GATEWAY_URL, m_baseParams);
 45 
 	}
 46 
 
 47 
 	public StopInfo getRouteSummaryForStop(int stopNo) 
 48 
 			throws FrankException
 49 
 	{
 50 
 		try {
 51 Block: 1/1 
 			ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
 52 
 			params.add(new BasicNameValuePair(STOP_NO, Integer.valueOf(stopNo).toString()));
 53 
 			return (StopInfo) m_broker.submitAndParse(GET_ROUTE_SUMMARY_FOR_STOP, params, RouteSummary.class);
 54 
 		}
 55 Block: 0/1 
 		catch (QDException e) {
 56 
 			throw new FrankException(e);
 57 
 		}
 58 
 	}
 59 
 
 60 
 	public StopInfo getNextTripsForStop(int stopNo, int routeNo) 
 61 
 			throws FrankException
 62 
 	{
 63 
 		try {
 64 Block: 1/1 
 			ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
 65 
 			params.add(new BasicNameValuePair(STOP_NO, Integer.valueOf(stopNo).toString()));
 66 
 			params.add(new BasicNameValuePair(ROUTE_NO, Integer.valueOf(routeNo).toString()));
 67 
 			return (StopInfo) m_broker.submitAndParse(GET_NEXT_TRIPS_FOR_STOP, params, NextTrips.class);
 68 
 		}
 69 Block: 0/1 
 		catch (QDException e) {
 70 
 			throw new FrankException(e);
 71 
 		}
 72 
 	}
 73 
 
 74 
 	public StopInfo getNextTripsForStopAllRoutes(int stopNo) 
 75 
 			throws FrankException
 76 
 	{
 77 
 		try {
 78 Block: 1/1 
 			ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
 79 
 			params.add(new BasicNameValuePair(STOP_NO, Integer.valueOf(stopNo).toString()));
 80 
 			return (StopInfo) m_broker.submitAndParse(GET_NEXT_TRIPS_FOR_STOP_ALL_ROUTES, params, RouteSummary.class);
 81 
 		}
 82 Block: 0/1 
 		catch (QDException e) {
 83 
 			throw new FrankException(e);
 84 
 		}
 85 
 	}
 86 
 }
 87 
 
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/octranspo/StopInfo.html b/report/net/jaekl/frank/octranspo/StopInfo.html new file mode 100644 index 0000000..4e65791 --- /dev/null +++ b/report/net/jaekl/frank/octranspo/StopInfo.html @@ -0,0 +1,528 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.octranspo.StopInfo
100%(9/9)
83%(24/29)
64%(14/22)
100%(32/32)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<clinit>[static]void <clinit>()
5<init>[public]void <init>(java.lang.String,java.lang.String[],java.lang.Object[][])
11endContents[public]void endContents(java.lang.String,java.lang.String,java.lang.String,java.lang.String)
5endExternal[public]void endExternal(java.lang.String,java.lang.String,java.lang.String)
5getDescr[public]java.lang.String getDescr()
3getError[public]java.lang.String getError()
7getNumRoutes[public]int getNumRoutes()
6getRoute[public]net.jaekl.frank.octranspo.Route getRoute(int)
5getStopNo[public]int getStopNo()
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank.octranspo;
 2 
 
 3 
 import java.util.ArrayList;
 4 
 
 5 
 import net.jaekl.qd.xml.ParseResult;
 6 
 import net.jaekl.qd.xml.XmlParseException;
 7 
 
 8 Block: 1/1 
 public class StopInfo extends ParseResult
 9 
 {
 10 
 	// potential child tag names
 11 Block: 1/1 
 	public static String STOP_NO = "StopNo";
 12 
 	public static String DESCRIPTION = "StopDescription";	// Present in RouteSummary
 13 
 	public static String STOP_LABEL = "StopLabel";			// What NextTrips calls StopDescription
 14 
 	public static String ERROR = "Error";
 15 
 	public static String ROUTES = "Routes";
 16 
 	public static String ROUTE = "Route";
 17 
 
 18 
 	// data returned inside our element
 19 
 	int m_stopNo;
 20 
 	String m_descr;
 21 
 	String m_error;
 22 
 	ArrayList<Route> m_routes;
 23 
 
 24 
 	// Constructor
 25 
 	public StopInfo(String rootTagName, String[] internal, Object[][] external) {
 26 Block: 1/1 
 		super(rootTagName, internal, external);
 27 
 
 28 
 		m_stopNo = 0;
 29 
 		m_descr = "";
 30 
 		m_error = "";
 31 
 		m_routes = new ArrayList<Route>();
 32 
 	}
 33 
 
 34 
 	// -----------------------------
 35 
 	// Public methods to access data
 36 
 
 37 Block: 1/1 
 	public int getStopNo() { return m_stopNo; }
 38 Block: 1/1 
 	public String getDescr() { return m_descr; }
 39 Block: 1/1 
 	public String getError() { return m_error; }
 40 Block: 1/1 
 	public int getNumRoutes() { return m_routes.size(); }
 41 Block: 1/1 
 	public Route getRoute(int idx) { return m_routes.get(idx); }
 42 
 
 43 
 	// --------------------------
 44 
 	// ParseResult implementation
 45 
 
 46 
 	@Override
 47 
 	public void endContents(String uri, String localName, String qName, String chars) throws XmlParseException
 48 
 	{
 49 Block: 1/1 
 		assert (null != localName);
 50 
 
 51 Block: 1/1 Branch: 1/2 
 		if (localName.equals(STOP_NO)) {
 52 Block: 1/1 Branch: 1/1 
 			m_stopNo = Integer.parseInt(chars);
 53 
 		}
 54 Block: 1/1 Branch: 1/1 
 		else if (localName.equals(DESCRIPTION) || localName.equals(STOP_LABEL)) {
 55 Block: 1/1 Branch: 2/2 
 			m_descr = chars;
 56 
 		}
 57 Block: 1/1 Branch: 1/1 
 		else if (localName.equals(ERROR)) {
 58 Block: 1/1 Branch: 1/1 
 			m_error = chars;
 59 
 		}
 60 Block: 1/1 Branch: 1/1 
 	}
 61 
 	
 62 
 	@Override
 63 
 	public void endExternal(String uri, String localName, String qName) throws XmlParseException
 64 
 	{
 65 Block: 1/1 
 		if (localName.equals(ROUTE)) {
 66 Block: 1/1 Branch: 1/1 
 			ParseResult[] collected = collectParsedChildren(Route.class);
 67 
 			for (ParseResult pr : collected) {
 68 Block: 1/1 Branch: 1/1 
 				assert (pr instanceof Route);
 69 Block: 1/1 Branch: 1/2 
 				m_routes.add((Route)pr);
 70 
 			}
 71 
 		}
 72 Block: 1/1 Branch: 1/2 
 	}
 73 
 	
 74 
 	
 75 
 }
 76 
 
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/octranspo/Trip.html b/report/net/jaekl/frank/octranspo/Trip.html new file mode 100644 index 0000000..c2e39ed --- /dev/null +++ b/report/net/jaekl/frank/octranspo/Trip.html @@ -0,0 +1,890 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.frank.octranspo.Trip
94%(15/16)
94%(34/36)
94%(17/18)
95%(58/61)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<clinit>[static]void <clinit>()
13<init>[public]void <init>()
8<init>[public]void <init>(net.jaekl.frank.octranspo.Trip)
81endContents[public]void endContents(java.lang.String,java.lang.String,java.lang.String,java.lang.String)
0endExternal[public]void endExternal(java.lang.String,java.lang.String,java.lang.String)
12getAdjAge[public]double getAdjAge()
12getAdjTime[public]int getAdjTime()
4getBusType[public]java.lang.String getBusType()
8getDest[public]java.lang.String getDest()
4getETA[public]java.util.Date getETA()
4getGPSTime[public]java.util.Date getGPSTime()
8getLatitude[public]double getLatitude()
8getLongitude[public]double getLongitude()
4getSpeed[public]double getSpeed()
4getStart[public]java.util.Date getStart()
4isLastTrip[public]boolean isLastTrip()
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.frank.octranspo;
 2 
 
 3 
 import java.text.DateFormat;
 4 
 import java.text.ParseException;
 5 
 import java.text.SimpleDateFormat;
 6 
 import java.util.Date;
 7 
 
 8 
 import net.jaekl.qd.util.ParseUtils;
 9 
 import net.jaekl.qd.xml.ParseResult;
 10 
 import net.jaekl.qd.xml.XmlParseException;
 11 
 
 12 
 public class Trip extends ParseResult {
 13 
 	static final String TRIP = "Trip";
 14 
 	
 15 
 	static final String TRIP_DESTINATION = "TripDestination";
 16 
 	static final String TRIP_START_TIME = "TripStartTime";
 17 
 	static final String ADJUSTED_SCHEDULE_TIME = "AdjustedScheduleTime";
 18 
 	static final String ADJUSTMENT_AGE = "AdjustmentAge";
 19 
 	static final String LAST_TRIP_OF_SCHEDULE = "LastTripOfSchedule";
 20 
 	static final String BUS_TYPE = "BusType";
 21 
 	static final String GPS_SPEED = "GPSSpeed";
 22 
 	static final String LATITUDE = "Latitude";
 23 
 	static final String LONGITUDE = "Longitude";
 24 
 	
 25 Block: 1/1 
 	static final String[] INTERNAL = { TRIP_DESTINATION,
 26 
 		                               TRIP_START_TIME,
 27 
 		                               ADJUSTED_SCHEDULE_TIME,
 28 
 		                               ADJUSTMENT_AGE,
 29 
 		                               LAST_TRIP_OF_SCHEDULE,
 30 
 		                               BUS_TYPE,
 31 
 		                               GPS_SPEED,
 32 
 		                               LATITUDE,
 33 
 		                               LONGITUDE };
 34 
 	static final Object[][] EXTERNAL = {};
 35 
 	
 36 
     String  m_dest;      // destination
 37 
     Date    m_start;     // time at which the trip started / is scheduled to start
 38 
     int     m_adjTime;   // minutes until bus is predicted to arrive
 39 
     double  m_adjAge;    // time since the last GPS data was received, in minutes (possibly fractional)
 40 
     boolean m_lastTrip;  // is this the last scheduled trip of the day?
 41 
     String  m_busType;   // type of bus
 42 
     double  m_speed;     // speed (km/h) when last polled
 43 
     double  m_long;      // longitude
 44 
     double  m_lat;       // latitude
 45 
     
 46 
     DateFormat m_dateFormat;
 47 
     Date       m_constructed;	// DateTime when this object was constructed
 48 
 
 49 
     public Trip() {
 50 Block: 1/1 
     	super(TRIP, INTERNAL, EXTERNAL);
 51 
     	m_dest = "";
 52 
     	m_start = new Date();
 53 
     	m_adjTime = 0;
 54 
     	m_adjAge = 0.0;
 55 
     	m_lastTrip = false;
 56 
     	m_busType = "";
 57 
     	m_speed = 0.0;
 58 
     	m_long = 0.0;
 59 
     	m_lat = 0.0;
 60 
     	
 61 
     	m_dateFormat = new SimpleDateFormat("hh:mm");
 62 
     	m_constructed = new Date();
 63 
     }
 64 
     
 65 
     public Trip(Trip other) {
 66 Block: 1/1 
     	super(TRIP, INTERNAL, EXTERNAL);
 67 
     	m_dest = other.m_dest;
 68 
     	m_start = other.m_start;
 69 
     	m_adjTime = other.m_adjTime;
 70 
     	m_adjAge = other.m_adjAge;
 71 
     	m_lastTrip = other.m_lastTrip;
 72 
     	m_busType = other.m_busType;
 73 
     	m_speed = other.m_speed;
 74 
     	m_long = other.m_long;
 75 
     	m_lat = other.m_lat;
 76 
     	m_constructed = other.m_constructed;
 77 
 	}
 78 
 
 79 Block: 1/1 
 	public String getDest() { return m_dest; }
 80 Block: 1/1 
     public Date getStart() { return m_start; }
 81 Block: 1/1 
     public int getAdjTime() { return m_adjTime; }
 82 Block: 1/1 
     public double getAdjAge() { return m_adjAge; }
 83 Block: 1/1 
     public boolean isLastTrip() { return m_lastTrip; }
 84 Block: 1/1 
     public String getBusType() { return m_busType; }
 85 Block: 1/1 
     public double getSpeed() { return m_speed; }
 86 Block: 1/1 
     public double getLongitude() { return m_long; }
 87 Block: 1/1 
     public double getLatitude() { return m_lat; }
 88 
     
 89 
     // Estimated (Date)Time of Arrival of this trip at the stop 
 90 Block: 1/1 
     public Date getETA() { return new Date(m_constructed.getTime() + (long)(1000 * 60 * m_adjTime)); }
 91 
     
 92 
     // (Date)Time when the GPS for this bus was last read
 93 Block: 1/1 
     public Date getGPSTime() { return new Date(m_constructed.getTime() - (long)(1000 * 60 * m_adjAge)); } 
 94 
 
 95 
     // ---------------------------
 96 
     // ParseResult implementation:
 97 
     
 98 
 	@Override
 99 
 	public void endContents(String uri, String localName, String qName,	String chars) throws XmlParseException 
 100 
 	{
 101 
 		try {
 102 Block: 1/1 
 			if (TRIP_DESTINATION.equals(localName)) {
 103 Block: 1/1 Branch: 1/1 
 				m_dest = chars;
 104 
 			}
 105 Block: 1/1 Branch: 1/1 
 			else if (TRIP_START_TIME.equals(localName)) {
 106 Block: 1/1 Branch: 1/1 
 				m_start = m_dateFormat.parse(chars);
 107 
 			}
 108 Block: 1/1 Branch: 1/1 
 			else if (ADJUSTED_SCHEDULE_TIME.equals(localName)) {
 109 Block: 1/1 Branch: 1/1 
 				m_adjTime = ParseUtils.parseInt(chars);
 110 
 			}
 111 Block: 1/1 Branch: 1/1 
 			else if (ADJUSTMENT_AGE.equals(localName)) {
 112 Block: 1/1 Branch: 1/1 
 				m_adjAge = ParseUtils.parseDouble(chars);
 113 
 			}
 114 Block: 1/1 Branch: 1/1 
 			else if (LAST_TRIP_OF_SCHEDULE.equals(localName)) {
 115 Block: 1/1 Branch: 1/1 
 				m_lastTrip = Boolean.parseBoolean(chars);
 116 
 			}
 117 Block: 1/1 Branch: 1/1 
 			else if (BUS_TYPE.equals(localName)) {
 118 Block: 1/1 Branch: 1/1 
 				m_busType = chars;
 119 
 			}
 120 Block: 1/1 Branch: 1/1 
 			else if (GPS_SPEED.equals(localName)) {
 121 Block: 1/1 Branch: 1/1 
 				m_speed = ParseUtils.parseDouble(chars);
 122 
 			}
 123 Block: 1/1 Branch: 1/1 
 			else if (LONGITUDE.equals(localName)) {
 124 Block: 1/1 Branch: 1/1 
 				m_long = ParseUtils.parseDouble(chars);
 125 
 			}
 126 Block: 1/1 Branch: 1/1 
 			else if (LATITUDE.equals(localName)) {
 127 Block: 1/1 Branch: 1/1 
 				m_lat = ParseUtils.parseDouble(chars);
 128 
 			}
 129 Block: 0/1 
 		} catch (ParseException pe) {
 130 
 			throw new XmlParseException(pe);
 131 Block: 1/1 Branch: 0/1 
 		}
 132 Block: 1/1 
 	}
 133 
 	
 134 
 	@Override
 135 
 	public void endExternal(String uri, String localName, String qName) throws XmlParseException
 136 
 	{
 137 
 		// no externally-parsed children
 138 Block: 0/1 
 	}
 139 
 }
 140 
 
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/octranspo/package-frame.html b/report/net/jaekl/frank/octranspo/package-frame.html new file mode 100644 index 0000000..27e2836 --- /dev/null +++ b/report/net/jaekl/frank/octranspo/package-frame.html @@ -0,0 +1,23 @@ + + +coverage report + + + +net.jaekl.frank.octranspo   98% (43/44)
+

+All classes + + + + +
+NextTrips  100% (2/2)
+Route  100% (10/10)
+RouteSummary  100% (2/2)
+Server  100% (5/5)
+StopInfo  100% (9/9)
+Trip  94% (15/16)
+
+ + diff --git a/report/net/jaekl/frank/octranspo/package-summary.html b/report/net/jaekl/frank/octranspo/package-summary.html new file mode 100644 index 0000000..50484c0 --- /dev/null +++ b/report/net/jaekl/frank/octranspo/package-summary.html @@ -0,0 +1,83 @@ + + +coverage report + + + + +Coverage report +

+ + + + + + + + + + + + + + + + + +
 #classes%method%block%branch%line
net.jaekl.frank.octranspo6
98%(43/44)
84%(97/115)
70%(49/70)
94%(150/159)
+

+Classes
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name%method%block%branch%line
NextTrips 100% (2/2) 100% (2/2) - 100% (4/4)
Route 100% (10/10) 90% (26/29) 75% (15/20) 100% (31/31)
RouteSummary 100% (2/2) 100% (2/2) - 100% (4/4)
Server 100% (5/5) 53% (9/17) 30% (3/10) 78% (21/27)
StopInfo 100% (9/9) 83% (24/29) 64% (14/22) 100% (32/32)
Trip 94% (15/16) 94% (34/36) 94% (17/18) 95% (58/61)
+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/frank/package-frame.html b/report/net/jaekl/frank/package-frame.html new file mode 100644 index 0000000..3f6ff3f --- /dev/null +++ b/report/net/jaekl/frank/package-frame.html @@ -0,0 +1,21 @@ + + +coverage report + + + +net.jaekl.frank   83% (15/18)
+

+All classes + + + + +
+FrankBundle  100% (4/4)
+FrankException  0% (0/1)
+Schedule  100% (7/7)
+ViewSchedule  67% (4/6)
+
+ + diff --git a/report/net/jaekl/frank/package-summary.html b/report/net/jaekl/frank/package-summary.html new file mode 100644 index 0000000..5ff0e15 --- /dev/null +++ b/report/net/jaekl/frank/package-summary.html @@ -0,0 +1,111 @@ + + +coverage report + + + + +Coverage report +

+ + + + + + + + + + + + + + + + + +
 #classes%method%block%branch%line
net.jaekl.frank4
83%(15/18)
64%(38/59)
57%(16/28)
77%(114/148)
+

+Packages
+ + + + + + + + + + + + + + + + + + + +
Name#classes%class%method%block%branch%line
net.jaekl.frank.octranspo6 100% (6/6) 98% (43/44) 84% (97/115) 70% (49/70) 94% (150/159)
+

+Classes
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name%method%block%branch%line
FrankBundle 100% (4/4) 69% (9/13) 67% (4/6) 79% (15/19)
FrankException 0% (0/1) 0% (0/1) - 0% (0/2)
Schedule 100% (7/7) 88% (21/24) 75% (9/12) 98% (79/81)
ViewSchedule 67% (4/6) 38% (8/21) 30% (3/10) 43% (20/46)
+

+Total (including subpackages)
+ + + + + + + + + + + + + + + + +
-#classes%method%block%branch%line
10 94% (58/62) 78% (135/174) 66% (65/98) 86% (264/307)
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/QDBundleFactory.html b/report/net/jaekl/qd/QDBundleFactory.html new file mode 100644 index 0000000..050181d --- /dev/null +++ b/report/net/jaekl/qd/QDBundleFactory.html @@ -0,0 +1,312 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.QDBundleFactory
100%(3/3)
88%(7/8)
75%(3/4)
100%(11/11)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<init>[private]void <init>()
3getBundle[public]java.util.ResourceBundle getBundle(java.lang.String,java.util.Locale)
5getInst[public, static]net.jaekl.qd.QDBundleFactory getInst()
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 // Copyright (C) 2004, 2014 Christian Jaekl
 2 
 
 3 
 // Central spot from which to access ResourceBundles.
 4 
 // This made more sense with earlier versions of Java, where the specification did not 
 5 
 // guarantee that ResourceBundles would be cached.  Java 7 and later cache by default,
 6 
 // but it still seems prudent to centralize accesses to resources here so that we have 
 7 
 // control in case we want to implement our own cache, or override certain behaviours.
 8 
 // 
 9 
 // Note that we rely on the JVM's caching, to avoid unnecessary overhead.
 10 
 // See http://java2go.blogspot.ca/2010/03/dont-be-smart-never-implement-resource.html
 11 
 
 12 
 package net.jaekl.qd;
 13 
 
 14 
 import java.util.Locale;
 15 
 import java.util.ResourceBundle;
 16 
 
 17 
 public class QDBundleFactory {
 18 
 	static volatile QDBundleFactory m_inst;	// singleton instance
 19 
 	
 20 Block: 1/1 
 	private QDBundleFactory() {
 21 
 		// no-op
 22 
 	}
 23 
 	
 24 
 	public static QDBundleFactory getInst() {
 25 Block: 1/1 
 		QDBundleFactory result = m_inst;
 26 
 		if (null == result) {
 27 Block: 1/1 Branch: 1/1 
 			synchronized(QDBundleFactory.class) {
 28 
 				if (null == m_inst) {
 29 Block: 1/1 Branch: 1/1 
 					m_inst = new QDBundleFactory();
 30 
 				}
 31 Block: 1/1 Branch: 0/1 
 				result = m_inst;
 32 
 			}
 33 
 		}
 34 Block: 1/1 Branch: 1/1 
 		return result;
 35 
 	}
 36 
 	
 37 
 	public ResourceBundle getBundle(String baseName, Locale locale) {
 38 Block: 1/1 
 		return ResourceBundle.getBundle(baseName, locale);
 39 
 	}
 40 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/QDException.html b/report/net/jaekl/qd/QDException.html new file mode 100644 index 0000000..b7a7f3b --- /dev/null +++ b/report/net/jaekl/qd/QDException.html @@ -0,0 +1,176 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.QDException
100%(2/2)
100%(2/2)
-%
100%(4/4)
+

+

+ + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
4<init>[public]void <init>()
1<init>[public]void <init>(java.lang.Throwable)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.qd;
 2 
 
 3 
 public class QDException extends Exception
 4 
 {
 5 
 	private static final long serialVersionUID = 1L;
 6 
 
 7 
 	public QDException() {
 8 Block: 1/1 
 		super();
 9 
 	}
 10 
 	
 11 
 	public QDException(Throwable t) {
 12 Block: 1/1 
 		super(t);
 13 
 	}
 14 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/http/RequestBroker.html b/report/net/jaekl/qd/http/RequestBroker.html new file mode 100644 index 0000000..7dcf324 --- /dev/null +++ b/report/net/jaekl/qd/http/RequestBroker.html @@ -0,0 +1,890 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.http.RequestBroker
83%(5/6)
50%(14/28)
40%(4/10)
53%(31/58)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<clinit>[static]void <clinit>()
9<init>[public]void <init>(java.lang.String,java.util.ArrayList)
0doSubmit[]java.io.InputStream doSubmit(java.lang.String,java.util.ArrayList)
1submit[public]java.lang.String submit(java.lang.String,java.util.ArrayList)
5submitAndParse[public]net.jaekl.qd.xml.ParseResult submitAndParse(java.lang.String,java.util.ArrayList,java.lang.Class)
4submitAndParse[public]net.jaekl.qd.xml.ParseResult submitAndParse(java.lang.String,java.util.ArrayList,java.lang.Class,java.lang.String)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 // Copyright (C) 2004, 2014 Christian Jaekl
 2 
 
 3 
 package net.jaekl.qd.http;
 4 
 
 5 
 import java.io.BufferedReader;
 6 
 import java.io.IOException;
 7 
 import java.io.InputStream;
 8 
 import java.io.InputStreamReader;
 9 
 import java.io.UnsupportedEncodingException;
 10 
 import java.lang.reflect.InvocationTargetException;
 11 
 import java.util.ArrayList;
 12 
 
 13 
 import net.jaekl.qd.QDException;
 14 
 import net.jaekl.qd.util.ExceptionUtils;
 15 
 import net.jaekl.qd.xml.ParseHandler;
 16 
 import net.jaekl.qd.xml.ParseResult;
 17 
 
 18 
 import org.apache.http.HttpEntity;
 19 
 import org.apache.http.HttpResponse;
 20 
 import org.apache.http.NameValuePair;
 21 
 import org.apache.http.client.HttpClient;
 22 
 import org.apache.http.client.config.RequestConfig;
 23 
 import org.apache.http.client.entity.UrlEncodedFormEntity;
 24 
 import org.apache.http.client.methods.HttpPost;
 25 
 import org.apache.http.impl.client.HttpClientBuilder;
 26 
 import org.xml.sax.InputSource;
 27 
 import org.xml.sax.SAXException;
 28 
 import org.xml.sax.XMLReader;
 29 
 import org.xml.sax.helpers.XMLReaderFactory;
 30 
 
 31 Block: 1/1 
 public class RequestBroker
 32 
 {
 33 
 	final String UTF_8 = "UTF-8";
 34 
 	final int TIMEOUT_MSEC = 5000;	// Allow at most 5 seconds before we declare the server to be unresponsive
 35 
 	
 36 
 	String m_gatewayUrl;
 37 
 	ArrayList<NameValuePair> m_baseParams;
 38 
 
 39 
 	public RequestBroker(String gatewayUrl, ArrayList<NameValuePair> baseParams)
 40 Block: 1/1 
 	{
 41 
 		m_gatewayUrl = gatewayUrl;
 42 
 		m_baseParams = new ArrayList<NameValuePair>(baseParams);
 43 
 	}
 44 
 	
 45 
 	// NB:  Caller is responsible for close()ing the returned InputStream
 46 
 	//
 47 
 	InputStream doSubmit(String method, ArrayList<NameValuePair> passedParams) throws QDException
 48 
 	{
 49 Block: 0/1 
 		ArrayList<NameValuePair> params = new ArrayList<NameValuePair>(m_baseParams);
 50 
 		params.addAll(passedParams);
 51 
 		
 52 
 		try {
 53 
 			RequestConfig.Builder requestBuilder = RequestConfig.custom();
 54 
 			requestBuilder = requestBuilder.setConnectTimeout(TIMEOUT_MSEC);
 55 
 			requestBuilder = requestBuilder.setSocketTimeout(TIMEOUT_MSEC);
 56 
 			HttpClientBuilder builder = HttpClientBuilder.create();
 57 
 			builder.setDefaultRequestConfig(requestBuilder.build());
 58 
 			HttpClient httpClient = builder.build(); 
 59 
 			HttpPost httpPost = new HttpPost(m_gatewayUrl + "/" + method + "/");
 60 
 			httpPost.setEntity(new UrlEncodedFormEntity(params, UTF_8));
 61 
 			HttpResponse response = httpClient.execute(httpPost);
 62 
 			HttpEntity entity = response.getEntity();
 63 
 
 64 
 			if (null != entity) {
 65 Block: 0/1 Branch: 0/1 
 				InputStream is = entity.getContent();
 66 
 				return is;
 67 
 			}
 68 
 		}
 69 Block: 0/1 
 		catch (UnsupportedEncodingException uee) {
 70 
 			// We should actually be guaranteed that this never happens, 
 71 
 			// because all JVMs are required to support UTF-8
 72 
 			assert(false);
 73 Block: 0/1 Branch: 0/1 
 			throw new Error(uee);
 74 
 		}
 75 Block: 0/1 
 		catch (IOException ioe) {
 76 
 			throw new QDException(ioe);
 77 Block: 0/1 Branch: 0/1 
 		}
 78 
 		
 79 Block: 0/1 
 		return null;
 80 
 	}
 81 
 
 82 
 	public String submit(String method, ArrayList<NameValuePair> passedParams) throws QDException
 83 
 	{
 84 Block: 1/1 
 		StringBuilder sb = new StringBuilder();
 85 
 		InputStream is = null;
 86 
 
 87 
 		try {
 88 
 			is = doSubmit(method, passedParams);
 89 
 			BufferedReader br = new BufferedReader(new InputStreamReader(is));
 90 
 			String line = br.readLine();
 91 Block: 1/1 
 			while (null != line) {
 92 Block: 1/1 Branch: 1/1 
 				sb.append(line).append("\n");
 93 
 				line = br.readLine();
 94 
 			}
 95 
 		}
 96 Block: 0/1 
 		catch (IOException ioe) {
 97 
 			throw new QDException(ioe);
 98 
 		}
 99 
 		finally {
 100 Block: 1/2 Branch: 1/1 
 			ExceptionUtils.tryClose(is);
 101 
 		}
 102 
 
 103 Block: 1/1 
 		return sb.toString();
 104 
 	}
 105 
 	
 106 
 	public ParseResult submitAndParse(String method, 
 107 
 			  ArrayList<NameValuePair> passedParams, 
 108 
 			  Class<? extends ParseResult> rootParserClass)
 109 
 	throws QDException
 110 
 	{
 111 Block: 1/1 
 		return submitAndParse(method, passedParams, rootParserClass, null);
 112 
 	}
 113 
 	
 114 
 	public ParseResult submitAndParse(String method, 
 115 
 									  ArrayList<NameValuePair> passedParams, 
 116 
 									  Class<? extends ParseResult> rootParserClass,
 117 
 									  String rootTagName)
 118 
 	throws QDException
 119 
 	{
 120 Block: 1/1 
 		ParseResult result = null;
 121 
 		InputStream is = null;
 122 
 		
 123 
 		try {
 124 
 			if (null == rootTagName) {
 125 Block: 1/1 Branch: 1/1 
 				result = (ParseResult) rootParserClass.newInstance();
 126 
 			} 
 127 
 			else {
 128 Block: 0/1 Branch: 0/1 
 				result = (ParseResult) rootParserClass.getDeclaredConstructor(String.class).newInstance(rootTagName);
 129 
 			}
 130 Block: 1/1 
 			is = doSubmit(method, passedParams);
 131 
 			XMLReader reader = XMLReaderFactory.createXMLReader();
 132 
 			ParseHandler ph = new ParseHandler(result);
 133 
 			reader.setContentHandler(ph);
 134 
 			reader.parse(new InputSource(is));
 135 
 		} 
 136 Block: 0/1 
 		catch ( InstantiationException
 137 
 				| InvocationTargetException
 138 
 				| IllegalAccessException
 139 
 				| IOException 
 140 
 				| NoSuchMethodException
 141 
 				| SAXException 
 142 
 				e )
 143 
 		{
 144 
 			throw new QDException(e);
 145 
 		} 
 146 
 		finally {
 147 Block: 0/1 
 			ExceptionUtils.tryClose(is);
 148 
 		}
 149 
 		
 150 Block: 1/1 
 		return result;
 151 
 	}
 152 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/http/package-frame.html b/report/net/jaekl/qd/http/package-frame.html new file mode 100644 index 0000000..2f2027c --- /dev/null +++ b/report/net/jaekl/qd/http/package-frame.html @@ -0,0 +1,18 @@ + + +coverage report + + + +net.jaekl.qd.http   83% (5/6)
+

+All classes + + + + +
+RequestBroker  83% (5/6)
+
+ + diff --git a/report/net/jaekl/qd/http/package-summary.html b/report/net/jaekl/qd/http/package-summary.html new file mode 100644 index 0000000..5eae753 --- /dev/null +++ b/report/net/jaekl/qd/http/package-summary.html @@ -0,0 +1,48 @@ + + +coverage report + + + + +Coverage report +

+ + + + + + + + + + + + + + + + + +
 #classes%method%block%branch%line
net.jaekl.qd.http1
83%(5/6)
50%(14/28)
40%(4/10)
53%(31/58)
+

+Classes
+ + + + + + + + + + + + + + + +
Name%method%block%branch%line
RequestBroker 83% (5/6) 50% (14/28) 40% (4/10) 53% (31/58)
+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/package-frame.html b/report/net/jaekl/qd/package-frame.html new file mode 100644 index 0000000..d236694 --- /dev/null +++ b/report/net/jaekl/qd/package-frame.html @@ -0,0 +1,19 @@ + + +coverage report + + + +net.jaekl.qd   100% (5/5)
+

+All classes + + + + +
+QDBundleFactory  100% (3/3)
+QDException  100% (2/2)
+
+ + diff --git a/report/net/jaekl/qd/package-summary.html b/report/net/jaekl/qd/package-summary.html new file mode 100644 index 0000000..e83749d --- /dev/null +++ b/report/net/jaekl/qd/package-summary.html @@ -0,0 +1,115 @@ + + +coverage report + + + + +Coverage report +

+ + + + + + + + + + + + + + + + + +
 #classes%method%block%branch%line
net.jaekl.qd2
100%(5/5)
90%(9/10)
75%(3/4)
100%(15/15)
+

+Packages
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name#classes%class%method%block%branch%line
net.jaekl.qd.http1 100% (1/1) 83% (5/6) 50% (14/28) 40% (4/10) 53% (31/58)
net.jaekl.qd.util3 100% (3/3) 57% (4/7) 88% (21/24) 100% (14/14) 86% (19/22)
net.jaekl.qd.xml5 80% (4/5) 74% (23/31) 80% (74/93) 82% (41/50) 83% (114/137)
+

+Classes
+ + + + + + + + + + + + + + + + + + + + + + +
Name%method%block%branch%line
QDBundleFactory 100% (3/3) 88% (7/8) 75% (3/4) 100% (11/11)
QDException 100% (2/2) 100% (2/2) - 100% (4/4)
+

+Total (including subpackages)
+ + + + + + + + + + + + + + + + +
-#classes%method%block%branch%line
11 76% (37/49) 76% (118/155) 79% (62/78) 77% (179/232)
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/util/ExceptionUtils.html b/report/net/jaekl/qd/util/ExceptionUtils.html new file mode 100644 index 0000000..54f408a --- /dev/null +++ b/report/net/jaekl/qd/util/ExceptionUtils.html @@ -0,0 +1,201 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.util.ExceptionUtils
50%(1/2)
83%(5/6)
100%(2/2)
86%(6/7)
+

+

+ + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
0<init>[public]void <init>()
8tryClose[public, static]void tryClose(java.io.Closeable)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.qd.util;
 2 
 
 3 
 import java.io.Closeable;
 4 
 import java.io.IOException;
 5 
 
 6 
 import net.jaekl.qd.QDException;
 7 
 
 8 Block: 0/1 
 public class ExceptionUtils {
 9 
 	public static void tryClose(Closeable closeable) throws QDException {
 10 
 		try {
 11 Block: 1/1 
 			if (null != closeable) {
 12 Block: 1/1 Branch: 1/1 
 				closeable.close();
 13 
 			}
 14 
 		}
 15 Block: 1/1 
 		catch (IOException ioe) {
 16 
 			throw new QDException(ioe);
 17 Block: 1/1 Branch: 1/1 
 		}
 18 Block: 1/1 
 	}
 19 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/util/ParseUtils.html b/report/net/jaekl/qd/util/ParseUtils.html new file mode 100644 index 0000000..557169e --- /dev/null +++ b/report/net/jaekl/qd/util/ParseUtils.html @@ -0,0 +1,262 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.util.ParseUtils
67%(2/3)
91%(10/11)
100%(8/8)
91%(10/11)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
0<init>[public]void <init>()
44parseDouble[public, static]double parseDouble(java.lang.String)
17parseInt[public, static]int parseInt(java.lang.String)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.qd.util;
 2 
 
 3 Block: 0/1 
 public class ParseUtils {
 4 
 	// Attempt to parse the string as a double.
 5 
 	// Treat errors as a zero value.
 6 
 	public static double parseDouble(String string) {
 7 Block: 1/1 
 		if ((null == string) || ("".equals(string))) {
 8 Block: 1/1 Branch: 2/2 
 			return 0.0;
 9 
 		}
 10 
 		try {
 11 Block: 1/1 Branch: 1/1 
 			return Double.parseDouble(string);
 12 
 		}
 13 Block: 1/1 
 		catch (NumberFormatException exc) {
 14 
 			return 0.0;
 15 
 		}
 16 
 	}
 17 
 	
 18 
 	public static int parseInt(String string) {
 19 Block: 1/1 
 		if ((null == string) || ("".equals(string))) {
 20 Block: 1/1 Branch: 2/2 
 			return 0;
 21 
 		}
 22 
 		try {
 23 Block: 1/1 Branch: 1/1 
 			return Integer.parseInt(string);
 24 
 		}
 25 Block: 1/1 
 		catch (NumberFormatException exc) {
 26 
 			return 0;
 27 
 		}
 28 
 		
 29 
 	}
 30 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/util/StringUtils.html b/report/net/jaekl/qd/util/StringUtils.html new file mode 100644 index 0000000..57edcc4 --- /dev/null +++ b/report/net/jaekl/qd/util/StringUtils.html @@ -0,0 +1,156 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.util.StringUtils
50%(1/2)
86%(6/7)
100%(4/4)
75%(3/4)
+

+

+ + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
0<init>[public]void <init>()
18areEqual[public, static]boolean areEqual(java.lang.String,java.lang.String)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.qd.util;
 2 
 
 3 Block: 0/1 
 public class StringUtils {
 4 
 	public static boolean areEqual(String a, String b) {
 5 Block: 1/1 
 		if (null == a) {
 6 Block: 1/1 Branch: 1/1 
 			return (null == b);
 7 
 		}
 8 Block: 1/1 Branch: 1/1 
 		return a.equals(b);
 9 
 	}
 10 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/util/package-frame.html b/report/net/jaekl/qd/util/package-frame.html new file mode 100644 index 0000000..106ba8d --- /dev/null +++ b/report/net/jaekl/qd/util/package-frame.html @@ -0,0 +1,20 @@ + + +coverage report + + + +net.jaekl.qd.util   57% (4/7)
+

+All classes + + + + +
+ExceptionUtils  50% (1/2)
+ParseUtils  67% (2/3)
+StringUtils  50% (1/2)
+
+ + diff --git a/report/net/jaekl/qd/util/package-summary.html b/report/net/jaekl/qd/util/package-summary.html new file mode 100644 index 0000000..8203e3f --- /dev/null +++ b/report/net/jaekl/qd/util/package-summary.html @@ -0,0 +1,62 @@ + + +coverage report + + + + +Coverage report +

+ + + + + + + + + + + + + + + + + +
 #classes%method%block%branch%line
net.jaekl.qd.util3
57%(4/7)
88%(21/24)
100%(14/14)
86%(19/22)
+

+Classes
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name%method%block%branch%line
ExceptionUtils 50% (1/2) 83% (5/6) 100% (2/2) 86% (6/7)
ParseUtils 67% (2/3) 91% (10/11) 100% (8/8) 91% (10/11)
StringUtils 50% (1/2) 86% (6/7) 100% (4/4) 75% (3/4)
+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/xml/MismatchedTagsException.html b/report/net/jaekl/qd/xml/MismatchedTagsException.html new file mode 100644 index 0000000..7a540c2 --- /dev/null +++ b/report/net/jaekl/qd/xml/MismatchedTagsException.html @@ -0,0 +1,217 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.xml.MismatchedTagsException
0%(0/3)
0%(0/3)
-%
0%(0/6)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
0<init>[public]void <init>(java.lang.String,java.lang.String)
0getActual[public]java.lang.String getActual()
0getExpected[public]java.lang.String getExpected()
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 // Copyright (C) 2004, 2014 Christian Jaekl
 2 
 
 3 
 package net.jaekl.qd.xml;
 4 
 
 5 
 
 6 
 public class MismatchedTagsException extends XmlParseException
 7 
 {
 8 
 	private static final long serialVersionUID = 1L;
 9 
 
 10 
 	String m_expected;
 11 
 	String m_actual;
 12 
 
 13 
 	public MismatchedTagsException(String expected, String actual) {
 14 Block: 0/1 
 		super();
 15 
 		m_expected = expected;
 16 
 		m_actual = actual;
 17 
 	}
 18 
 
 19 Block: 0/1 
 	public String getExpected() { return m_expected; }
 20 Block: 0/1 
 	public String getActual() { return m_actual; }
 21 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/xml/MissingInfoException.html b/report/net/jaekl/qd/xml/MissingInfoException.html new file mode 100644 index 0000000..bdfb71a --- /dev/null +++ b/report/net/jaekl/qd/xml/MissingInfoException.html @@ -0,0 +1,359 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.xml.MissingInfoException
100%(5/5)
100%(11/11)
100%(4/4)
100%(19/19)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
3<init>[public]void <init>(java.lang.String)
2addMissingAttribute[public]void addMissingAttribute(java.lang.String)
2addMissingChild[public]void addMissingChild(java.lang.String)
3getMessage[public]java.lang.String getMessage()
4getTagName[public]java.lang.String getTagName()
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 // Copyright (C) 2004, 2014 Christian Jaekl
 2 
 
 3 
 package net.jaekl.qd.xml;
 4 
 
 5 
 import java.util.ArrayList;
 6 
 
 7 
 public class MissingInfoException extends XmlParseException
 8 
 {
 9 
 	private static final long serialVersionUID = 1L;
 10 
 
 11 
 	String m_tagName;
 12 
 	ArrayList<String> m_missingAttributes;
 13 
 	ArrayList<String> m_missingChildTags;
 14 
 
 15 
 	public MissingInfoException(String tagName) {
 16 Block: 1/1 
 		super();
 17 
 		m_tagName = tagName;
 18 
 		m_missingAttributes = new ArrayList<String>();
 19 
 		m_missingChildTags = new ArrayList<String>();
 20 
 	}
 21 
 
 22 
 	public void addMissingAttribute(String name) {
 23 Block: 1/1 
 		m_missingAttributes.add(name);
 24 
 	}
 25 
 
 26 
 	public void addMissingChild(String name) {
 27 Block: 1/1 
 		m_missingChildTags.add(name);
 28 
 	}
 29 
 
 30 Block: 1/1 
 	public String getTagName() { return m_tagName; }
 31 
 	
 32 
 	@Override
 33 
 	public String getMessage() {
 34 Block: 1/1 
 		StringBuilder sb = new StringBuilder();
 35 
 		
 36 
 		sb.append("Tag:  \"" + getTagName() + "\"");
 37 
 		
 38 
 		for (String attr : m_missingAttributes) {
 39 Block: 1/1 Branch: 1/1 
 			sb.append("\n  Attribute:  \"" + attr + "\"");
 40 
 		}
 41 
 		
 42 Block: 1/1 Branch: 1/1 
 		for (String child : m_missingChildTags) {
 43 Block: 1/1 Branch: 1/1 
 			sb.append("\n  Child tag:  \"" + child + "\"");
 44 
 		}
 45 Block: 1/1 Branch: 1/1 
 		return sb.toString();
 46 
 	}
 47 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/xml/ParseHandler.html b/report/net/jaekl/qd/xml/ParseHandler.html new file mode 100644 index 0000000..f104550 --- /dev/null +++ b/report/net/jaekl/qd/xml/ParseHandler.html @@ -0,0 +1,781 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.xml.ParseHandler
75%(9/12)
80%(24/30)
100%(12/12)
79%(33/42)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
12<init>[public]void <init>(net.jaekl.qd.xml.ParseResult)
204characters[public]void characters(char[],int,int)
12endDocument[public]void endDocument()
235endElement[public]void endElement(java.lang.String,java.lang.String,java.lang.String)
48endPrefixMapping[public]void endPrefixMapping(java.lang.String)
0ignorableWhitespace[public]void ignorableWhitespace(char[],int,int)
0processingInstruction[public]void processingInstruction(java.lang.String,java.lang.String)
12setDocumentLocator[public]void setDocumentLocator(org.xml.sax.Locator)
0skippedEntity[public]void skippedEntity(java.lang.String)
12startDocument[public]void startDocument()
235startElement[public]void startElement(java.lang.String,java.lang.String,java.lang.String,org.xml.sax.Attributes)
48startPrefixMapping[public]void startPrefixMapping(java.lang.String,java.lang.String)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 // Copyright (C) 2004, 2014 Christian Jaekl
 2 
 
 3 
 package net.jaekl.qd.xml;
 4 
 
 5 
 import java.util.Stack;
 6 
 
 7 
 import org.xml.sax.Attributes;
 8 
 import org.xml.sax.ContentHandler;
 9 
 import org.xml.sax.Locator;
 10 
 import org.xml.sax.SAXException;
 11 
 
 12 
 public class ParseHandler implements ContentHandler
 13 
 {
 14 
 	Stack<ParseResult> m_stack;
 15 
 
 16 Block: 1/1 
 	public ParseHandler(ParseResult root) {
 17 
 		m_stack = new Stack<ParseResult>();
 18 
 		m_stack.push(root);
 19 
 	}
 20 
 
 21 
 	@Override 
 22 
 	public void characters(char[] ch, int start, int length) throws SAXException
 23 
 	{
 24 Block: 1/1 
 		if (m_stack.isEmpty()) {
 25 Block: 1/1 Branch: 1/1 
 			return;
 26 
 		}
 27 
 		
 28 
 		try {
 29 Block: 1/1 Branch: 1/1 
 			m_stack.peek().characters(ch, start, length);
 30 
 		}
 31 Block: 0/1 
 		catch (XmlParseException xpe) {
 32 
 			throw new SAXException(xpe);
 33 
 		}
 34 Block: 1/1 
 	}
 35 
 
 36 
 	@Override 
 37 
 	public void endElement(String uri, String localName, String qName) throws SAXException
 38 
 	{
 39 
 		try {
 40 Block: 1/1 
 			if (m_stack.isEmpty()) {
 41 Block: 1/1 Branch: 1/1 
 				return;
 42 
 			}
 43 
 			
 44 Block: 1/1 Branch: 1/1 
 			boolean pop = m_stack.peek().endElement(uri, localName, qName);
 45 
 			if (pop) {
 46 Block: 1/1 Branch: 1/1 
 				m_stack.pop();
 47 
 
 48 
 				if (m_stack.isEmpty()) {
 49 Block: 1/1 Branch: 1/1 
 					return;
 50 
 				}
 51 
 				
 52 Block: 1/1 Branch: 1/1 
 				m_stack.peek().endExternal(uri, localName, qName);
 53 
 			}
 54 
 		}
 55 Block: 0/1 
 		catch (XmlParseException xpe) {
 56 
 			throw new SAXException(xpe);
 57 Block: 1/1 Branch: 1/1 
 		}
 58 Block: 1/1 
 	}
 59 
 
 60 
 	@Override
 61 
 	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
 62 
 	{
 63 
 		try {
 64 Block: 1/1 
 			ParseResult current = m_stack.peek();
 65 
 			ParseResult next = current.startElement(uri, localName, qName, attributes);
 66 
 			if (next != current) {
 67 Block: 1/1 Branch: 1/1 
 				m_stack.push(next);
 68 
 			}
 69 
 		}
 70 Block: 0/1 
 		catch (XmlParseException xpe) {
 71 
 			throw new SAXException(xpe);
 72 Block: 1/1 Branch: 1/1 
 		}
 73 Block: 1/1 
 	}
 74 
 
 75 
 	@Override
 76 
 	public void endDocument() throws SAXException {
 77 Block: 1/1 
 		if (! m_stack.isEmpty()) {
 78 Block: 1/1 Branch: 1/1 
 			String missingTag = m_stack.peek().getTagName();
 79 
 			throw new SAXException(new MissingInfoException(missingTag));
 80 
 		}
 81 Block: 1/1 Branch: 1/1 
 	}
 82 
 
 83 
 	@Override
 84 
 	public void endPrefixMapping(String prefix) throws SAXException {
 85 
 		// no-op
 86 Block: 1/1 
 	}
 87 
 
 88 
 	@Override
 89 
 	public void ignorableWhitespace(char[] ch, int start, int length)
 90 
 	throws SAXException 
 91 
 	{
 92 
 		// no-op
 93 Block: 0/1 
 	}
 94 
 
 95 
 	@Override
 96 
 	public void processingInstruction(String target, String data)
 97 
 	throws SAXException 
 98 
 	{
 99 
 		// no-op
 100 Block: 0/1 
 	}
 101 
 
 102 
 	@Override
 103 
 	public void setDocumentLocator(Locator locator) {
 104 
 		// no-op
 105 Block: 1/1 
 	}
 106 
 
 107 
 	@Override
 108 
 	public void skippedEntity(String name) throws SAXException {
 109 
 		// no-op
 110 Block: 0/1 
 	}
 111 
 
 112 
 	@Override
 113 
 	public void startDocument() throws SAXException {
 114 
 		// no-op
 115 Block: 1/1 
 	}
 116 
 
 117 
 	@Override
 118 
 	public void startPrefixMapping(String prefix, String uri)
 119 
 	throws SAXException 
 120 
 	{
 121 
 		// no-op
 122 Block: 1/1 
 	}
 123 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/xml/ParseResult.html b/report/net/jaekl/qd/xml/ParseResult.html new file mode 100644 index 0000000..c96b93a --- /dev/null +++ b/report/net/jaekl/qd/xml/ParseResult.html @@ -0,0 +1,978 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.xml.ParseResult
89%(8/9)
81%(38/47)
74%(25/34)
91%(60/66)
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
1<clinit>[static]void <clinit>()
45<init>[public]void <init>(java.lang.String,java.lang.String[],java.lang.Object[][])
195characters[public]void characters(char[],int,int)
20collectParsedChildren[protected]net.jaekl.qd.xml.ParseResult[] collectParsedChildren(java.lang.Class)
216endElement[public]boolean endElement(java.lang.String,java.lang.String,java.lang.String)
1getTagName[public]java.lang.String getTagName()
0haveSeenMyTag[public]boolean haveSeenMyTag()
255startElement[public]net.jaekl.qd.xml.ParseResult startElement(java.lang.String,java.lang.String,java.lang.String,org.xml.sax.Attributes)
31validate[public]void validate()
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 // Copyright (C) 2004, 2014 Christian Jaekl
 2 
 
 3 
 // Abstract class representing the result of parsing an XML Element.
 4 
 // A class derived from this one will know how to parse a subtree inside an XML file, and 
 5 
 // will contain the result of that parse within itself when the parse has completed.
 6 
 //
 7 
 // Note that this code will need to be augmented and fixed if XML namespace support is desired.
 8 
 
 9 
 package net.jaekl.qd.xml;
 10 
 
 11 
 import java.util.ArrayList;
 12 
 import java.util.HashMap;
 13 
 import java.util.HashSet;
 14 
 import java.util.Iterator;
 15 
 import java.util.Stack;
 16 
 
 17 
 import org.xml.sax.Attributes;
 18 
 
 19 Block: 1/1 
 public abstract class ParseResult
 20 
 {
 21 
 	Stack<String> m_current;									// Name of the element that we're currently inside
 22 
 	StringBuilder m_chars;										// character content of m_current.peek()
 23 
 	ArrayList<ParseResult> m_childParsers;						// Set of all child parsers
 24 
 	boolean m_haveSeenMyTag;									// Have I encountered my own (root) tag yet?
 25 
 
 26 
 	String m_tagName;											// Name of the (root) element tag that I'm parsing
 27 
 	HashSet<String> m_internal;									// Tags that we will store as members of this class instance
 28 
 	HashMap<String,Class<? extends ParseResult>> m_external;	// Tags that we will store as child ParseResult-derived objects
 29 
 
 30 
 	@SuppressWarnings("unchecked")
 31 
 	public ParseResult(String tagName, String[] internalMemberTags, Object[][] externalParserTags)
 32 Block: 1/1 
 	{
 33 
 		m_current = new Stack<String>();
 34 
 		m_chars = new StringBuilder();
 35 
 		m_childParsers = new ArrayList<ParseResult>();
 36 
 		m_haveSeenMyTag = false;
 37 
 		
 38 
 		m_tagName = tagName;
 39 
 		m_internal = new HashSet<String>();
 40 
 		m_external = new HashMap<String, Class<? extends ParseResult>>();
 41 
 
 42 
 		for (String internalTag : internalMemberTags) {
 43 Block: 1/1 Branch: 1/1 
 			m_internal.add(internalTag);
 44 
 		}
 45 
 
 46 Block: 1/1 Branch: 1/1 
 		for (int idx = 0; idx < externalParserTags.length; ++idx) {
 47 Block: 1/1 Branch: 1/1 
 			String externalTag = (String)externalParserTags[idx][0];
 48 
 			Class<? extends ParseResult>  parserClass = (Class<? extends ParseResult>)externalParserTags[idx][1];
 49 
 			m_external.put(externalTag, parserClass);
 50 
 		}
 51 Block: 1/1 Branch: 1/1 
 	}
 52 
 
 53 
 	public abstract void endContents(String uri, String localName, String qName, String chars) throws XmlParseException;
 54 
 	public abstract void endExternal(String uri, String localName, String qName) throws XmlParseException;
 55 
 	
 56 Block: 1/1 
 	public String getTagName() { return m_tagName; }
 57 Block: 0/1 
 	public boolean haveSeenMyTag() { return m_haveSeenMyTag; }
 58 
 
 59 
 	public void characters(char[] ch, int start, int length) throws XmlParseException
 60 
 	{
 61 Block: 1/1 
 		m_chars.append(ch, start, length);
 62 
 	}
 63 
 	
 64 
 	protected ParseResult[] collectParsedChildren(Class<? extends ParseResult> cls) {
 65 Block: 1/1 
 		ArrayList<ParseResult> collection = new ArrayList<ParseResult>();
 66 
 		Iterator<ParseResult> iter = m_childParsers.iterator();
 67 Block: 1/1 
 		while (iter.hasNext()) {
 68 Block: 1/1 Branch: 1/1 
 			ParseResult pr = iter.next();
 69 
 			if (pr.getClass().isAssignableFrom(cls)) {
 70 Block: 1/1 Branch: 1/1 
 				collection.add(pr);
 71 
 				iter.remove();
 72 
 			}
 73 Block: 1/1 Branch: 0/1 
 		}
 74 
 		
 75 Block: 1/1 Branch: 1/1 
 		ParseResult[] result = new ParseResult[collection.size()];
 76 
 		return collection.toArray(result);
 77 
 	}
 78 
 
 79 
 	// returns true if this ParseResult's context has ended with this endElement() call
 80 
 	public boolean endElement(String uri, String localName, String qName) throws XmlParseException
 81 
 	{
 82 Block: 1/1 
 		assert (null != localName);
 83 
 		
 84 Block: 1/1 Branch: 1/2 
 		boolean isInternal = m_internal.contains(localName);
 85 
 
 86 
 		if (! m_haveSeenMyTag) {
 87 
 			// We're in some unrecognised prologue.  Ignore it and move on.
 88 Block: 1/1 Branch: 1/1 
 			return false;
 89 
 		}
 90 
 		
 91 Block: 1/1 Branch: 1/1 
 		if (m_tagName.equals(localName)) {
 92 Block: 1/1 Branch: 1/1 
 			validate();
 93 
 			return true;
 94 
 		}
 95 
 		
 96 Block: 1/1 Branch: 1/1 
 		if (!isInternal) {
 97 
 			// Unrecognized tag.  Ignore it.
 98 Block: 1/1 Branch: 1/1 
 			return false;
 99 
 		}
 100 
 		
 101 Block: 1/1 Branch: 1/1 
 		String tag = m_current.pop();
 102 
 		if ( ! tag.equals(localName) ) {
 103 Block: 0/1 Branch: 0/1 
 			throw new MismatchedTagsException(tag, localName);
 104 
 		}
 105 
 		
 106 Block: 1/1 Branch: 1/1 
 		String chars = m_chars.toString();
 107 
 		endContents(uri, localName, qName, chars);
 108 
 		
 109 
 		return false;
 110 
 	}
 111 
 	
 112 
 	// returns either itself, or a new ParseResult-derived object, whichever should handle parsing the inside of this element
 113 
 	public ParseResult startElement(String uri, String localName, String qName, Attributes attributes) 
 114 
 			throws XmlParseException
 115 
 	{
 116 Block: 1/1 
 		assert (null != localName);
 117 
 
 118 Block: 1/1 Branch: 1/2 
 		m_chars.setLength(0);
 119 
 		
 120 
 		if (! m_haveSeenMyTag) {
 121 
 			// Have we opened our own (root) tag yet?
 122 
 			
 123 Block: 1/1 Branch: 1/1 
 			if (m_tagName.equals(localName)) {
 124 Block: 1/1 Branch: 1/1 
 				m_haveSeenMyTag = true;
 125 
 				return this;
 126 
 			}
 127 
 			else {
 128 
 				// One of two things has happened here.
 129 
 				// Either (a) we've got some sort of wrapper here, and have not yet reach our own tag, 
 130 
 				//     or (b) we're parsing XML that doesn't match expectations.
 131 
 				// In either case, we're going to ignore this tag, and scan forward looking for our own root.
 132 Block: 1/1 Branch: 1/1 
 				return this;
 133 
 			}
 134 
 		}
 135 
 
 136 Block: 1/1 Branch: 1/1 
 		if (m_internal.contains(localName)) {
 137 Block: 1/1 Branch: 1/1 
 			m_current.push(localName);
 138 
 			return this;
 139 
 		}
 140 
 
 141 Block: 1/1 Branch: 1/1 
 		Class<? extends ParseResult> parserClass = m_external.get(localName);
 142 
 		if (null != parserClass) {
 143 
 			try {
 144 Block: 1/1 Branch: 1/1 
 				ParseResult childParser = (ParseResult) parserClass.newInstance();
 145 
 				m_childParsers.add(childParser);
 146 
 				return childParser.startElement(uri, localName, qName, attributes);
 147 
 			}
 148 Block: 0/1 
 			catch (IllegalAccessException iae) {
 149 
 				throw new XmlParseException(iae);
 150 
 			}
 151 Block: 0/1 
 			catch (InstantiationException ie) {
 152 
 				throw new XmlParseException(ie);
 153 
 			}
 154 
 		}
 155 
 		
 156 
 		// Not a recognized tag.  Ignore it, rather than complaining. 
 157 Block: 1/1 Branch: 1/1 
 		return this;
 158 
 	}
 159 
 	
 160 
 	public void validate() throws XmlParseException
 161 
 	{
 162 
 		// Default implementation is a no-op.
 163 
 		// Override if you want to validate on endElement()
 164 Block: 1/1 
 	}
 165 
 }
 166 
 
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/xml/XmlParseException.html b/report/net/jaekl/qd/xml/XmlParseException.html new file mode 100644 index 0000000..94ba0aa --- /dev/null +++ b/report/net/jaekl/qd/xml/XmlParseException.html @@ -0,0 +1,191 @@ + + +tests coverage + + + + + + + + + +
+Frames +No Frames +
+

+ + + + + + + + + + + + + + + +
 %method%block%branch%line
net.jaekl.qd.xml.XmlParseException
50%(1/2)
50%(1/2)
-%
50%(2/4)
+

+

+ + + + + + + + + + + + + + + + + + + +
hit countmethod namemethod modifiersmethod signature
3<init>[public]void <init>()
0<init>[public]void <init>(java.lang.Throwable)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 1 
 package net.jaekl.qd.xml;
 2 
 
 3 
 import net.jaekl.qd.QDException;
 4 
 
 5 
 public class XmlParseException extends QDException 
 6 
 {
 7 
 	private static final long serialVersionUID = 1L;
 8 
 
 9 Block: 1/1 
 	public XmlParseException() {
 10 
 		// no-op
 11 
 	}
 12 
 	
 13 
 	public XmlParseException(Throwable t) {
 14 Block: 0/1 
 		super(t);
 15 
 	}
 16 
 
 17 
 }
+

+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/net/jaekl/qd/xml/package-frame.html b/report/net/jaekl/qd/xml/package-frame.html new file mode 100644 index 0000000..b2f92f2 --- /dev/null +++ b/report/net/jaekl/qd/xml/package-frame.html @@ -0,0 +1,22 @@ + + +coverage report + + + +net.jaekl.qd.xml   74% (23/31)
+

+All classes + + + + +
+MismatchedTagsException  0% (0/3)
+MissingInfoException  100% (5/5)
+ParseHandler  75% (9/12)
+ParseResult  89% (8/9)
+XmlParseException  50% (1/2)
+
+ + diff --git a/report/net/jaekl/qd/xml/package-summary.html b/report/net/jaekl/qd/xml/package-summary.html new file mode 100644 index 0000000..492a571 --- /dev/null +++ b/report/net/jaekl/qd/xml/package-summary.html @@ -0,0 +1,76 @@ + + +coverage report + + + + +Coverage report +

+ + + + + + + + + + + + + + + + + +
 #classes%method%block%branch%line
net.jaekl.qd.xml5
74%(23/31)
80%(74/93)
82%(41/50)
83%(114/137)
+

+Classes
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name%method%block%branch%line
MismatchedTagsException 0% (0/3) 0% (0/3) - 0% (0/6)
MissingInfoException 100% (5/5) 100% (11/11) 100% (4/4) 100% (19/19)
ParseHandler 75% (9/12) 80% (24/30) 100% (12/12) 79% (33/42)
ParseResult 89% (8/9) 81% (38/47) 74% (25/34) 91% (60/66)
XmlParseException 50% (1/2) 50% (1/2) - 50% (2/4)
+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/overview-frame.html b/report/overview-frame.html new file mode 100644 index 0000000..3c499f1 --- /dev/null +++ b/report/overview-frame.html @@ -0,0 +1,33 @@ + + +coverage report + + + +Coverage report
+ + + + +
+Overview
+All classes +
+

+ + + + + + + +
All packages
+net.jaekl.frank
+net.jaekl.frank.octranspo
+net.jaekl.qd
+net.jaekl.qd.http
+net.jaekl.qd.util
+net.jaekl.qd.xml
+
+ + diff --git a/report/overview-summary.html b/report/overview-summary.html new file mode 100644 index 0000000..b3792ff --- /dev/null +++ b/report/overview-summary.html @@ -0,0 +1,90 @@ + + +coverage report + + + + +Coverage report +

+ + + + + + + + + + + + + + + + + +
 #classes%method%block%branch%line
Overall statistics21
86%(95/111)
77%(253/329)
72%(127/176)
82%(443/539)
+

+Packages
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name#classes%method%block%branch%line
net.jaekl.frank4 83% (15/18) 64% (38/59) 57% (16/28) 77% (114/148)
net.jaekl.frank.octranspo6 98% (43/44) 84% (97/115) 70% (49/70) 94% (150/159)
net.jaekl.qd2 100% (5/5) 90% (9/10) 75% (3/4) 100% (15/15)
net.jaekl.qd.http1 83% (5/6) 50% (14/28) 40% (4/10) 53% (31/58)
net.jaekl.qd.util3 57% (4/7) 88% (21/24) 100% (14/14) 86% (19/22)
net.jaekl.qd.xml5 74% (23/31) 80% (74/93) 82% (41/50) 83% (114/137)
+

Report generated 11/12/14 11:31 PM
+ + diff --git a/report/sorttable.js b/report/sorttable.js new file mode 100644 index 0000000..6d2bf5f --- /dev/null +++ b/report/sorttable.js @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +addEvent(window, "load", sortables_init); + +var SORT_COLUMN_INDEX; + +function sortables_init() { + // Find all tables with class sortable and make them sortable + if (!document.getElementsByTagName) return; + tbls = document.getElementsByTagName("table"); + for (ti=0;ti 0) { + var firstRow = table.rows[0]; + } + if (!firstRow) return; + + // We have a first row: assume it's the header, and make its contents clickable links + for (var i=0;i' + + txt+'   '; + } +} + +function ts_getInnerText(el) { + if (typeof el == "string") return el; + if (typeof el == "undefined") { return el }; + if (el.innerText) return el.innerText; //Not needed but it is faster + var str = ""; + + var cs = el.childNodes; + var l = cs.length; + for (var i = 0; i < l; i++) { + switch (cs[i].nodeType) { + case 1: //ELEMENT_NODE + str += ts_getInnerText(cs[i]); + break; + case 3: //TEXT_NODE + str += cs[i].nodeValue; + break; + } + } + return str; +} + +function ts_resortTable(lnk,clid) { + // get the span + var span; + for (var ci=0;ci + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + + 0=31;11=32;16=33;22=34;33=35;38=36;47=38;56=39;66=41; +
+ + + + + + 0=44;4=45;17=46; + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=50;8=51;17=58;20=54;21=56;25=59; +
+ + + + + + 0=26; + +
+ + + + + + + 0=9;5=10; + + + + + + + + + 0=20;4=21;9=22;17=23;31=24;45=25; + + + + + + + 0=28; + + + + + + + 0=32; + + + + + + + 0=36;6=37;12=38;18=39;24=40;30=41;36=42;42=43; + + + + + + + 0=47;8=48;16=50;22=51;28=52;56=53;62=54;68=55;74=56;113=57;141=58;147=59;153=60;159=61;165=62;171=63;177=64;210=65;216=66;222=67;228=68;234=70; + + + + + + + 0=73;6=74;12=75;40=76;45=77;51=78; + + + + + + + +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + +
+ + +
+
+ + + + + + + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + 0=82;10=83;13=85;49=86;57=88;91=90;97=91;103=92;132=93;221=105;233=106;241=107;254=108;263=109;282=110;287=112;296=114;302=116;334=117;366=118;405=120;452=121;457=122;464=124;474=125;477=127;487=128;523=131;584=134;590=107;596=105;602=138;608=140;616=141;644=142;673=144;680=145;691=146;697=147; +
+
+ + + + + + + 0=19; + + + + + + + + + + + 0=28;7=30;12=32;14=34; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + 0=39;8=40;22=42;24=44; +
+ + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + 0=48;2=49;10=50;15=51;24=53;29=54;33=56; +
+ + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0=63;8=64;15=66;22=67;31=68;40=71;51=73;64=74;67=76;73=77;85=80;96=82;107=84;115=87;120=88;123=87;133=92;136=90;138=91;147=93; +
+ + + + + + 0=96;9=97;20=98;26=100;75=104;124=116;130=117;137=118;143=120;149=121;155=122; + +
+
+ + + + + + + + 0=9;12=10; + + + + + + + 0=5;31=6; + + + + + + + + + 0=29;12=30;17=31;22=32;28=33;34=34;45=35; + + + + + + + 0=37; + + + + + + + 0=38; + + + + + + + 0=39; + + + + + + + 0=40; + + + + + + + 0=41; + + + + + + + 0=42; + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + 0=47;9=48;21=50;30=51;42=53;51=54;60=56;78=57;84=59; +
+ + + +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + 0=64;9=65;18=66;44=67;66=68;79=66;85=71; +
+ + + +
+ + +
+
+ + + + + + + + + + + + 0=7;17=19;55=20; +
+
+ + + + + + + 0=9;12=10; + + + + + + + 0=5;31=6; + + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + 0=33;4=34;23=35;42=37;58=38;64=40;75=41;93=42;112=44;129=45; +
+ + + + + + + + + + 0=51;8=52;29=53;46=55;47=56; + + + + + + + + + + + 0=64;8=65;29=66;50=67;67=69;68=70; + + + + + + + + + + + 0=78;8=79;29=80;46=82;47=83; + + + + +
+ + +
+
+ + + + + + + + + + + + 0=16; +
+
+ + + + + + + 0=26;7=28;12=29;18=30;24=31;35=32; + + + + + + + 0=37; + + + + + + + 0=38; + + + + + + + 0=39; + + + + + + + 0=40; + + + + + + + 0=41; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + + 0=49;19=51;29=52;41=54;61=55;70=57;80=58;86=60; +
+ + + +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + 0=65;10=66;19=67;45=68;67=69;80=67;86=72; +
+ + + +
+ + +
+
+ + + + + + + + + + + + 0=8;17=11;22=12;27=13;32=14;37=15;42=16; +
+
+ + + + + + + 0=50;12=51;18=52;29=53;34=54;39=55;44=56;50=57;55=58;60=59;65=61;78=62;89=63; + + + + + + + 0=66;12=67;20=68;28=69;36=70;44=71;52=72;60=73;68=74;76=75;84=76;92=77; + + + + + + + 0=79; + + + + + + + 0=80; + + + + + + + 0=81; + + + + + + + 0=82; + + + + + + + 0=83; + + + + + + + 0=84; + + + + + + + 0=85; + + + + + + + 0=86; + + + + + + + 0=87; + + + + + + + 0=90; + + + + + + + 0=93; + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=102;9=103;18=105;27=106;43=108;52=109;64=111;73=112;85=114;94=115;106=117;115=118;124=120;133=121;145=123;154=124;166=126;175=127;184=131;187=129;189=130;199=132; +
+ + + + + + 0=138; + + + + + + + 0=25;56=34; + +
+
+ + + + + + + + 0=20;4=22; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + + 0=25;4=26;9=27;15=28;22=29;32=31;36=32;46=34; +
+ + + + + + 0=38; + +
+ + + + + + + 0=8;4=9; + + + + + + + 0=12;5=13; + + +
+ + + + + + + + 0=40;4=33;10=34;17=41;22=42;34=43; + + + + +
+ + +
+
+ + + + + + + + + + +
+ + +
+
+ + + + + + + + + + + + + + 0=49;12=50;18=53;23=54;33=55;43=56;48=57;59=58;66=59;106=60;121=61;132=62;141=64;147=65;156=66;159=77;162=69;164=72;178=73;188=75;190=76;200=79; +
+ + + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + 0=84;8=85;11=88;19=89;37=90;44=91;50=92;62=93;72=100;77=101;80=96;82=97;92=100;102=103; +
+ + + + + + 0=111; + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + 0=120;3=121;6=124;12=125;24=128;55=130;63=131;68=132;79=133;88=134;104=147;109=148;112=136;114=144;124=147;134=150; +
+ + + +
+ + +
+
+ + + + + + + + + + + + 0=31; +
+
+
+ + + + + + + + 0=8; + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=11;5=12;11=17;14=15;15=16;24=18; +
+
+ + + + + + + 0=3; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + 0=7;14=8;16=11;21=13;22=14; +
+ + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + 0=19;14=20;16=23;21=25;22=26; +
+
+ + + + + + + 0=3; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + 0=5;5=6;16=8; +
+
+
+ + + + + + + + 0=14;4=15;9=16;14=17; + + + + + + + 0=19; + + + + + + + 0=20; + + + + + + + + + 0=16;4=17;9=18;20=19;31=20; + + + + + + + 0=23;9=24; + + + + + + + 0=27;9=28; + + + + + + + 0=30; + + + + + + + +
+ + +
+
+ + + + + + + + + +
+ + +
+
+ + + + + + + + + 0=34;8=36;40=38;67=39;96=40;99=42;126=43;155=44;158=45; +
+
+ + + + + + + 0=16;4=17;15=18;24=19; + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=24;10=25;11=29;27=33;30=31;32=32;42=34; +
+ + + +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + + + + + 0=40;10=41;11=44;29=45;34=46;42=48;52=49;53=52;69=57;72=55;74=56;84=58; +
+ + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=64;12=65;24=66;31=67;41=72;44=70;46=71;56=73; +
+ + + +
+ + +
+
+ + + + + + + 0=77;10=78;24=79;40=81; +
+ + + + + + 0=86; + + + + + + + 0=93; + + + + + + + 0=100; + + + + + + + 0=105; + + + + + + + 0=110; + + + + + + + 0=115; + + + + + + + 0=122; + +
+ + + + + + + +
+ + +
+
+ + + + + + + + + +
+ + +
+
+ + + + + + + + + 0=32;4=33;15=34;26=35;37=36;42=38;47=39;58=40;69=42;94=43;104=42;110=46;120=47;131=48;142=49;154=46;160=51; +
+ + + + + + 0=56; + + + + + + + 0=57; + + + + + + + 0=61;11=62; + + + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + 0=65;8=66;16=67;25=68;36=69;48=70;55=71;61=73;64=75;73=76; +
+ + + +
+ + +
+
+ +
+ + +
+
+ + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + 0=82;19=84;29=86;36=88;38=91;49=92;53=93;55=96;60=98;62=101;74=102;83=103;94=106;103=107;112=109; +
+ + + +
+ + +
+
+ +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + + + + + + + 0=116;19=118;27=120;34=123;45=124;50=125;52=132;54=136;65=137;74=138;76=141;89=142;95=144;105=145;115=146;126=148;128=149;138=151;140=152;150=157; +
+ + + + + + 0=164; + + + + +
+ + +
+
+ + + + + + + + + + + + 0=19; +
+
+ + + + + + + 0=9;4=11; + + + + + + + 0=14;5=15; + + +
+
diff --git a/setcp.sh b/setcp.sh new file mode 100644 index 0000000..255f177 --- /dev/null +++ b/setcp.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +export CLASSPATH=${SCRIPT_DIR}/WEB-INF/classes + +for x in ${SCRIPT_DIR}/WEB-INF/lib/*.jar +do + export CLASSPATH=${x}:${CLASSPATH} +done + +export CLASSPATH=/usr/share/java/jenkins-winstone.jar:${CLASSPATH} +export CLASSPATH=/usr/share/java/junit4.jar:${CLASSPATH} +#export CLASSPATH=${SCRIPT_DIR}/jcov/jcov.jar:${CLASSPATH} diff --git a/template.xml b/template.xml new file mode 100644 index 0000000..04f10a7 --- /dev/null +++ b/template.xml @@ -0,0 +1,1898 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + + 0=31;11=32;16=33;22=34;33=35;38=36;47=38;56=39;66=41; +
+ + + + + + 0=44;4=45;17=46; + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=50;8=51;17=58;20=54;21=56;25=59; +
+ + + + + + 0=26; + +
+ + + + + + + 0=9;5=10; + + + + + + + + + 0=20;4=21;9=22;17=23;31=24;45=25; + + + + + + + 0=28; + + + + + + + 0=32; + + + + + + + 0=36;6=37;12=38;18=39;24=40;30=41;36=42;42=43; + + + + + + + 0=47;8=48;16=50;22=51;28=52;56=53;62=54;68=55;74=56;113=57;141=58;147=59;153=60;159=61;165=62;171=63;177=64;210=65;216=66;222=67;228=68;234=70; + + + + + + + 0=73;6=74;12=75;40=76;45=77;51=78; + + + + + + + +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + +
+ + +
+
+ + + + + + + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + 0=82;10=83;13=85;49=86;57=88;91=90;97=91;103=92;132=93;221=105;233=106;241=107;254=108;263=109;282=110;287=112;296=114;302=116;334=117;366=118;405=120;452=121;457=122;464=124;474=125;477=127;487=128;523=131;584=134;590=107;596=105;602=138;608=140;616=141;644=142;673=144;680=145;691=146;697=147; +
+
+ + + + + + + 0=19; + + + + + + + + + + + 0=28;7=30;12=32;14=34; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + 0=39;8=40;22=42;24=44; +
+ + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + 0=48;2=49;10=50;15=51;24=53;29=54;33=56; +
+ + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0=63;8=64;15=66;22=67;31=68;40=71;51=73;64=74;67=76;73=77;85=80;96=82;107=84;115=87;120=88;123=87;133=92;136=90;138=91;147=93; +
+ + + + + + 0=96;9=97;20=98;26=100;75=104;124=116;130=117;137=118;143=120;149=121;155=122; + +
+
+ + + + + + + + 0=9;12=10; + + + + + + + 0=5;31=6; + + + + + + + + + 0=29;12=30;17=31;22=32;28=33;34=34;45=35; + + + + + + + 0=37; + + + + + + + 0=38; + + + + + + + 0=39; + + + + + + + 0=40; + + + + + + + 0=41; + + + + + + + 0=42; + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + 0=47;9=48;21=50;30=51;42=53;51=54;60=56;78=57;84=59; +
+ + + +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + 0=64;9=65;18=66;44=67;66=68;79=66;85=71; +
+ + + +
+ + +
+
+ + + + + + + + + + + + 0=7;17=19;55=20; +
+
+ + + + + + + 0=9;12=10; + + + + + + + 0=5;31=6; + + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + 0=33;4=34;23=35;42=37;58=38;64=40;75=41;93=42;112=44;129=45; +
+ + + + + + + + + + 0=51;8=52;29=53;46=55;47=56; + + + + + + + + + + + 0=64;8=65;29=66;50=67;67=69;68=70; + + + + + + + + + + + 0=78;8=79;29=80;46=82;47=83; + + + + +
+ + +
+
+ + + + + + + + + + + + 0=16; +
+
+ + + + + + + 0=26;7=28;12=29;18=30;24=31;35=32; + + + + + + + 0=37; + + + + + + + 0=38; + + + + + + + 0=39; + + + + + + + 0=40; + + + + + + + 0=41; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + + 0=49;19=51;29=52;41=54;61=55;70=57;80=58;86=60; +
+ + + +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + 0=65;10=66;19=67;45=68;67=69;80=67;86=72; +
+ + + +
+ + +
+
+ + + + + + + + + + + + 0=8;17=11;22=12;27=13;32=14;37=15;42=16; +
+
+ + + + + + + 0=50;12=51;18=52;29=53;34=54;39=55;44=56;50=57;55=58;60=59;65=61;78=62;89=63; + + + + + + + 0=66;12=67;20=68;28=69;36=70;44=71;52=72;60=73;68=74;76=75;84=76;92=77; + + + + + + + 0=79; + + + + + + + 0=80; + + + + + + + 0=81; + + + + + + + 0=82; + + + + + + + 0=83; + + + + + + + 0=84; + + + + + + + 0=85; + + + + + + + 0=86; + + + + + + + 0=87; + + + + + + + 0=90; + + + + + + + 0=93; + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=102;9=103;18=105;27=106;43=108;52=109;64=111;73=112;85=114;94=115;106=117;115=118;124=120;133=121;145=123;154=124;166=126;175=127;184=131;187=129;189=130;199=132; +
+ + + + + + 0=138; + + + + + + + 0=25;56=34; + +
+
+ + + + + + + + 0=20;4=22; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + + 0=25;4=26;9=27;15=28;22=29;32=31;36=32;46=34; +
+ + + + + + 0=38; + +
+ + + + + + + 0=8;4=9; + + + + + + + 0=12;5=13; + + +
+ + + + + + + + 0=40;4=33;10=34;17=41;22=42;34=43; + + + + +
+ + +
+
+ + + + + + + + + + +
+ + +
+
+ + + + + + + + + + + + + + 0=49;12=50;18=53;23=54;33=55;43=56;48=57;59=58;66=59;106=60;121=61;132=62;141=64;147=65;156=66;159=77;162=69;164=72;178=73;188=75;190=76;200=79; +
+ + + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + 0=84;8=85;11=88;19=89;37=90;44=91;50=92;62=93;72=100;77=101;80=96;82=97;92=100;102=103; +
+ + + + + + 0=111; + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + 0=120;3=121;6=124;12=125;24=128;55=130;63=131;68=132;79=133;88=134;104=147;109=148;112=136;114=144;124=147;134=150; +
+ + + +
+ + +
+
+ + + + + + + + + + + + 0=31; +
+
+
+ + + + + + + + 0=8; + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=11;5=12;11=17;14=15;15=16;24=18; +
+
+ + + + + + + 0=3; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + 0=7;14=8;16=11;21=13;22=14; +
+ + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + 0=19;14=20;16=23;21=25;22=26; +
+
+ + + + + + + 0=3; + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + 0=5;5=6;16=8; +
+
+
+ + + + + + + + 0=14;4=15;9=16;14=17; + + + + + + + 0=19; + + + + + + + 0=20; + + + + + + + + + 0=16;4=17;9=18;20=19;31=20; + + + + + + + 0=23;9=24; + + + + + + + 0=27;9=28; + + + + + + + 0=30; + + + + + + + +
+ + +
+
+ + + + + + + + + +
+ + +
+
+ + + + + + + + + 0=34;8=36;40=38;67=39;96=40;99=42;126=43;155=44;158=45; +
+
+ + + + + + + 0=16;4=17;15=18;24=19; + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=24;10=25;11=29;27=33;30=31;32=32;42=34; +
+ + + +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + + + + + 0=40;10=41;11=44;29=45;34=46;42=48;52=49;53=52;69=57;72=55;74=56;84=58; +
+ + + +
+ + +
+
+ + + + + + + + + + + + + + + + 0=64;12=65;24=66;31=67;41=72;44=70;46=71;56=73; +
+ + + +
+ + +
+
+ + + + + + + 0=77;10=78;24=79;40=81; +
+ + + + + + 0=86; + + + + + + + 0=93; + + + + + + + 0=100; + + + + + + + 0=105; + + + + + + + 0=110; + + + + + + + 0=115; + + + + + + + 0=122; + +
+ + + + + + + +
+ + +
+
+ + + + + + + + + +
+ + +
+
+ + + + + + + + + 0=32;4=33;15=34;26=35;37=36;42=38;47=39;58=40;69=42;94=43;104=42;110=46;120=47;131=48;142=49;154=46;160=51; +
+ + + + + + 0=56; + + + + + + + 0=57; + + + + + + + 0=61;11=62; + + + + + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + 0=65;8=66;16=67;25=68;36=69;48=70;55=71;61=73;64=75;73=76; +
+ + + +
+ + +
+
+ +
+ + +
+
+ + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + 0=82;19=84;29=86;36=88;38=91;49=92;53=93;55=96;60=98;62=101;74=102;83=103;94=106;103=107;112=109; +
+ + + +
+ + +
+
+ +
+ + +
+
+ + + + +
+ + +
+
+ +
+ + +
+
+ + + + + + + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + + + + + + + 0=116;19=118;27=120;34=123;45=124;50=125;52=132;54=136;65=137;74=138;76=141;89=142;95=144;105=145;115=146;126=148;128=149;138=151;140=152;150=157; +
+ + + + + + 0=164; + + + + +
+ + +
+
+ + + + + + + + + + + + 0=19; +
+
+ + + + + + + 0=9;4=11; + + + + + + + 0=14;5=15; + + +
+
diff --git a/test/net/jaekl/frank/Main.java b/test/net/jaekl/frank/Main.java new file mode 100644 index 0000000..21f7761 --- /dev/null +++ b/test/net/jaekl/frank/Main.java @@ -0,0 +1,46 @@ +// Simple driver for ad-hoc testing of the query interface +// (useful if we want to try running a query without a Servlet Engine). + +package net.jaekl.frank; + +import java.io.FileInputStream; +import java.io.PrintWriter; +import java.util.Locale; + +import net.jaekl.frank.octranspo.Server; +import net.jaekl.frank.octranspo.StopInfo; + +public class Main{ + public static void main(String[] args) { + try { + FileInputStream fis = null; + PrintWriter pw = null; + try { + fis = new FileInputStream("/home/chris/prog/Frank/apikey.txt"); + Server server = new Server("192f31d2", fis); + pw = new PrintWriter(System.out); + // StopInfo summary = server.getRouteSummaryForStop(7655); + + Schedule schedule = new Schedule(Locale.getDefault()); + // schedule.writePage(pw, summary); + + StopInfo one = server.getNextTripsForStop(7655, 1); + schedule.writePage(pw, one); + // StopInfo seven = server.getNextTripsForStop(7655, 7); + // schedule.writePage(pw, seven); + } + finally { + if (null != fis) { + fis.close(); + } + if (null != pw) { + pw.flush(); + pw.close(); + } + } + } + catch (Throwable t) { + t.printStackTrace(); + } + } +} diff --git a/test/net/jaekl/frank/ScheduleTest.java b/test/net/jaekl/frank/ScheduleTest.java new file mode 100644 index 0000000..2fc9ac2 --- /dev/null +++ b/test/net/jaekl/frank/ScheduleTest.java @@ -0,0 +1,209 @@ +package net.jaekl.frank; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Date; +import java.util.Locale; + +import junit.framework.Assert; + +import net.jaekl.frank.octranspo.RouteMock; +import net.jaekl.frank.octranspo.RouteSummaryMock; +import net.jaekl.frank.octranspo.TripMock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ScheduleTest { + static final String EXPECTED_STYLE = "\n"; + static final String TITLE_PREFIX = "\n\n"; + static final String TITLE_SUFFIX = "\n" + EXPECTED_STYLE + "\n"; + + + ByteArrayOutputStream m_baos; + PrintWriter m_pw; + + @Before + public void setUp() { + m_baos = new ByteArrayOutputStream(); + m_pw = new PrintWriter(m_baos); + } + + @After + public void tearDown() throws IOException { + if (null != m_baos) { + m_baos.close(); + } + } + + // Confirm that writeStyle's output does not vary with the locale + @Test + public void test_writeStyle() { + String actual; + + Locale[] locales = { Locale.CANADA, Locale.CANADA_FRENCH, Locale.JAPAN }; + + for (Locale locale : locales) { + m_baos.reset(); + + Schedule schedule = new Schedule(locale); + schedule.writeStyle(m_pw); + m_pw.flush(); + + actual = m_baos.toString(); + Assert.assertEquals(EXPECTED_STYLE, actual); + } + } + + @Test + public void test_writeHeader() { + Locale[] locales = { Locale.CANADA, Locale.CANADA_FRENCH, Locale.JAPAN }; + String[] titles = { "", "CARLETON", "BILLINGS BRIDGE (3204)" }; + + for (Locale locale : locales) { + Schedule schedule = new Schedule(locale); + for (String title : titles) { + String expected = TITLE_PREFIX + title + TITLE_SUFFIX; + + m_baos.reset(); + + schedule.writeHeader(m_pw, title); + m_pw.flush(); + + String actual = m_baos.toString(); + Assert.assertEquals(expected, actual); + } + } + } + + @Test + public void test_writePage_withNoRoutes() { + final String stopName = "RIDEAU RIDEAU"; + final int stopNo = 3009; + + RouteSummaryMock rsm = new RouteSummaryMock(); + rsm.mock_setDescr(stopName); + rsm.mock_setStopNo(stopNo); + + Schedule schedule = new Schedule(Locale.CANADA); + + m_baos.reset(); + + schedule.writePage(m_pw, rsm); + m_pw.flush(); + + String actual = m_baos.toString(); + + Assert.assertTrue(actual.contains(EXPECTED_STYLE)); + + String expectedTitle = TITLE_PREFIX + "Frank: " + stopName + " (" + stopNo + ")" + TITLE_SUFFIX; + Assert.assertTrue(actual.contains(expectedTitle)); + } + + @Test + public void test_writePage_withMultipleRoutes() { + final String stopName = "RIDEAU RIDEAU"; + final String SOUTH_KEYS = "South Keys"; + final String HURDMAN = "Hurdman"; + final int stopNo = 3009; + final Date queryDate = new Date(1418357155000L); + final double LAT1 = 45.379298; + final double LON1 = -75.652007; + final double LAT2 = 45.351322; + final double LON2 = -75.652007; + + RouteSummaryMock rsm; + RouteMock route; + TripMock trip; + + rsm = new RouteSummaryMock(); + rsm.mock_setDescr(stopName); + rsm.mock_setStopNo(stopNo); + + // First route: Number 1 + route = new RouteMock(); + route.mock_setRouteNo(1); + + trip = new TripMock(queryDate); + trip.mock_setDest(SOUTH_KEYS); + trip.mock_setAdjTime(10); + trip.mock_setLatitude(LAT1); + trip.mock_setLongitude(LON1); + trip.mock_setSpeed(0.5); + route.mock_addTrip(trip); + + trip = new TripMock(queryDate); + trip.mock_setDest(SOUTH_KEYS); + trip.mock_setAdjTime(20); + trip.mock_setLatitude(LAT2); + trip.mock_setLongitude(LON2); + trip.mock_setSpeed(22.4); + route.mock_addTrip(trip); + + rsm.mock_addRoute(route); + + // Second route: Number 4 + route = new RouteMock(); + route.mock_setRouteNo(4); + + trip = new TripMock(queryDate); + trip.mock_setDest(HURDMAN); + trip.mock_setAdjTime(17); + route.mock_addTrip(trip); + + trip = new TripMock(queryDate); + trip.mock_setDest(HURDMAN); + trip.mock_setAdjTime(37); + route.mock_addTrip(trip); + + rsm.mock_addRoute(route); + + // Generate the HTML + Schedule schedule = new Schedule(Locale.CANADA); + + m_baos.reset(); + + schedule.writePage(m_pw, rsm); + m_pw.flush(); + + String actual = m_baos.toString(); + + // Some rudimentary validation of the result. + // Should really go through more permutations, and examine them more closely, here. + Assert.assertTrue(actual.contains(EXPECTED_STYLE)); + + String expectedTitle = TITLE_PREFIX + "Frank: " + stopName + " (" + stopNo + ")" + TITLE_SUFFIX; + Assert.assertTrue(actual.contains(expectedTitle)); + + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + Assert.assertTrue(actual.contains("")); + } +} diff --git a/test/net/jaekl/frank/ViewScheduleTest.java b/test/net/jaekl/frank/ViewScheduleTest.java new file mode 100644 index 0000000..eec671f --- /dev/null +++ b/test/net/jaekl/frank/ViewScheduleTest.java @@ -0,0 +1,100 @@ +package net.jaekl.frank; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Locale; + +import net.jaekl.qd.QDException; +import net.jaekl.qd.http.HttpServletRequestMock; + +import org.junit.Assert; +import org.junit.Test; + +public class ViewScheduleTest { + + private HashMap makeParamMap() { + String[] routeNos = { "127" }; + String[] alphas = { "abcdefg" }; + String[] kanas = { "あいうえお" }; + + HashMap paramMap = new HashMap(); + paramMap.put("routeNo", routeNos); + paramMap.put("alpha", alphas); + paramMap.put("kana", kanas); + + return paramMap; + } + + @Test + public void testGetParamInt() { + HashMap paramMap = makeParamMap(); + HttpServletRequestMock reqMock = new HttpServletRequestMock(paramMap); + ViewSchedule vs = new ViewSchedule(); + + // Try to get a parameter that is an integer, and is there + int value = vs.getParamInt(reqMock, "routeNo"); + Assert.assertEquals(127, value); + + // Try to get a parameter that is not an integer + value = vs.getParamInt(reqMock, "alpha"); + Assert.assertEquals(0, value); + + // Try to get a parameter that is not alphanumeric + value = vs.getParamInt(reqMock, "kana"); + Assert.assertEquals(0, value); + + // Try to get a parameter that is not present + value = vs.getParamInt(reqMock, "notPresent"); + Assert.assertEquals(0, value); + } + + @Test + public void testGetParamString() { + HashMap paramMap = makeParamMap(); + HttpServletRequestMock reqMock = new HttpServletRequestMock(paramMap); + ViewSchedule vs = new ViewSchedule(); + + // Try to get a parameter that is an integer, and is there + String value = vs.getParamString(reqMock, "routeNo"); + Assert.assertEquals("127", value); + + // Try to get a parameter that is not an integer + value = vs.getParamString(reqMock, "alpha"); + Assert.assertEquals("abcdefg", value); + + // Try to get a parameter that is not alphanumeric + value = vs.getParamString(reqMock, "kana"); + Assert.assertEquals("あいうえお", value); + + // Try to get a parameter that is not present + value = vs.getParamString(reqMock, "notPresent"); + Assert.assertEquals(null, value); + } + + @Test + public void testWriteErrorPage() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintWriter pw = new PrintWriter(baos); + + ViewSchedule vs = new ViewSchedule(); + + Throwable[] throwables = { + new NullPointerException(), + new QDException(), + new SQLException() + }; + + for (Throwable t : throwables) { + baos.reset(); + vs.writeErrorPage(pw, t, Locale.CANADA); // TODO: test translations + pw.flush(); + + String actual = baos.toString(); + Assert.assertTrue(actual.contains("Frank: Error Page")); + Assert.assertTrue(actual.contains(t.toString())); + } + } + +} diff --git a/test/net/jaekl/frank/octranspo/RouteMock.java b/test/net/jaekl/frank/octranspo/RouteMock.java new file mode 100644 index 0000000..5a91f21 --- /dev/null +++ b/test/net/jaekl/frank/octranspo/RouteMock.java @@ -0,0 +1,11 @@ +package net.jaekl.frank.octranspo; + +public class RouteMock extends Route { + public void mock_addTrip(Trip trip) { + m_trips.add(trip); + } + + public void mock_setRouteNo(int routeNo) { + m_routeNo = routeNo; + } +} diff --git a/test/net/jaekl/frank/octranspo/RouteSummaryMock.java b/test/net/jaekl/frank/octranspo/RouteSummaryMock.java new file mode 100644 index 0000000..da54e10 --- /dev/null +++ b/test/net/jaekl/frank/octranspo/RouteSummaryMock.java @@ -0,0 +1,15 @@ +package net.jaekl.frank.octranspo; + +public class RouteSummaryMock extends RouteSummary { + public void mock_addRoute(Route route) { + m_routes.add(route); + } + + public void mock_setDescr(String descr) { + m_descr = descr; + } + + public void mock_setStopNo(int stopNo) { + m_stopNo = stopNo; + } +} diff --git a/test/net/jaekl/frank/octranspo/ServerMock.java b/test/net/jaekl/frank/octranspo/ServerMock.java new file mode 100644 index 0000000..2dffc50 --- /dev/null +++ b/test/net/jaekl/frank/octranspo/ServerMock.java @@ -0,0 +1,20 @@ +package net.jaekl.frank.octranspo; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import net.jaekl.qd.http.RequestBrokerMock; + +public class ServerMock extends Server { + public static final String mock_APP_ID = "4567"; + public static final String mock_API_KEY = "CaiusIuliusCaesarSPQR"; + + + public ServerMock() throws IOException { + super(mock_APP_ID, new ByteArrayInputStream(mock_API_KEY.getBytes("UTF-8"))); + + m_broker = new RequestBrokerMock(GATEWAY_URL, m_baseParams); + } + + public RequestBrokerMock mock_getBroker() { return (RequestBrokerMock)m_broker; } +} diff --git a/test/net/jaekl/frank/octranspo/ServerTest.java b/test/net/jaekl/frank/octranspo/ServerTest.java new file mode 100644 index 0000000..7f576cb --- /dev/null +++ b/test/net/jaekl/frank/octranspo/ServerTest.java @@ -0,0 +1,298 @@ +package net.jaekl.frank.octranspo; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +import junit.framework.Assert; +import net.jaekl.frank.FrankException; +import net.jaekl.qd.QDException; +import net.jaekl.qd.http.RequestBrokerMock; + +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; +import org.junit.Test; + +public class ServerTest { + private static final String ROUTE_SUMMARY_FOR_STOP = + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " 1234\n" + + " ONE-TWO-THREE-FOUR\n" + + " \n" + + " \n" + + " \n" + + " 123\n" + + " 0\n" + + " NORTH\n" + + " First Mall\n" + + " \n" + + " \n" + + " 123\n" + + " 1\n" + + " SOUTH\n" + + " Second Mall\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + + private static final String NEXT_TRIPS_FOR_STOP = + "" + + "" + + "" + + "" + + "" + + "7659" + + "BANK FIFTH" + + "" + + "" + + "" + + "1Ottawa-Rockcliffe" + + "Northbound20141130111450" + + "" + + "" + + "Ottawa Rockcliffe" + + "11:10" + + "20" + + "0.18" + + "false" + + "4LA - DEH" + + "45.352026-75.64999350.0" + + "" + + "" + + "Ottawa Rockcliffe" + + "11:40" + + "51-1" + + "false4E - DEH" + + "" + + "" + + "" + + "Ottawa Rockcliffe" + + "12:00" + + "71-1" + + "false4E - DEH" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + private static final String NEXT_TRIPS_FOR_STOP_ALL_ROUTES = + "" + + "" + + "" + + "" + + "" + + "7659" + + "BANK FIFTH" + + "" + + "" + + "" + + "11Northbound" + + "Ottawa-Rockcliffe" + + "" + + "" + + "Ottawa Rockcliffe11:40" + + "20.55" + + "4E - DEH" + + "45.392965-75.68256135.7" + + "" + + "" + + "Ottawa Rockcliffe12:00" + + "170.62" + + "4E - DEH" + + "45.362237-75.65811257.6" + + "" + + "" + + "Ottawa Rockcliffe12:20" + + "37-1" + + "4E - DEH" + + "" + + "" + + "" + + "" + + "" + + "71Eastbound" + + "St-Laurent" + + "St. Laurent12:11" + + "120.61" + + "4LB - IN" + + "45.384370-75.6963610.5" + + "" + + "" + + "St. Laurent12:31" + + "32-1" + + "4L - DEH" + + "" + + "" + + "St. Laurent12:51" + + "52-1" + + "4LA - DEH" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + @Test + public void testGetRouteSummaryForStop() throws IOException, QDException, FrankException { + ServerMock sm = new ServerMock(); + RequestBrokerMock broker = sm.mock_getBroker(); + + ArrayList params = new ArrayList(); + params.add(new BasicNameValuePair(Server.STOP_NO, "1234")); + + broker.setOutput(Server.GET_ROUTE_SUMMARY_FOR_STOP, params, ROUTE_SUMMARY_FOR_STOP); + + StopInfo stopInfo = sm.getRouteSummaryForStop(1234); + + Assert.assertNotNull(stopInfo); + Assert.assertEquals(1234, stopInfo.getStopNo()); + Assert.assertEquals("ONE-TWO-THREE-FOUR", stopInfo.getDescr()); + Assert.assertEquals("", stopInfo.getError()); + Assert.assertEquals(2, stopInfo.getNumRoutes()); + + Route route; + + route = stopInfo.getRoute(0); + Assert.assertEquals(123, route.getRouteNo()); + Assert.assertEquals(0, route.getDirectionID()); + Assert.assertEquals("NORTH", route.getDirection()); + Assert.assertEquals("First Mall", route.getRouteHeading()); + + route = stopInfo.getRoute(1); + Assert.assertEquals(123, route.getRouteNo()); + Assert.assertEquals(1, route.getDirectionID()); + Assert.assertEquals("SOUTH", route.getDirection()); + Assert.assertEquals("Second Mall", route.getRouteHeading()); + } + + @Test + public void testGetNextTripsForStop() throws FrankException, IOException, ParseException { + Route route; + Trip trip; + Date expectedDate; + DateFormat hourMinFormat = new SimpleDateFormat("hh:mm"); + + ServerMock sm = new ServerMock(); + RequestBrokerMock broker = sm.mock_getBroker(); + + ArrayList params = new ArrayList(); + params.add(new BasicNameValuePair(Server.STOP_NO, "7659")); + params.add(new BasicNameValuePair(Server.ROUTE_NO, "1")); + + broker.setOutput(Server.GET_NEXT_TRIPS_FOR_STOP, params, NEXT_TRIPS_FOR_STOP); + + StopInfo stopInfo = sm.getNextTripsForStop(7659, 1); + + Assert.assertNotNull(stopInfo); + Assert.assertEquals(7659, stopInfo.getStopNo()); + Assert.assertEquals("BANK FIFTH", stopInfo.getDescr()); + Assert.assertEquals("", stopInfo.getError()); + Assert.assertEquals(1, stopInfo.getNumRoutes()); + + route = stopInfo.getRoute(0); + Assert.assertEquals(1, route.getRouteNo()); + Assert.assertEquals("Northbound", route.getDirection()); + Assert.assertEquals("Ottawa-Rockcliffe", route.getRouteHeading()); + Assert.assertEquals(3, route.getNumTrips()); + + trip = route.getTrip(0); + Assert.assertEquals("Ottawa Rockcliffe", trip.getDest()); + expectedDate = hourMinFormat.parse("11:10"); + Assert.assertEquals(expectedDate, trip.getStart()); + Assert.assertEquals(20, trip.getAdjTime()); + Assert.assertEquals(0.18, trip.getAdjAge()); + Assert.assertEquals(false, trip.isLastTrip()); + Assert.assertEquals("4LA - DEH", trip.getBusType()); + Assert.assertEquals(50.0, trip.getSpeed()); + Assert.assertEquals(45.352026, trip.getLatitude()); + Assert.assertEquals(-75.649993, trip.getLongitude()); + + trip = route.getTrip(2); + Assert.assertEquals("Ottawa Rockcliffe", trip.getDest()); + expectedDate = hourMinFormat.parse("12:00"); + Assert.assertEquals(expectedDate, trip.getStart()); + Assert.assertEquals(71, trip.getAdjTime()); + Assert.assertEquals(-1.0, trip.getAdjAge()); + Assert.assertEquals(false, trip.isLastTrip()); + Assert.assertEquals("4E - DEH", trip.getBusType()); + Assert.assertEquals(0.0, trip.getSpeed()); + Assert.assertEquals(0.0, trip.getLatitude()); + Assert.assertEquals(0.0, trip.getLongitude()); + } + + @Test + public void testGetNextTripsForStopAllRoutes() throws IOException, FrankException, ParseException { + Route route; + Trip trip; + Date expectedDate; + DateFormat hourMinFormat = new SimpleDateFormat("hh:mm"); + + ServerMock sm = new ServerMock(); + RequestBrokerMock broker = sm.mock_getBroker(); + + ArrayList params = new ArrayList(); + params.add(new BasicNameValuePair(Server.STOP_NO, "7659")); + + broker.setOutput(Server.GET_NEXT_TRIPS_FOR_STOP_ALL_ROUTES, params, NEXT_TRIPS_FOR_STOP_ALL_ROUTES); + + StopInfo stopInfo = sm.getNextTripsForStopAllRoutes(7659); + + Assert.assertNotNull(stopInfo); + Assert.assertEquals(7659, stopInfo.getStopNo()); + Assert.assertEquals("BANK FIFTH", stopInfo.getDescr()); + Assert.assertEquals("", stopInfo.getError()); + Assert.assertEquals(2, stopInfo.getNumRoutes()); + + route = stopInfo.getRoute(0); + Assert.assertEquals(1, route.getRouteNo()); + Assert.assertEquals("Northbound", route.getDirection()); + Assert.assertEquals("Ottawa-Rockcliffe", route.getRouteHeading()); + Assert.assertEquals(3, route.getNumTrips()); + + trip = route.getTrip(0); + Assert.assertEquals("Ottawa Rockcliffe", trip.getDest()); + expectedDate = hourMinFormat.parse("11:40"); + Assert.assertEquals(expectedDate, trip.getStart()); + Assert.assertEquals(2, trip.getAdjTime()); + Assert.assertEquals(0.55, trip.getAdjAge()); + Assert.assertEquals(false, trip.isLastTrip()); + Assert.assertEquals("4E - DEH", trip.getBusType()); + Assert.assertEquals(35.7, trip.getSpeed()); + Assert.assertEquals(45.392965, trip.getLatitude()); + Assert.assertEquals(-75.682561, trip.getLongitude()); + + trip = route.getTrip(2); + Assert.assertEquals("Ottawa Rockcliffe", trip.getDest()); + expectedDate = hourMinFormat.parse("12:20"); + Assert.assertEquals(expectedDate, trip.getStart()); + Assert.assertEquals(37, trip.getAdjTime()); + Assert.assertEquals(-1.0, trip.getAdjAge()); + Assert.assertEquals(false, trip.isLastTrip()); + Assert.assertEquals("4E - DEH", trip.getBusType()); + Assert.assertEquals(0.0, trip.getSpeed()); + Assert.assertEquals(0.0, trip.getLatitude()); + Assert.assertEquals(0.0, trip.getLongitude()); + } + +} diff --git a/test/net/jaekl/frank/octranspo/TripMock.java b/test/net/jaekl/frank/octranspo/TripMock.java new file mode 100644 index 0000000..7585c23 --- /dev/null +++ b/test/net/jaekl/frank/octranspo/TripMock.java @@ -0,0 +1,22 @@ +package net.jaekl.frank.octranspo; + +import java.util.Date; + +public class TripMock extends Trip { + + public TripMock(Date constructed) { + super(); + + this.m_constructed = constructed; + } + + public void mock_setDest(String dest) { m_dest = dest; } + public void mock_setStart(Date start) { m_start = start; } + public void mock_setAdjTime(int adjTime) { m_adjTime = adjTime; } + public void mock_setAdjAge(double adjAge) { m_adjAge = adjAge; } + public void mock_setLastTrip(boolean lastTrip) { m_lastTrip = lastTrip; } + public void mock_setBusType(String busType) { m_busType = busType; } + public void mock_setSpeed(double speed) { m_speed = speed; } + public void mock_setLongitude(double longitude) { m_long = longitude; } + public void mock_setLatitude(double lat) { m_lat = lat; } +} diff --git a/test/net/jaekl/qd/QDBundleFactoryTest.java b/test/net/jaekl/qd/QDBundleFactoryTest.java new file mode 100644 index 0000000..c45bf56 --- /dev/null +++ b/test/net/jaekl/qd/QDBundleFactoryTest.java @@ -0,0 +1,19 @@ +// Copyright (C) 2004, 2014 Christian Jaekl + +package net.jaekl.qd; + +import org.junit.Assert; + +import org.junit.Test; + +public class QDBundleFactoryTest { + + @Test + public void test_getInst() { + QDBundleFactory factory = QDBundleFactory.getInst(); + + Assert.assertNotNull(factory); + Assert.assertTrue(factory instanceof QDBundleFactory); + Assert.assertTrue(factory == QDBundleFactory.getInst()); + } +} diff --git a/test/net/jaekl/qd/http/HttpServletRequestMock.java b/test/net/jaekl/qd/http/HttpServletRequestMock.java new file mode 100644 index 0000000..8d22b03 --- /dev/null +++ b/test/net/jaekl/qd/http/HttpServletRequestMock.java @@ -0,0 +1,351 @@ +package net.jaekl.qd.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletInputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +public class HttpServletRequestMock implements HttpServletRequest { + Map m_paramMap; + + public HttpServletRequestMock(Map paramMap) { + m_paramMap = paramMap; + } + + @Override + public Object getAttribute(String arg0) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Enumeration getAttributeNames() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getCharacterEncoding() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getContentLength() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String getContentType() { + // TODO Auto-generated method stub + return null; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getLocalAddr() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getLocalName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getLocalPort() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public Locale getLocale() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Enumeration getLocales() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getParameter(String arg0) { + String[] values = m_paramMap.get(arg0); + if (null == values || 0 == values.length) { + return null; + } + return values[0]; + } + + @Override + public Map getParameterMap() { + return m_paramMap; + } + + @Override + public Enumeration getParameterNames() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String[] getParameterValues(String arg0) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getProtocol() { + // TODO Auto-generated method stub + return null; + } + + @Override + public BufferedReader getReader() throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getRealPath(String arg0) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getRemoteAddr() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getRemoteHost() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getRemotePort() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public RequestDispatcher getRequestDispatcher(String arg0) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getScheme() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getServerName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getServerPort() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isSecure() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void removeAttribute(String arg0) { + // TODO Auto-generated method stub + + } + + @Override + public void setAttribute(String arg0, Object arg1) { + // TODO Auto-generated method stub + + } + + @Override + public void setCharacterEncoding(String arg0) + throws UnsupportedEncodingException { + // TODO Auto-generated method stub + + } + + @Override + public String getAuthType() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getContextPath() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Cookie[] getCookies() { + // TODO Auto-generated method stub + return null; + } + + @Override + public long getDateHeader(String arg0) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String getHeader(String arg0) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Enumeration getHeaderNames() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Enumeration getHeaders(String arg0) { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getIntHeader(String arg0) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String getMethod() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getPathInfo() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getPathTranslated() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getQueryString() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getRemoteUser() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getRequestURI() { + // TODO Auto-generated method stub + return null; + } + + @Override + public StringBuffer getRequestURL() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getRequestedSessionId() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getServletPath() { + // TODO Auto-generated method stub + return null; + } + + @Override + public HttpSession getSession() { + // TODO Auto-generated method stub + return null; + } + + @Override + public HttpSession getSession(boolean arg0) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Principal getUserPrincipal() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isRequestedSessionIdValid() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean isUserInRole(String arg0) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/test/net/jaekl/qd/http/RequestBrokerMock.java b/test/net/jaekl/qd/http/RequestBrokerMock.java new file mode 100644 index 0000000..7e7624b --- /dev/null +++ b/test/net/jaekl/qd/http/RequestBrokerMock.java @@ -0,0 +1,95 @@ +package net.jaekl.qd.http; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; + +import net.jaekl.qd.QDException; +import net.jaekl.qd.util.StringUtils; + +import org.apache.http.NameValuePair; +import org.junit.Assert; + +public class RequestBrokerMock extends RequestBroker { + private static class InputParams { + private String m_method; + private ArrayList m_passedParams; + + public InputParams(String method, ArrayList passedParams) { + m_method = method; + m_passedParams = new ArrayList(); + for (NameValuePair nvp : passedParams) { + m_passedParams.add(nvp); + } + } + + @Override + public boolean equals(Object obj) { + if (null == obj || !(obj instanceof InputParams)) { + return false; + } + InputParams other = (InputParams)obj; + if (! StringUtils.areEqual(m_method, other.m_method)) { + return false; + } + if (m_passedParams.size() != other.m_passedParams.size()) { + return false; + } + for (int i = 0; i < m_passedParams.size(); ++i) { + NameValuePair a, b; + a = m_passedParams.get(i); + b = other.m_passedParams.get(i); + if ( ! a.equals(b) ) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + final int PRIME = 31; + int result = PRIME + m_method.hashCode(); + for (NameValuePair nvp : m_passedParams) { + result *= (PRIME + nvp.hashCode()); + } + return result; + } + } + + HashMap m_output; + + public RequestBrokerMock(String gatewayUrl, ArrayList baseParams) { + super(gatewayUrl, baseParams); + m_output = new HashMap(); + } + + public void setOutput(String method, ArrayList passedParams, String output) { + InputParams ip = new InputParams(method, passedParams); + m_output.put(ip, output); + } + + @Override + InputStream doSubmit(String method, ArrayList passedParams) throws QDException + { + InputParams ip = new InputParams(method, passedParams); + String output = m_output.get(ip); + + if (null == output) { + Assert.fail("No output specified for given inputs."); + } + + InputStream is = null; + + try { + is = new ByteArrayInputStream(output.getBytes("UTF-8")); + } + catch (UnsupportedEncodingException uee) { + throw new QDException(uee); + } + + return is; + } +} diff --git a/test/net/jaekl/qd/http/RequestBrokerTest.java b/test/net/jaekl/qd/http/RequestBrokerTest.java new file mode 100644 index 0000000..037e079 --- /dev/null +++ b/test/net/jaekl/qd/http/RequestBrokerTest.java @@ -0,0 +1,170 @@ +package net.jaekl.qd.http; + +import java.util.ArrayList; + +import junit.framework.Assert; +import net.jaekl.qd.QDException; +import net.jaekl.qd.xml.ParseResult; +import net.jaekl.qd.xml.XmlParseException; + +import org.apache.http.NameValuePair; +import org.junit.Test; + +public class RequestBrokerTest { + + private static final String GATEWAY = "http://hostname.bogus/"; + private static final String METHOD = "apiMethod"; + private static final String ROUTE_SUMMARY_FOR_STOP = + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " 1234\n" + + " ONE-TWO-THREE-FOUR\n" + + " \n" + + " \n" + + " \n" + + " 123\n" + + " 0\n" + + " NORTH\n" + + " First Mall\n" + + " \n" + + " \n" + + " 123\n" + + " 1\n" + + " SOUTH\n" + + " Second Mall\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + + // Parse sub-tags, handling some internally and some externally + public static class RouteSummaryParse extends ParseResult { + private static final String STOP_NO = "StopNo"; + + private static final String[] INTERNAL = { STOP_NO }; + private static final Object[][] EXTERNAL = { }; + + // Data gleaned from parsing + int m_stopNo; + + public RouteSummaryParse() { + super("GetRouteSummaryForStopResult", INTERNAL, EXTERNAL); + + m_stopNo = 0; + } + + public int getStopNo() { return m_stopNo; } + + @Override + public void endContents(String uri, String localName, String qName, + String chars) throws XmlParseException + { + if (localName.equals(STOP_NO)) { + m_stopNo = Integer.parseInt(chars); + } + } + + @Override + public void endExternal(String uri, String localName, String qName) + throws XmlParseException + { + Assert.fail("Should not be trying to parse external tags here"); + } + } + + // Try submitting a request (with a mocked-out http post component) + // and validate that we do, in fact, return the expected text (XML). + @Test + public void test_sumbit() throws QDException { + ArrayList emptyParams = new ArrayList(); + + RequestBrokerMock rbm = new RequestBrokerMock(GATEWAY, emptyParams); + rbm.setOutput(METHOD, emptyParams, ROUTE_SUMMARY_FOR_STOP); + + String actual = rbm.submit(METHOD, emptyParams); + + Assert.assertEquals(ROUTE_SUMMARY_FOR_STOP, actual); + } + + // Try submitting a request (with a mocked-out http post component) + // and validate that we do, in fact, parse something correctly. + @Test + public void test_submitAndParse() throws QDException { + ArrayList emptyParams = new ArrayList(); + + RequestBrokerMock rbm = new RequestBrokerMock(GATEWAY, emptyParams); + rbm.setOutput(METHOD, emptyParams, ROUTE_SUMMARY_FOR_STOP); + + ParseResult pr = rbm.submitAndParse(METHOD, emptyParams, RouteSummaryParse.class); + + Assert.assertNotNull(pr); + Assert.assertTrue(pr instanceof RouteSummaryParse); + RouteSummaryParse rsp = (RouteSummaryParse)pr; + Assert.assertEquals(1234, rsp.getStopNo()); + } + + + class RequestBrokerParamExpandMock extends RequestBrokerMock { + private int m_timesCalled; + private String m_methodPassed; + private ArrayList m_paramsPassed; + private Class m_parserClassPassed; + + public RequestBrokerParamExpandMock(String gatewayUrl, ArrayList baseParams) { + super(gatewayUrl, baseParams); + m_timesCalled = 0; + m_methodPassed = null; + m_paramsPassed = null; + m_parserClassPassed = null; + } + + public int getTimesCalled() { return m_timesCalled; } + public String getMethodPassed() { return m_methodPassed; } + public ArrayList getParamsPassed() { return m_paramsPassed; } + public Class getParserClassPassed() { return m_parserClassPassed; } + + @Override + public ParseResult submitAndParse(String method, + ArrayList passedParams, + Class rootParserClass, + String rootTagName) + throws QDException + { + Assert.assertNull(rootTagName); + + m_timesCalled += 1; + m_methodPassed = method; + m_paramsPassed = passedParams; + m_parserClassPassed = rootParserClass; + + return null; + } + } + + // submitAndParse(method, passedParams, rootParser) + // should call through to + // submitAndParse(method, passedParams, rootParser, rootTagName=null) + @Test + public void test_submitAndParse_expandsDefault4thParam() throws QDException { + final String GATEWAY_URL = "http://bogus.net:1234/gateway"; + final String METHOD = "MyMethod"; + + RequestBrokerParamExpandMock rbpem = new RequestBrokerParamExpandMock(GATEWAY_URL, new ArrayList() ); + ArrayList passedParams = new ArrayList(); + ParseResult pr = rbpem.submitAndParse(METHOD, passedParams, ParseResult.class); + + Assert.assertNull(pr); + Assert.assertEquals(1, rbpem.getTimesCalled()); + Assert.assertEquals(METHOD, rbpem.getMethodPassed()); + Assert.assertEquals(passedParams, rbpem.getParamsPassed()); + Assert.assertEquals(ParseResult.class, rbpem.getParserClassPassed()); + } +} diff --git a/test/net/jaekl/qd/util/ExceptionUtilsTest.java b/test/net/jaekl/qd/util/ExceptionUtilsTest.java new file mode 100644 index 0000000..c1361ba --- /dev/null +++ b/test/net/jaekl/qd/util/ExceptionUtilsTest.java @@ -0,0 +1,77 @@ +// Copyright (C) 2004, 2014 Christian Jaekl + +package net.jaekl.qd.util; + +import java.io.Closeable; +import java.io.IOException; + +import junit.framework.Assert; +import net.jaekl.qd.QDException; + +import org.junit.Test; + +public class ExceptionUtilsTest { + + private static class CloseableMock implements Closeable + { + private IOException m_exception; + private int m_timesClosed; + + public CloseableMock() { + m_exception = null; + m_timesClosed = 0; + } + + public int getTimesClosed() { return m_timesClosed; } + public void setExceptionToThrow(IOException ioe) { m_exception = ioe; } + + @Override + public void close() throws IOException { + m_timesClosed++; + if (null != m_exception) { + throw new IOException("This is a test"); + } + } + } + + // Try closing a closeable. + // It should be closed (once only). + @Test + public void test_tryClose_success() throws QDException + { + CloseableMock cm = new CloseableMock(); + + ExceptionUtils.tryClose(cm); + Assert.assertEquals(1, cm.getTimesClosed()); + } + + // Try closing a closeable that throws an IOException + @Test + public void test_tryClose_exception() throws QDException + { + final String MESSAGE = "This is a test"; + + CloseableMock cm = new CloseableMock(); + cm.setExceptionToThrow(new IOException(MESSAGE)); + + try { + ExceptionUtils.tryClose(cm); + } + catch (QDException qde) { + Assert.assertNotNull(qde); + Throwable t = qde.getCause(); + Assert.assertNotNull(t); + Assert.assertTrue(t instanceof IOException); + Assert.assertEquals(MESSAGE, t.getMessage()); + } + } + + // Try closing a null pointer. + // This should be harmless (no exception thrown). + @Test + public void test_tryClose_null() throws QDException + { + ExceptionUtils.tryClose(null); + } + +} diff --git a/test/net/jaekl/qd/util/ParseUtilsTest.java b/test/net/jaekl/qd/util/ParseUtilsTest.java new file mode 100644 index 0000000..0f8d33f --- /dev/null +++ b/test/net/jaekl/qd/util/ParseUtilsTest.java @@ -0,0 +1,33 @@ +package net.jaekl.qd.util; + +import junit.framework.Assert; + +import org.junit.Test; + +public class ParseUtilsTest { + + @Test + public void testParseDouble() { + String[] inputs = { null, "", "0.0", "27.34", "1234", "3.141592653589793238", "-77.18", "bogus" }; + double[] expected = { 0.0, 0.0, 0.0, 27.34, 1234, 3.141592653589793238, -77.18, 0.0 }; + double actual; + + for (int i = 0; i < inputs.length; ++i) { + actual = ParseUtils.parseDouble(inputs[i]); + Assert.assertEquals(expected[i], actual); + } + } + + @Test + public void testParseInt() { + String[] inputs = { null, "", "0", "7.1", "1234", "314159265", "-7718", "bogus" }; + int[] expected = { 0, 0, 0, 0, 1234, 314159265, -7718, 0 }; + int actual; + + for (int i = 0; i < inputs.length; ++i) { + actual = ParseUtils.parseInt(inputs[i]); + Assert.assertEquals(expected[i], actual); + } + } + +} diff --git a/test/net/jaekl/qd/util/StringUtilsTest.java b/test/net/jaekl/qd/util/StringUtilsTest.java new file mode 100644 index 0000000..b57f657 --- /dev/null +++ b/test/net/jaekl/qd/util/StringUtilsTest.java @@ -0,0 +1,38 @@ +package net.jaekl.qd.util; + +import junit.framework.Assert; + +import org.junit.Test; + +public class StringUtilsTest { + + @Test + public void test_equals() { + String [][] expectFalse = { + { null, "" }, + { "", null }, + { null, "abc" }, + { "abc", null }, + { "ABC", "abc" }, + { "123458980abc", "abc123459090" }, + { "Fred ", "Fred" }, + { "", "Fred" }, + { "Fred", "" } + }; + + String [][] expectTrue = { + { null, null }, + { "", "" }, + { "1234", "1234" }, + { "Fred", "Fred" } + }; + + for (String[] tuple : expectFalse) { + Assert.assertFalse(StringUtils.areEqual(tuple[0], tuple[1])); + } + for (String[] tuple : expectTrue) { + Assert.assertTrue(StringUtils.areEqual(tuple[0], tuple[1])); + } + } + +} diff --git a/test/net/jaekl/qd/xml/MissingInfoExceptionTest.java b/test/net/jaekl/qd/xml/MissingInfoExceptionTest.java new file mode 100644 index 0000000..32b5ad8 --- /dev/null +++ b/test/net/jaekl/qd/xml/MissingInfoExceptionTest.java @@ -0,0 +1,40 @@ +package net.jaekl.qd.xml; + +import junit.framework.Assert; + +import org.junit.Test; + +public class MissingInfoExceptionTest { + + @Test + public void test_getMessage_withSimpleTag() { + final String TAG = "TagNameGoesHere"; + MissingInfoException mie = new MissingInfoException(TAG); + String expected = "Tag: \"" + TAG + "\""; + String actual = mie.getMessage(); + Assert.assertTrue(actual.contains(expected)); + } + + @Test + public void test_getMessage_withAttributesAndChildren() { + final String AUGUSTUS = "Augustus"; + final String NOMEN = "nomen"; + final String COGNOMEN = "cognomen"; + final String TIBERIUS = "Tiberius"; + final String JULIA = "Julia"; + + MissingInfoException mie = new MissingInfoException(AUGUSTUS); + mie.addMissingAttribute(NOMEN); + mie.addMissingAttribute(COGNOMEN); + mie.addMissingChild(TIBERIUS); + mie.addMissingChild(JULIA); + + String actual = mie.getMessage(); + Assert.assertTrue(actual.contains("Tag: \"" + AUGUSTUS + "\"")); + Assert.assertTrue(actual.contains("Attribute: \"" + NOMEN + "\"")); + Assert.assertTrue(actual.contains("Attribute: \"" + COGNOMEN + "\"")); + Assert.assertTrue(actual.contains("Child tag: \"" + TIBERIUS + "\"")); + Assert.assertTrue(actual.contains("Child tag: \"" + JULIA + "\"")); + } + +} diff --git a/test/net/jaekl/qd/xml/ParseResultTest.java b/test/net/jaekl/qd/xml/ParseResultTest.java new file mode 100644 index 0000000..040308d --- /dev/null +++ b/test/net/jaekl/qd/xml/ParseResultTest.java @@ -0,0 +1,523 @@ +// Copyright (C) 2004, 2014 Christian Jaekl + +package net.jaekl.qd.xml; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; + +import junit.framework.Assert; + +import org.junit.Test; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +public class ParseResultTest { + // Some samples of XML that we're going to (try to) parse\ + private static final String MINIMAL_XML = + ""; + private static final String MINIMAL_XML_WITH_PROLOGUE = + ""; + private static final String XML_WITH_MINOR_CONTENT = + ""; + private static final String ROOT_INSIDE_SECONDARY_ELEMENT = + ""; + private static final String PROLOGUE_AND_SECONDARY_ELEMENT = + ""; + private static final String SIMPLE_INTERNAL_TAGS = + "content of two3"; + private static final String ROUTE_SUMMARY_FOR_STOP = + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " 1234\n" + + " ONE-TWO-THREE-FOUR\n" + + " \n" + + " \n" + + " \n" + + " 123\n" + + " 0\n" + + " NORTH\n" + + " First Mall\n" + + " \n" + + " \n" + + " 123\n" + + " 1\n" + + " SOUTH\n" + + " Second Mall\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + private static final String NEXT_TRIPS_FOR_STOP = + "\n" + + "" + + "" + + "" + + "2438" + + "BRONSON SUNNYSIDE" + + "" + + "41Northbound" + + "Rideau C / Ctr Rideau" + + "" + + "Rideau Centre / Centre Rideau19:00" + + "160.45" + + "4LB - IN45.408957-75.664125" + + "66.4" + + "Rideau Centre / Centre Rideau" + + "19:3040-1" + + "4LB - IN" + + "Rideau Centre / Centre Rideau20:00" + + "70-1" + + "4LB - IN" + + "" + + ""; + + // Do the least possible parsing: check for the element only. + public static class MinimalParse extends ParseResult { + private static final String[] INTERNAL = {}; + private static final Object[][] EXTERNAL = {} ; + + public MinimalParse() { + super("Root", INTERNAL, EXTERNAL); + } + + @Override + public void endContents(String uri, String localName, String qName, + String chars) throws XmlParseException + { + Assert.fail("Should not have any contents to end."); + } + + @Override + public void endExternal(String uri, String localName, String qName) + throws XmlParseException + { + Assert.fail("Should not have any external tags to end."); + } + } + + // Check that we can parse a minimal document without errors. + // Because there's no content being parsed (beyond the root element), there is + // no "correct" behaviour to assert. The test is to confirm that we + // don't do anything incorrect--no calls to endContent() nor endExternal(), + // and no exceptions thrown along the way. + @Test + public void test_withMinimalParse() throws IOException, SAXException { + MinimalParse mp = new MinimalParse(); + ByteArrayInputStream bais = null; + + String[] data = { + MINIMAL_XML, + MINIMAL_XML_WITH_PROLOGUE, + XML_WITH_MINOR_CONTENT, + ROOT_INSIDE_SECONDARY_ELEMENT + }; + + for (String datum : data) { + try { + bais = new ByteArrayInputStream(datum.getBytes("UTF-8")); + XMLReader reader = XMLReaderFactory.createXMLReader(); + ParseHandler ph = new ParseHandler(mp); + reader.setContentHandler(ph); + reader.parse(new InputSource(bais)); + } + finally { + if (null != bais) { + bais.close(); + } + } + } + } + + // If we parse something that doesn't have the expected root element, we should generate an exception + @Test + public void test_minimalParseWithMismatchedRootElement() throws IOException { + MinimalParse mp = new MinimalParse(); + ByteArrayInputStream bais = null; + + String[] data = { PROLOGUE_AND_SECONDARY_ELEMENT }; + + for (String datum : data) { + try { + bais = new ByteArrayInputStream(datum.getBytes("UTF-8")); + XMLReader reader = XMLReaderFactory.createXMLReader(); + ParseHandler ph = new ParseHandler(mp); + reader.setContentHandler(ph); + reader.parse(new InputSource(bais)); + Assert.fail("Should have thrown an exception."); + } + catch ( SAXException se ) { + Throwable cause = se.getCause(); + Assert.assertNotNull(cause); + Assert.assertTrue(cause instanceof MissingInfoException); + MissingInfoException mie = (MissingInfoException) cause; + Assert.assertEquals("Root", mie.getTagName()); + } + finally { + if (null != bais) { + bais.close(); + } + } + } + } + + // Do the some simple parsing: and some subtags that are processed internally + public static class SimpleParse extends ParseResult { + private static final String ONE = "One"; + private static final String TWO = "Two"; + private static final String THREE = "Three"; + + private static final String[] INTERNAL = {ONE, TWO, THREE}; + private static final Object[][] EXTERNAL = {} ; + + String m_one; + String m_two; + String m_three; + + public SimpleParse() { + super("Root", INTERNAL, EXTERNAL); + + m_one = m_two = m_three = null; + } + + public String getOne() { return m_one; } + public String getTwo() { return m_two; } + public String getThree() { return m_three; } + + @Override + public void endContents(String uri, String localName, String qName, + String chars) throws XmlParseException + { + if (localName.equals(ONE)) { + m_one = chars; + } + else if (localName.equals(TWO)) { + m_two = chars; + } + else if (localName.equals(THREE)) { + m_three = chars; + } + } + + @Override + public void endExternal(String uri, String localName, String qName) + throws XmlParseException + { + Assert.fail("Should not have any external tags to end."); + } + } + + // Parse some XML containing subtags that are handled internally by SimpleParse + @Test + public void test_parseWithInternalSubtags() throws IOException, SAXException + { + SimpleParse sp = new SimpleParse(); + ByteArrayInputStream bais = null; + + String[] data = { + SIMPLE_INTERNAL_TAGS + }; + + for (String datum : data) { + try { + bais = new ByteArrayInputStream(datum.getBytes("UTF-8")); + XMLReader reader = XMLReaderFactory.createXMLReader(); + ParseHandler ph = new ParseHandler(sp); + reader.setContentHandler(ph); + reader.parse(new InputSource(bais)); + + Assert.assertEquals("", sp.getOne()); + Assert.assertEquals("content of two", sp.getTwo()); + Assert.assertEquals("3", sp.getThree()); + } + finally { + if (null != bais) { + bais.close(); + } + } + } + } + + // Parse sub-tags, handling some internally and some externally + public static class RouteSummaryParse extends ParseResult { + private static final String STOP_NO = "StopNo"; + private static final String STOP_DESCR = "StopDescription"; + private static final String ERROR = "Error"; + private static final String ROUTES = "Routes"; + private static final String ROUTE = "Route"; + + private static final String[] INTERNAL = {STOP_NO, STOP_DESCR, ERROR, ROUTES}; + private static final Object[][] EXTERNAL = { {ROUTE, RouteParse.class} }; + + // Data gleaned from parsing + int m_stopNo; + String m_stopDescr; + String m_error; + ArrayList m_routes; + + public RouteSummaryParse() { + super("GetRouteSummaryForStopResult", INTERNAL, EXTERNAL); + + m_stopNo = 0; + m_stopDescr = m_error = null; + m_routes = new ArrayList(); + } + + public int getStopNo() { return m_stopNo; } + public String getStopDescription() { return m_stopDescr; } + public String getError() { return m_error; } + public int getNumRoutes() { return m_routes.size(); } + public RouteParse getRoute(int idx) { return m_routes.get(idx); } + + @Override + public void endContents(String uri, String localName, String qName, + String chars) throws XmlParseException + { + if (localName.equals(STOP_NO)) { + m_stopNo = Integer.parseInt(chars); + } + else if (localName.equals(STOP_DESCR)) { + m_stopDescr = chars; + } + else if (localName.equals(ERROR)) { + m_error = chars; + } + } + + @Override + public void endExternal(String uri, String localName, String qName) + throws XmlParseException + { + if (localName.equals(ROUTE)) { + ParseResult[] collected = collectParsedChildren(RouteParse.class); + for (ParseResult pr : collected) { + Assert.assertTrue(pr instanceof RouteParse); + m_routes.add((RouteParse)pr); + } + } + } + } + public static class RouteParse extends ParseResult { + private static final String ROUTE = "Route"; + private static final String ROUTE_NO = "RouteNo"; + private static final String DIR_ID = "DirectionID"; + private static final String DIR = "Direction"; + private static final String HEADING = "RouteHeading"; + private static final String TRIPS = "Trips"; + private static final String TRIP = "Trip"; + + private static final String[] INTERNAL = {ROUTE_NO, DIR_ID, DIR, HEADING, TRIPS}; + private static final Object[][] EXTERNAL = { {TRIP, TripParse.class} }; + + // Data gleaned from parsing + int m_routeNo; + int m_dirID; + String m_dir; + String m_heading; + ArrayList m_trips; + + public RouteParse() { + super(ROUTE, INTERNAL, EXTERNAL); + + m_routeNo = m_dirID = 0; + m_dir = m_heading = null; + m_trips = new ArrayList(); + } + + public int getRouteNo() { return m_routeNo; } + public int getDirectionID() { return m_dirID; } + public String getDirection() { return m_dir; } + public String getHeading() { return m_heading; } + public int getNumTrips() { return m_trips.size(); } + public TripParse getTrip(int idx) { return m_trips.get(idx); } + + @Override + public void endContents(String uri, String localName, String qName, + String chars) throws XmlParseException + { + if (localName.equals(ROUTE_NO)) { + m_routeNo = Integer.parseInt(chars); + } + else if (localName.equals(DIR_ID)) { + m_dirID = Integer.parseInt(chars); + } + else if (localName.equals(DIR)) { + m_dir = chars; + } + else if (localName.equals(HEADING)) { + m_heading = chars; + } + } + + @Override + public void endExternal(String uri, String localName, String qName) + throws XmlParseException + { + if (localName.equals(TRIP)) { + ParseResult[] collected = collectParsedChildren(TripParse.class); + for (ParseResult pr : collected) { + Assert.assertTrue(pr instanceof TripParse); + m_trips.add((TripParse)pr); + } + } + + } + } + public static class TripParse extends ParseResult { + private static final String TRIP = "Trip"; + private static final String TRIP_DEST = "TripDestination"; + private static final String TRIP_START = "TripStartTime"; + private static final String ADJ_SCHED_TIME = "AdjustedScheduleTime"; + + private static final String[] INTERNAL = {TRIP_DEST, TRIP_START, ADJ_SCHED_TIME }; + private static final Object[][] EXTERNAL = { }; + + // Data gleaned from parsing + String m_dest; + String m_startTime; + int m_adjSchedTime;; + + public TripParse() { + super(TRIP, INTERNAL, EXTERNAL); + + m_dest = m_startTime = null; + m_adjSchedTime = 0; + } + + public String getDestination() { return m_dest; } + public String getStartTime() { return m_startTime; } + public int getAdjustedScheduleTime() { return m_adjSchedTime; } + + @Override + public void endContents(String uri, String localName, String qName, + String chars) throws XmlParseException + { + if (localName.equals(TRIP_DEST)) { + m_dest = chars; + } + else if (localName.equals(TRIP_START)) { + m_startTime = chars; + } + else if (localName.equals(ADJ_SCHED_TIME)) { + m_adjSchedTime = Integer.parseInt(chars); + } + } + + @Override + public void endExternal(String uri, String localName, String qName) + throws XmlParseException + { + Assert.fail("Should not be attempting to parse external tags."); + } + } + + // Parse some XML containing subtags that are handled both internally and externally + @Test + public void test_parseRouteSummary() throws IOException, SAXException + { + RouteSummaryParse rsp = new RouteSummaryParse(); + ByteArrayInputStream bais = null; + + try { + RouteParse rp; + + bais = new ByteArrayInputStream(ROUTE_SUMMARY_FOR_STOP.getBytes("UTF-8")); + XMLReader reader = XMLReaderFactory.createXMLReader(); + ParseHandler ph = new ParseHandler(rsp); + reader.setContentHandler(ph); + reader.parse(new InputSource(bais)); + + Assert.assertEquals(1234, rsp.getStopNo()); + Assert.assertEquals("ONE-TWO-THREE-FOUR", rsp.getStopDescription()); + Assert.assertEquals("", rsp.getError()); + + Assert.assertEquals(2, rsp.getNumRoutes()); + + rp = rsp.getRoute(0); + Assert.assertNotNull(rp); + Assert.assertEquals(123, rp.getRouteNo()); + Assert.assertEquals(0, rp.getDirectionID()); + Assert.assertEquals("NORTH", rp.getDirection()); + Assert.assertEquals("First Mall", rp.getHeading()); + + rp = rsp.getRoute(1); + Assert.assertNotNull(rp); + Assert.assertEquals(123, rp.getRouteNo()); + Assert.assertEquals(1, rp.getDirectionID()); + Assert.assertEquals("SOUTH", rp.getDirection()); + Assert.assertEquals("Second Mall", rp.getHeading()); + } + finally { + if (null != bais) { + bais.close(); + } + } + } + + // Parse a 3-level external-tag hierarchy: RouteSummary contains Routes contains Trips + @Test + public void test_parseThreeLevels() throws IOException, SAXException + { + RouteSummaryParse rsp = new RouteSummaryParse(); + ByteArrayInputStream bais = null; + + try { + RouteParse rp; + TripParse tp; + + bais = new ByteArrayInputStream(NEXT_TRIPS_FOR_STOP.getBytes("UTF-8")); + XMLReader reader = XMLReaderFactory.createXMLReader(); + ParseHandler ph = new ParseHandler(rsp); + reader.setContentHandler(ph); + reader.parse(new InputSource(bais)); + + Assert.assertEquals(2438, rsp.getStopNo()); + Assert.assertEquals("BRONSON SUNNYSIDE", rsp.getStopDescription()); + Assert.assertEquals("", rsp.getError()); + + Assert.assertEquals(1, rsp.getNumRoutes()); + + rp = rsp.getRoute(0); + Assert.assertNotNull(rp); + Assert.assertEquals(4, rp.getRouteNo()); + Assert.assertEquals(1, rp.getDirectionID()); + Assert.assertEquals("Northbound", rp.getDirection()); + Assert.assertEquals("Rideau C / Ctr Rideau", rp.getHeading()); + + Assert.assertEquals(3, rp.getNumTrips()); + + tp = rp.getTrip(0); + Assert.assertNotNull(tp); + Assert.assertEquals("Rideau Centre / Centre Rideau", tp.getDestination()); + Assert.assertEquals("19:00", tp.getStartTime()); + Assert.assertEquals(16, tp.getAdjustedScheduleTime()); + + tp = rp.getTrip(1); + Assert.assertNotNull(tp); + Assert.assertEquals("Rideau Centre / Centre Rideau", tp.getDestination()); + Assert.assertEquals("19:30", tp.getStartTime()); + Assert.assertEquals(40, tp.getAdjustedScheduleTime()); + + tp = rp.getTrip(2); + Assert.assertNotNull(tp); + Assert.assertEquals("Rideau Centre / Centre Rideau", tp.getDestination()); + Assert.assertEquals("20:00", tp.getStartTime()); + Assert.assertEquals(70, tp.getAdjustedScheduleTime()); + } + finally { + if (null != bais) { + bais.close(); + } + } + } +}
" + stopName + " (" + stopNo + ")
RouteDestinationETARemainGPS Read1" + SOUTH_KEYS + "11:15PM10m11:05:55PM1" + SOUTH_KEYS + "11:25PM")); + Assert.assertTrue(actual.contains("20m11:05:55PM4" + HURDMAN + "17m4" + HURDMAN + "37m